Pull to refresh

Comments 149

А как вам вариант <?== $q ?>? Более привычно выглядит, и где-то уже видел подобное использование в шаблонах.
Я бы предложил тогда <?- $q ?> по аналогии с underscore (<%-… %> для экранирования и <%=… %> без экранирования)
В feature request человек, который его предложил, объясняет это так:
the reason is that ~ is often located near the 'ESC' on the keyboard, so it feels more like escape

Ну и набирать удобнее, и перепутать сложнее.
А что будет, когда он клаву поменяет? :)
А что, есть клавиатуры, где ~ не рядом с Esc?
Конечно, стандартная international qwerty — между левым шифтом и Z.
Вариант сделать функцию <?= h($value) ?> это не выход, так как копи-паста все равно остается, и можно когда-нибудь забыть ее вызвать.
А что мешает забыть поставить тильду?
Оператор без тильды сейчас не используется. Написать случайно <?= ?> сложнее, потому что надо символы по-другому набирать. Скопировать <?= ?> из другого места можно, но если этот оператор будет применяться всегда при выводе в HTML, то и эта проблема решится. Проблема с функцией в том, что можно вывести без функции, а без оператора вывести ничего не получится.
А далее напишите свою функцию __($val).
Зачем засорять язык?
Написание функции с коротким именем не решает проблему копи-пасты или невнимательности. Кроме того, дело не в копи-пасте как таковой, а в том, что это очень частая операция. 90% выводимых данных — это данные из БД, и только иногда нам надо вывести готовый HTML-контент. Ну и просто это логично, что в языке для веб-программирования должен быть оператор безопасного вывода данных в HTML-окружение.
XaveScor предлагает андерскор, чтобы получилось <?_($val);?> что уже очень близко к вашему варианту, только с другим знаком.
https://eval.in/596367 собственно вот вам и пример — вместо тильды — андерскор, а итог тот же что вы и хотели. Новая синтаксическая конструкция, не правда ли?
Ага, только еще скобки, и короткий оператор <? ?> надо включать.
Не превращайте язык в шаблонизатор, да, когда-то php так позиционировался, но это уже давно не так, не нужно засорять язык бесполезным хламом усложняя его синтаксис.
Аргументируйте пожалуйста. Я считаю, что это не бесполезный хлам, это нужная и часто используемая операция. Один оператор не превратит PHP в хороший шаблонизатор и не заставит исчезнуть другие шаблонизаторы, зато поможет в разработке тех проектов, где шаблонизатора нет. Кроме того, такой аргумент можно было бы рассмотреть, если бы в PHP не добавлялись новые операторы для уже существующих действий — ?? или <=>. Много ли людей пишут свою сортировку на PHP? А данные из БД выводят практически все.
Внедрение ?? и <=> тоже было сомнительным, но в меньшей степени. Как уже подсказали выше, достаточно завести короткую функцию вместо htmlspecialchars(). Выходит так, у нас есть проблема — длинная и неудобная функция htmlspecialchars(), ага, значит надо решать ее путем внедрения в язык новой синтаксической конструкции, а сделать синоним из одной буквы той же функции для нас проблема, но это же на несколько символом длиннее…

P.S. Все это выглядит как из пушки по воробьям. Желание многих разработчиков решить свои проблемы путем «исправления» языка вызывает у меня лишь негодование.
Проблема не в длине названия функции, а в том, что есть возможность написать без нее, специально или по невнимательности, что бывает довольно часто. И приходится постоянно держать в голове, что я не могу просто воспользоваться оператором <?= ?>, а надо туда еще что-то дописывать, причем дописывать всегда одно и то же.
В PHP по невнимательности можно сделать много ошибок, а еще в голове надо постоянно держать порядок аргументов для функций. А то что надо постоянно держать в голове, что надо писать <?~ вместо <?= и можно случайно ошибиться… Это не аргументы, многие разработчики могут быть со мной несогласны, но если каждый будет решать свои проблемы таким радикальным способом, в скором времени php превратится в франкенштейна с кучей возможностей, как perl например. Но зачем нам php тогда, если есть уже perl.
если каждый будет решать свои проблемы

Это не какая-то моя локальная проблема, это операция, которая часто встречается во многих проектах. И чтобы каждый не решал таким способом свои проблемы, существует процесс RFC. Данная статья нужна для того, чтобы решить, стоит ли его начинать, узнать мнение сообщества, аргументы за и против. Потому что могут быть причины, с которыми я не сталкивался и о которых не подумал. Пока что все аргументы против сводятся к вариантам «мне не нравится странный синтаксис» и «используйте шаблонизаторы, PHP уже не шаблонизатор», либо к контекстам экранирования. По поводу шаблонизаторов и контекста я и написал в статье, и пока что нет аргументов в пользу того, что я не прав.
Если такая фича вызывает бурные обсуждения, как сторонников, так и противников, это означает что фича далеко не однозначна и маловероятно будет принята сообществом.
Если есть неоднозначность, значит должны быть обоснованные аргументы. «Мне не нравится новый синтаксис» не аргумент в технической дискуссии.
Это ваше субъективное мнение, что они необоснованные. Они вполне обоснованные.
Ваше обоснование в том, что можно сделать специальную функцию. Но проблема не в том, что у нас нет функции, а в том, что часто повторяется одно и то же действие, а если случайно его не повторить, то это приводит к проблемам в безопасности.

Но проблема с функцией немного шире. Как обеспечить глобальную доступность экранирующей функции в каждом файле представления? Делать дополнительный include_once / require_once при каждом рендеринге шаблона? Подключать один раз при старте приложения, независимо от того, будем мы рендерить HTML или нет? Автозагрузки функций в PHP еще нет, это пока только на стадии RFC.

Многие считают ваши шаблонизаторы, построенные на базе языка шаблонизатора, хламом.
Фанаты битрикса наверное?
Не всё из наследия Расмуса хорошо для PHP, многое, наоборот, очень-очень плохо, но, так или иначе, он начинал писать язык как шаблонизатор. Приделывать к шаблонизатору шаюлонизатор можно, и такое повышение уровня косвенномти даже решит какие-то проблемы, но руки сами тянутся к бритве Оккама.

Логичное и простое развитие языка в стиле «чтобы стоять на ногах, мне надо держаться корней» с неплохим потенциалом в плане повышения безопасности при написании простых приложений.
UFO just landed and posted this here
Ну, в какой-то мере предложенный <?~ можно рассматривать как замену в аргументе по регулярному выражению.
А underscor'овая конструкция у меня ассоциируется с вырезанием пробелов слева от блока {%-… %} в jinja2/twig.
Постоянно пишу шаблоны на чистом php, но никогда не сталкивался с проблемой копипасты. Что мешает готовить экранированные данные со стороны бэкэнда? Если этот оператор действительно нужен, можете написать extension для PHP и выложить в паблик.
бэкэндом может выступать все что угодно, в частности чужой апи. У бэкэнда может быть много фронтэндов с разными задачами — бэкэнд должен отдавать raw-данные, фронтэнд должен эти данные представить в том виде, в котором ему нужно. Бэкэнд ничего о фронтэнде не должен знать.
Да, согласен, бэкэнд не должен экранировать данные. Просто меня ни разу не напрягало писать что то вроде Html::encode($var), потому предложил, совершенно не думая, такое решение.
UFO just landed and posted this here
UFO just landed and posted this here
<?= e($anyString) ?>
Отличий от предложенного варианта нет, но сущности не плодятся.

Плодится копи-паста. И если случайно убрать символ «e», то выведутся небезопасные данные. А если убрать специальный оператор, то не выведется ничего.

Пример ошибки с XSS?
<?= $anyString ?>
Тут вместо <?~ ?> использовали <?= ?> из-за невнимательности, вызванной привычным использованием <?= ?>

Ну так сейчас только так и выводится. Небезопасный вывод не должен быть привычным использованием. В 90% случаев данные нужно экранировать для HTML, поэтому с новым оператором привычным будет его использование.

Шаблонизаторы же такой проблемы лишены, и в них нужно задумываться не для экранирования, а для отмены экранирования. Хочу заметить, что с шаблонами работают фронтендеры, и их навыки в PHP минимальны.

То есть надо каждому новому фронтендеру объяснять, что у нас есть специальная функция e(), которую надо вызывать всегда и везде, и что им надо забыть про функциию h(), которая была у них на предыдущем проекте? Кроме того, я имею в виду в первую очередь уже существующие проекты, в которых уже не используются шаблонизаторы. С тем, что шаблонизаторы это хорошо, никто и не спорит.

Или мы заведём <?~ ?> для HTML вне тегов, <?# ?> для строк в json, <?@#$*&^% ?> для LIKE выражений в SQL и т.д.

Нет. Это оператор специально для HTML-контекста. Потому что это самая частая операция, и она применяется совместно с другими контекстами. И вывод данных из PHP напрямую в JS это не настолько частая операция, как вывод данных в HTML-контекст. С LIKE-выражениями вполне справляются движки для работы с БД, они используются везде, в отличие от шаблонизаторов.
UFO just landed and posted this here
И если заменить "~" на "="

Дело не в том, что и где можно заменить. а в том, что часто повторяется одно и то же действие, а если случайно его не повторить, то это приводит к проблемам в безопасности.

Этот способ в бесконечное количество раз лучше того, который предложили вы.

Предложите пожалуйста такой же бесконечно простой способ перевести 100500 работающих шаблонов в большом внутреннем проекте компании с PHP на Twig.

И оба эти способа без автоматического экранирования плохие

<?~ ?> и есть способ с автоматическим экранированием.

Последний раз я <?= ?> встречал три года назад, работая с легаси-проектом.

Поэтому в статье я специально обратил внимание на то, что требуется мнение людей, которые реально работают с проектами без шаблонизаторов.

А давайте введём register_globals обратно, объявим неймспейсы и классы deprecated

Не вижу связи. Для введения неймспейсов и других нововведений были свои причины. Причины для появления такого оператора я изложил в статье. Он не уменьшает безопасность и не увеличивает длину названий в коде, как раз наоборот.

UFO just landed and posted this here
а если случайно его не повторить, то это приводит к проблемам в безопасности.

Именно поэтому я против <?~ ?>

Это взаимоисключающие операторы, можно написать либо <?~ ?>, либо <?= ?>, иначе не будет работать.
А с отдельной функцией это совместное использование, можно написать начало <?=, а потом либо продолжить, либо нет.
Я вообще не очень понимаю вашу логику. Введение специального оператора для экранирования в каком-либо контексте как минимум не уменьшит безопасность, потому что это не влияет на старый код и старые операторы.

Это не будет работать: если кто-то использует "=" вместо "~" по ошибке, то автоэкранирования не будет.

А если кто-то в Twig по ошибке скопипастит длинный оператор с | raw в конце, то тоже экранирования не будет.
От ошибок никто не застрахован, это не аргумент. Я упомянул про это не в качестве аргумента за или против, а чтобы показать отличие. Если кто-то использует =, то будет точно так же, как сейчас. А новый оператор позволит уменьшить количество вариантов, где можно совершить ошибку.

Элементарно. Ставится директива: все новые шаблоны и исправление старых — только на twig.

Это абсолютно никак не уменьшает сложность перевода. Как нарисовать сову? Элементарно — ставится директива: нарисовать сову.

Почему тогда просто не использовать нормальный качественный шаблонизатор?

Есть такие проекты, это факт. Для них нужно писать код, это тоже факт. Не думайте только о себе.
Речь не о том, нужны шаблонизаторы или нет. Шаблонизаторы вещь полезная, это тоже факт.

Вы предлагаете ввести средство, которое пригодится лишь в коде плохого качества.

Отсутствие шаблонизатора не означает код плохого качества.

Прекрасная идея, обоими клешнями за это нововведение. Те господа, что голосуют против, видимо слишком сильно любят свои шаблонизаторы, эгоистично полагая что это единственно правильный путь гуру, а те кто с этим не согласен — должны страдать.


Тем временем MVC в Битриксе решает эту проблему следующим образом:


Компонент формирует данные для шаблона:


array(
    'TITLE'   => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
    'CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
);

Движок перед подключением шаблона экранирует данные:


array(
    'TITLE'    => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
    'CONTENT'  => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
    '~TITLE'   => 'Опрос. Новый тег &lt;?~ $value ?&gt; для HTML-экранирования данных в PHP',
    '~CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
);

Теперь в шаблоне вы можете использовать:


<?=$arResult['TITLE']?>
<?=$arResult['~TITLE']?>

Конечно экранирование абсолютно всех данных для шаблона — избыточно, но вот такая идея жертвует производительностью в угоду удобству разработки кода, тем более что после кеширования потери производительности практически не будет.

Такое экранирование опасно. Можно же умудриться и вывести данные в немного другом контексте, где стандартное экранирование не сработает.

Тильда — побитовое отрицание, одно неловкое движение — — и мы получаем не то, что хотим.

По самому же вопросу — да, с одной стороны это удобно. Но, на мой взгляд, введение оператора прозвучит как призыв опять размыть границы между логикой и шаблоном, яркий пример чего — печально известный код CMS-Которую-Нельзя-Называть.
между тире должно стоять <?=~$foo;?> — прошу прощения, не разобрался с допустимыми тегами.
> Я знаю про шаблонизаторы

Похоже, что нет, так как PHP суть просто шаблонизатор с «расширенными» возможностями

С одной стороны желание понятно, с другой — надо будет очень сильно постараться, чтобы втолковать всем, что <~$content ?> — всего лишь замена <?= htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>, а не универсальное экранирование под любой возможный контекст...


Так я считаю, что подумать над RFC стоит.

Да, по этому поводу была пара замечаний от разработчиков. Это то, что стоит подробно описать в документации (если она будет).
Проблема с <?~ и тп в том что их будут часто забывать использовать.

Можно сделать обратное предложение.
1. сделать настройку (или любым другим способом), чтобы все подобные записи были автоматически экранированы: <?= $abc ?> — автоматически экранировано.
2. сделать новый оператор: <?== $abc ?> который выводит не экранировано.

Проблемы:
1. Сейчас пхп комьюнити движется в сторону уменьшение настроек.
2. Плохо то, что один и тот же оператор будет значить разные вещи, в зависимости от настроек.

Решение 2:
1. не менять работу оператора <?=
2. добавить оператор <?~ или <?== (синтаксис — наименьшая проблема) который автоматически экранирует
3. Добавить настройку, которая включает режим варнингов в случае использования <?= оператора. Нужно это чтобы люди не использовали <?= где не надо.
4. Добавить оператор <?== который выводит данные без экранирования.

Проблемы:
1. вместо 1 оператора — становиться 3, и 1 настройка.

Оба варианта слишком ломают обратную совместимость. С тем, что будут забывать использовать, никакой проблемы нет, потому что нет никакого отличия от текущей ситуации. К тому же, забывать можно, если используется редко, а экранированный вывод используется часто.
Вообще тут не верный подход, например в yii проще и лучше при получении данных в модели сделать все преобразования afterFind, а потом отправлять безопасные данные на вывод в шаблон. Так получается место откуда все расходиться и не надо думать где ты написал <?= или <?~
https://habrahabr.ru/post/304162/#comment_9676184
вы поняли или не поняли? разделение ответственностей — модель ничего не должна знать о представлении, соответсвенно и не должна подготавливать данные для представления. Поэтому никаких afterFind.
Мне это не кажется логичным. По мимо экранирования вывода есть ещё уйма всего чего надо сделать с данными перед отправкой в шаблон. Начиная от преобразования даты до замены boolean. Перекладывая ответственность на шаблон вы порождаете кучу спагетти кода. А ещё не будем забывать про рефакторинг. Если заголовок новости используется в шаблоне 3 раза (сам заголовок, alt и title для фото то зачем 3 раза вызывать эскейп, пусть даже и в короткой форме чем один раз в модели. Тем более что если сложно представить что где-то может понадобиться не экранированный заголовок.

В самой схеме классов заложен механизм преобразования и не очень понятно зачем снимать с него ответственность и перекладывая на шаблоны которых может быть гораздо больше чем один
В представлении у меня дата в формате «2015, 26 марта», а в апи в формате 2015-03-26T15:41:12Z. Дублировать преобразование? Для 10 точек выхода — десятерить?

>>> Перекладывая ответственность на шаблон вы порождаете кучу спагетти кода

каким образом? уж не думаете ли вы, что преобразования должны быть сделаны непосредственно в коде вьюшки?

>>> А ещё не будем забывать про рефакторинг

вот-вот. Что-то вы забыли совсем о нем, раздувая модель засчет каких-то хелперов.

>>> Если заголовок новости используется в шаблоне 3 раза (сам заголовок, alt и title для фото то зачем 3 раза вызывать эскейп, пусть даже и в короткой форме чем один раз в модели. Тем более что если сложно представить что где-то может понадобиться не экранированный заголовок.

ну как где? В апи например. В выхлопе консольной команды. В логах. Везде кроме html-вьюшки на самом деле.

>>> В самой схеме классов заложен механизм преобразования

в какой схеме классов? какой механизм? Вы же сейчас про yii? Там есть некая событийная модель, позволяющая подписываться на события до и после сохранения например. Как вы этим будете пользоваться, дело ваше. Но возможность подписаться !== заложенный механизм преобразования.

Экранирование данных — ответственность или контроллера, или представления, скорее представления, но никак не модели.
В некоторых европейских раскладках тильды на клавиатуре в явном виде нет, и вводить ее надо с AltGr/Shift-Alt/прочими ухищрениями.
Хороший аргумент, спасибо. Надо будет подумать над этим.
Иметь что-то подобное было бы неплохо, но копать надо глубже, чем кажется на первый взгляд.

Ну и что касается контекстно-зависимого экранирования:
https://github.com/symfony/symfony/blob/303f05baafc2267b72812c44670493433b7acb0f/src/Symfony/Component/Templating/PhpEngine.php#L419
  • новая синтаксическая конструкция для тривиальной задачи. Лучше вообще убрать эти <?php к чертям или сделать необязательными (RFC для этого вроде бы была). Вот тогда заживем и вариантов кроме как использовать адекватные шаблонизаторы не останется.
  • "забыть" ее применить можно с той же долей вероятности что и хелпер функцию
  • целая новая синтаксическая конструкция которая обрабатывает только ОДИН частный случай экранирования
  • лишний пробел — мы проиграли.

Вывод — бесполезное нововведение которым никто не будет пользоваться и которое будет приводить к бОльшим проблемам.


p.s. Аргумент мол "php и так шаблонизатор" можно рассматривать конечно, но это очень плохой шаблонизатор.

  • Проекты с его использованием никуда не денутся, и для них также надо будет писать код.
  • Можно написать либо одну конструкцию, либо другую. А хелпер-функцию можно написать либо нет независимо от начала конструкции. И вероятность забыть применить все-таки немного меньше, потому что эта конструкция должна применяться при любом выводе в HTML, и если у вас везде в файле встречается <?~ ?>, то надо задуматься, зачем ставить другой оператор. Также обычно просто копи-пастят, и копи-паста в данном случае будет помогать.
  • Это один частный случай, но и самый частый. Он встречается даже чаще, чем конструкции для ?? и <=>.
    Вообще, слово "частный" здесь не очень подходит. Если бы контексты были взаимоисключающими, тогда да. А так этот частный случай используется вместе с другими частными случаями. Причем используется он всегда, за исключением тега script, но внутри тега скрипт ничего и работать не будет, если мы выведем туда объект через htmlspecialchars.
  • Если будет лишний пробел, то ничего не выведется, и это будет заметно. А также для этого надо короткие теги включить.

Можете привести конкретный пример возможных проблем с кодом на PHP?

Кстати, про лишний пробел Fesor прав: <? ~$test ?>.

HTML — это лишь один из множества возможных форматов вывода PHP-приложения. Вы предлагаете отдать предпочтение одному формату, обделив остальные. Ваши примеры с экранированием URL и JS некорректны, потому что в них htmlspecialchars используется не для экранирования строки для вставки в URL и JS, а для вставки готового проэкранированного кода в HTML.
HTML — это самый часто используемый формат вывода.

Ваши примеры с экранированием URL и JS некорректны

Ок, как по-вашему они должны быть написаны? Приведите конкретный код, пожалуйста.
Самый частый в вашей практике, а в практике другого человека не самый частый, он бы хотел, что бы этот оператор экранировал для другого формата. Да и способов экранировать тот же HTML множество, в разных случаях могут понадобиться разные.

Пример использования PHP для генерации JS:
var string = <?= json_encode( $string ) ?>;

htmlspecialchars не требуется. А вот пример вставки JS-кода в HTML:
Ссылка

Тут он требуется, потому что это вставка в HTML. Следовательно, htmlspecialchars не обязателен для экранирования строк для JS.

(прошу прощения за не подсвеченный код, у меня нет прав на это)
а в практике другого человека не самый частый, он бы хотел

Для этого и нужен процесс RFC. Кроме того, речь идет не об одном человеке, а о наиболее частом случае использования языка. Думаю, никто не будет отрицать, что PHP чаще всего применяется для веб-програмирования и обработки гипертекста.

Да и способов экранировать тот же HTML множество, в разных случаях могут понадобиться разные.

Приведите пример.

Следовательно, htmlspecialchars не обязателен для экранирования строк для JS.

Я про это и писал в статье. Это не универсальный оператор экранирования, он предназначен специально для контекста HTML. Потому что вывод в HTML делается во много раз чаще, чем передача переменых в JS, и потому что он может применяться совместно с другими контекстами, а не вместо них. Это просто замена постоянному вызову htmlspecialchars.
Приведите пример.
string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get(«default_charset») [, bool $double_encode = true ]]] ) http://php.net/manual/en/function.htmlspecialchars.php
Как минимум 3 разных стандарта HTML (4.01, xhtml, 5)

Вот, кстати, да. Тоже важный момент.

Нет, я имел в виду пример, который может привести к проблемам в безопасности и/или нарушению разметки. Эти флаги для разных стандартов HTML различаются набором дополнительных сущностей. Насколько я понимаю, для правильного формирования разметки достаточно преобразовывать 5 базовых сущностей.
Для правильного формирования разметки по соответствующему стандарту надо преобразовывать то, что нужно по этому конкретному стандарту. Только этот вариант будет правильным.
Правильным — это в смысле не пройдет валидацию валидатором, если у нас в данных вдруг есть символ ©, который должен кодироваться & copy ;? Это полезное замечание конечно. Но основная цель — уменьшить повторяющиеся действия и повысить безопасность. Поэтому я и попросил привести пример, в котором будет видно именно проблему с применением.
Это не универсальный оператор экранирования


сделайте свой препроцессор и используйте в свое удовольствие. Операторы только для 80% случаев не нужны в языке программирования.
Есть операторы, которые используются гораздо реже. Если бы их не было, я бы не стал поднимать этот вопрос. Опять же есть более короткие записи для уже существующих операторов. Вместо тернарного оператора можно использовать if, вместо <?= ?> оператор <?php echo ?>. Но ни у кого не вызывает вопросов необходимость их наличия в языке.

Универсальное экранирование для всех случаев в принципе невозможно придумать. Делать возможность добавлять свои эскейперы технически сложно, да и не нужно. Это как раз к тому вопросу, что не стоит превращать язык в шаблонизатор. Раз-другой можно и вручную указать. Если это часто используется в каком-то отдельном проекте, то нужно действительно взять специализированный движок для шаблонов. Но на фоне общего числа проектов на PHP проекты с кастомными эскейперами это очень незначительная часть.
Есть операторы, которые используются гораздо реже.


От того что вы их не используете они не теряют полезности. К примеру взять spaceship оператор. Вроде штука бесполезная, особенно если мы циферки сравниваем. А что если сегодня была циферка, а завтра станет строка?:
usort($arr, function ($a, $b) {
    return $a['foo'] <=> $b['bar']; // и там не важно что за тип данных там
});


А главное — оператор покрывает все кейсы связанные со сравнением. Ваш же хорошо если 80% случаев покрывает. И смысл тогда его использовать, всеравно рано или поздно придется отказываться от этого в пользу функций хэлперов.

А еще можно поступить вообще круто, перестать использовать php как шаблонизатор и взять какой twig.
А главное — оператор покрывает все кейсы связанные со сравнением. Ваш же хорошо если 80% случаев покрывает.

Ну это уже демагогия) А «мой оператор» покрывает 100% случаев, связанных с экранированием вывода в HTML. Если вы не используете экранирование HTML, оно не теряет полезности для тех, кто его использует. А специальный оператор — это сокращение для этой частой операции. В проектах без шаблонизаторов экранирование в местах текущего использования <?= ?> нужно даже не в 80, а примерно в 99% случаев.
Кстати, если вы не используете экранирование, то этот оператор на ваши проекты не повлияет вообще никак. А тем, кто использует, поможет повысить безопасность и уменьшить копи-пасту одних и тех же вызовов.

все равно рано или поздно придется отказываться от этого в пользу функций хэлперов.

Например в каких случаях? Я снова прошу вас привести пример кода. Если задача будет связана с выводом в HTML, то и экранирование никуда не денется, независимо от дополнительной обработки через urlencode, json_encode или something_else_encode.

А еще можно поступить вообще круто, перестать использовать php как шаблонизатор и взять какой twig.

Речь не о том, нужны шаблонизаторы или нет. Я согласен с тем, что лучше использовать их. Есть проекты без них, и во многих случаях перейти на шаблонизатор уже нет возможности.
А «мой оператор» покрывает 100% случаев, связанных с экранированием вывода в HTML


вам уже в рамках этой статьи приводили минимум 2-3 отличающихся от вашего способа экранирования (различия в опциях).
Есть проекты без них, и во многих случаях перейти на шаблонизатор уже нет возможности.


так напишите маленький препроцессор PHP-шаблонов, и там вводите свой новый синтаксис. Нечего подобным вещам делать на уровне языка. И в итоге мы имеем решение проблемы за 15 минут и отсутствие необходимости расширять синтаксические конструкции языка.
Laravel-овский blade фактически и есть такой препроцессор. Разве что не совсем маленький.
вам уже в рамках этой статьи приводили минимум 2-3 отличающихся от вашего способа экранирования (различия в опциях)

В примерах указаны частные случаи соответствия стандартам. На правильность разметки и безопасность эти флаги не влияют. Все крупные фреймворки и шаблонизаторы используют другие сочетания флагов, причем похожие.

Symfony — ENT_QUOTES | ENT_SUBSTITUTE
Yii — ENT_QUOTES | ENT_SUBSTITUTE
Zend — ENT_QUOTES | ENT_SUBSTITUTE
Twig — ENT_QUOTES | ENT_SUBSTITUTE
Smarty — ENT_QUOTES
Blade — ENT_QUOTES
Facebook XHP — [по умолчанию]

Я даже больше скажу. При любых флагах в htmlspecialchars кодируется только 5 основных сущностей (а, ну да, можно кавычки не кодировать, только смысл тогда экранировать данные). Сочетания флагов влияют только на способ кодирования апострофа, остальное различие сводится к разным способам обработки невалидных последовательностей. Много у вас в БД невалидных последовательностей?)

htmlspecialchars()
php_escape_html_entities_ex()
determine_entity_table()
таблицы

так напишите маленький препроцессор PHP-шаблонов, и там вводите свой новый синтаксис. Нечего подобным вещам делать на уровне языка

Вот если бы у меня такая необходимость встретилась в одном проекте, я бы так и сделал. Но она встречается во многих проектах. И результаты опросов показывают, что не у меня одного. Этот оператор не помешает никому, а поможет многим.
Больше половины используют ещё и чарсет (привет вин1251 на легаси), некоторые играются четвёртым параметром htmlspecialchars. А в базе, то, что ввёл пользователь :)
Не, ну положить в нормальную базу невалидный юникод — это надо специально постараться.
Чарсет по умолчанию настраивается отдельно: string $encoding = ini_get("default_charset"), так что и данные в этой кодировке будут считаться валидными. Много ли таких проектов, у которых в базе одна кодировка, на странице другая, в приложении третья, и для всего этого еще и не сделана нормальная конвертация? Много ли таких «некоторых» от общего числа, кто использует четвертый параметр? Это наверное те, кто экранирует данные при сохранении в базу.

Как-то даже не очень хорошо получается. «Мы используем нестандартные параметры, поэтому из-за нас не должно быть оператора для стандартных параметров. И все обязаны делать так же, как мы, несмотря на то, что стандартных случаев гораздо больше».
Дело не в «мы», дело в том, что если <?~ позиционируется как замена <?= htmlspecialchars, то нужно и все параметры её как-то использовать. И что значит «нестандартные»?
Она позиционируется как замена самому частому случаю. Я бы даже не стал называть это именно «замена». Сама функция никуда не денется.
Нестандартные — значит встречающиеся очень редко и специфичные для задачи. Думаю, стандартными в данном случае можно считать параметры, использующиеся по умолчанию в большинстве фреймворков.
Так вон во фреймворках разные значения, плюс чарсет, плюс у некоторых false четвертый параметр.
Во фреймворках ENT_QUOTES | ENT_SUBSTITUTE. Отдельно ENT_QUOTES это менее строгий режим, и следовательно менее безопасный. Чарсет берется из настроек приложения. Задач, когда в документе одна кодировка, а в данных другая, и это действительно надо, очень мало. Как и не кодировать HTML-сущности в функции их кодирования. Я же говорю, это не повод из-за отдельных случаев не добавлять оператор, которым будут пользоваться многие. Некоторые фиче-реквесты еще в 2002 году созданы.
Какой-то «оператор частного случая» получается.
Я вот, html экранирую так:

htmlspecialchars($str, ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8');

Заведем еще один тег?
Конкретные флаги это уже детали реализации. Думаю, нужно делать такие, которые используются в большинстве крупных фреймворков. Сейчас вопрос в том, нужно ли наличие такого специализированного оператора для HTML контекста.
У нас минимум 3 разных HTML контекста, особенно если речь о legacy коде. А шаблонизаторы зачастую позволяют устанавливать метод и флаги экранирования. То есть, как минимум, если и вводить эту инструкцию, то с возможностью гибкого конфигурирования на уровне php.ini, а то и declare.
Ещё подумал, что если конфигурировать на уровне php.ini и(или) declare, то конфигурировать можно обычный <?=, добавив новую инструкцию, которая не экранирует. По умолчанию для <?= экранирование должно быть отключено, чтобы не нарушать BC, но должна быть возможность гибко его настроить.
И получится еще один magic_quotes. Спасибо, нет.
А не проще решить всё абстракцией?))
Ставим прослойку между контроллером и представлением и обрабатываем нужные данные, так как нужно (экранирование, преобразование даты и прочее)?
Тем самым в представлении уже подготовленные данные, которые можно выводить как угодно.
Прослойка мигрирует из проекта в проект.

Даже в Bitrix можно воткнуть данное решение ( костылём правда ).

Так что я лично не вижу смысла в добавлении оператора, так как экранировать можно в миллионах разных вариаций и одним оператором это не решить.
Приведите пример, как это можно сделать. Даже с преобразованием даты будут проблемы — если для дэйтпикера нужен один формат с полным названием месяца на текущем языке пользователя, а в hidden-поле нужен другой технический формат, который можно отправить на сервер. Или другой пример — когда передается объект user, а свойство user->fullName это геттер, который конкатенирует отдельные составляющие имени.

так как экранировать можно в миллионах разных вариаций

Этот оператор не является супер-универсальным оператором на все случаи жизни. Как раз потому что такой оператор сделать нельзя. В этих вариациях есть 2 части — одна зависит от задачи, другая от внешнего контекста. Миллион вариаций — это про первую часть, а вторая у нас всегда HTML, исключения из этого правила описаны в статье. Для нее и нужен этот оператор.
Декоратор?)

Есть некий абстрактный сервис преобразования ( будем использовать немного магии ).

```php
interface IDataPreparator{

public function setData( array $data );

/**
* return array
*/
public function getData();

}
```

Массив передаём предположим так.

```php

$data = [
'dateOne' => ['value' => '10 feb 2012', 'type' => 'date', 'format' => 'RU' ],
'dateTwo' => [ 'value' => '10 feb 2012', 'type' => 'date', 'format' => 'EN' ],
'HtmlString' => ['value' => 'Hello world', 'type' => 'html', 'options' => ['encoding' => 'utf-8']]
];

```
Где ключ название переменной, а значения правила.

Да, придётся много писать, но более универсальное средство.
Да и делается это сколько угодно раз.
Чтобы вернуть всё в состояние переменных сделаем extract();

Profit, написали библиотеку для преобразований, а в представлении работаем уже с подготовленными данными.

```php

class Controller {

public function actionIndex(){
$data = Model::getData();
$preparedData = (new DataPreparator()) -> setData([ 'dateOne' => ['value' => $data['dateNewYork'], 'format' => 'EN' ] ])->getPreparedData();
$this->renderView('view', $preparedData );
}

}

```
Как минимум в шаблоне нам может понадобиться выводить:
— экранированные значения элементов массивов
— экранированные значения публичных свойств объектов
— экранированные значения публичных методов объектов
— экранированные значения метода __to_string объектов
— экранированные значения выражений из всего этого, причём разница между экранированием результата выражения и экранированием его операндов может оказаться критической.

Представляете такую абстракцию, готовящую данные для представления заранее, а не декорированием по месту использования?
— экранированные значения метода __to_string объектов ( вот это я впринципе не использую и ни разу не видел чтобы где-то использовалось, по мне, так плохой тон пытаться объект неявно приводить к строке ИМХО).

— экранированные значения элементов массивов ( вы предлагаете мусорить в представлении? хотя по сути да, лишние хопы на двойной обход массива, но опять же мы, если массив содержит неоднородные данные, то переносится логика в представление, что не есть хорошо. Если однородные то можно пожертвовать одним лишним проходом для приведения значений к нужным данным )

— экранированные значения публичных свойств объектов ( для этого существуют геттеры и сеттеры, инкапсуляция наше всё )

— экранированные значения публичных методов объектов ( тут согласен, есть проблемы, хотя опять же вызывайте получающие данные методы ранее, и передавайте во вьюху только данные, тут вопрос где вызывать, с какими условиями и зависит конкретно от логики приложения и архитектуры)

— экранированные значения выражений из всего этого, причём разница между экранированием результата выражения и экранированием его операндов может оказаться критической. ( опять же, зачем вам операции во вьюхе, готовьте данные в сервисе, отдавайте во вьюху результаты, вы снова пытаетесь логику закинуть туда, где её не должно быть).

Представляете такую абстракцию, готовящую данные для представления заранее, а не декорированием по месту использования? (Да, фильтры, не вижу ничего страшного чтобы ввести ещё один уровень для подготовки данных)

вы снова пытаетесь логику закинуть туда, где её не должно быть

Это тема отдельного разговора. У представления есть своя внутренняя логика отображения. Не нужно путать ее с бизнес-логикой.

Скрытый текст
Пример — одни и те же данные (из сервиса их получения) можно представить и в виде таблицы, и в виде чарта, а в таблице сделать строку «Итого» вверху, внизу, либо и там и там, а так же еще и в тексте перед таблицей изложить, а для разных чартов сгруппировать данные по-разному. Предлагаете все эти знания, касающиеся отображения, поместить в контроллер или бизнес-логику?
Да отображайте как хотите.
Вы всё равно представляете готовый данные, в чём проблема в генерации чарта, таблицы или любой другой части используя уже преобразованные данные?

Итого посчитать? Это задача бизнес логики, а никак не представления, или если у вас в 3-ёх представлениях «ИТОГО» нужно, а в 4-ом нет, вы будете считать отдельно в каждом из трёх?

Группировка по сумме, опять же инкапсулирование в отдельный виджет, куда вы передаёте данные, а там уже строите как хотите, вы же чётко понимаете что в представлении вам нужно экранировать, а что будет использоваться для расчётов?
На вывод пользователю виджете можно так же внедрить этот слой абстракции и всё отображение будет преобразовано так как нужно.

Группируйте сколько угодно, опять же какая разница группировать подготовленные данные или неподготовленные к выводу?
А как это делать, уже вопрос задачи.

Теперь по факту:
Вы хотите узнать нужен ли оператор, я вам говорю, что по моему мнению не нужен, так как 99.9% проблем с обработкой вывода я могу решить так или иначе без его использования (и намного гибче), на каком либо уровне абстракции, там где мне это нужно.Лишняя фича просто.

Вплоть до того что объект можно декорировать как угодно, для JSON, для XML, для HTML, да хоть для слепых в аудио виде выводить.
Итого посчитать? Это задача бизнес логики, а никак не представления

Обычно это как раз задача представления. В бизнес-логике «итого» часто не фигурирует никак, это лишь пожелание пользователей к видимому им результату. Ещё могут быть, например, пожелания показывать проценты от итого, проценты от максимума и(или) минимума и т. п. для каждого элемента, причём форматировать их по локали пользователя — это тоже всё в модель тащить?
вот это я впринципе не использую и ни разу не видел чтобы где-то использовалось, по мне, так плохой тон пытаться объект неявно приводить к строке ИМХО

Вообще много где используется.
вы предлагаете мусорить в представлении?

Передать в представление массив из элементов списка — это мусорить?
для этого существуют геттеры и сеттеры, инкапсуляция наше всё

1. Геттеры и сеттеры часто являются оверинжеренингом
2. Не путайте инкапсуляцию с сокрытием. То, что мы перенесли какую-то переменную, связанную с объектом, в его свойство уже является инкапсуляцией.
вызывайте получающие данные методы ранее, и передавайте во вьюху только данные

Как минимум нарушение принципов MVC — представление должно получать данные от модели, вы же предлагаете реализовывать какой-то MVVM.
опять же, зачем вам операции во вьюхе, готовьте данные в сервисе, отдавайте во вьюху результаты

Нарушение границ ответственности, только вьюха должна решать в какой форме выдавать данные. Банальное прибавление 1 к индексу массива, чтобы получить номер элемента — это чисто функция представления. А если мы заэкранируем индекс, а потом прибавим заэкраннированную 1 то получим, мягко говоря, не совсем то, что ожидаем.
Передать в представление массив из элементов списка — это мусорить? — нет, а вот проходить по массиву и делать с ним манипуляции не касающиеся вывода. ( складывать, вычитать, умножать, проверять на вхождения и прочее. )

1. Геттеры и сеттеры часто являются оверинжеренингом
2. Не путайте инкапсуляцию с сокрытием. То, что мы перенесли какую-то переменную, связанную с объектом, в его свойство уже является инкапсуляцией. — Мы ищем решение проблемы или что? Вам нужны данные которые вы ожидаете. Используйте get и set чтобы отдавать и получать те данные которые вы ожидаете. Не просто так же придумано.

Нарушение границ ответственности, только вьюха должна решать в какой форме выдавать данные. Банальное прибавление 1 к индексу массива, чтобы получить номер элемента — это чисто функция представления.
— Мы с вами об одном и том же говорим? Отображайте данные как хотите. Данная абстракция относится скорее в препроцесу представления, чем к логике модели, вы заранее приводите данные для отдачи клиенту (HTML, URL и прочее к виду который нужен htmlspecialchars и т.д. а далее навешивайте всё как хотите).

А если мы заэкранируем индекс, а потом прибавим заэкраннированную 1 то получим, мягко говоря, не совсем то, что ожидаем. — А зачем вам экранировать int float double вы не знаете что где используете в вашем приложении? Экранируйте на этапе передачи то что нужно, зачем экранировать и преобразовывать всё?

Мы с вами видимо про разные вещи разговариваем.

вот проходить по массиву и делать с ним манипуляции не касающиеся вывода. ( складывать, вычитать, умножать, проверять на вхождения и прочее. )

Если пользовательский интерфейс содержит настройки типа фильтров, сортировки, галочки «показывать итоги» и т. п., то всё это тащить в модель или контроллер и на каждый чих их дёргать?
Мы с вами об одном и том же говорим? Отображайте данные как хотите.

Вот именно, задача представления отобразить данные как оно хочет. Ни модели, ни контроллеру незачем знать как оно хочет, их задача обеспечить представлению данные, которые оно хочет отобразить, а уж оно само решает как их отображать.
А зачем вам экранировать int float double вы не знаете что где используете в вашем приложении? Экранируйте на этапе передачи то что нужно, зачем экранировать и преобразовывать всё?

Экранировать нужно всё по умолчанию, выводить сырые данные в каком-то контексте только при стопроцентной уверенности, что это данные уже готовы для этого контекста. Это требование безопасности.

Видимо, да, про разные. Не похоже, что вы говорите о разделении ответственности подобному MVC.
Если пользовательский интерфейс содержит настройки типа фильтров, сортировки, галочки «показывать итоги» и т. п., то всё это тащить в модель или контроллер и на каждый чих их дёргать? — а как вы ещё собираетесь это делать? Вы в любом случае будете дёргать бэкенд, или же как раз переходим к модели MVVM где вьюха решает что где как и в каких количествах (если это десктоп предположим). в вебе передавать все результаты, страннова-то, особенно если данных много.

Вот именно, задача представления отобразить данные как оно хочет. Ни модели, ни контроллеру незачем знать как оно хочет, их задача обеспечить представлению данные, которые оно хочет отобразить, а уж оно само решает как их отображать. — вот и обеспечьте эти данные, ещё раз говорю абстракция над представлением, а не над моделью или контроллером. Разговор, вообще шёл про библиотеку helper которая дёргается там где нужно и экранирует то что нужно.

Экранировать нужно всё по умолчанию, выводить сырые данные в каком-то контексте только при стопроцентной уверенности, что это данные уже готовы для этого контекста. Это требование безопасности. — ключевое слово выводить. Вот то что выводите и экранируйте на этапе прекомпиляции представления. А индексы, и прочее не вижу смысла экранировать если они не выводятся, если они числовые. Расскажите мне как не экранированное число в выводе может навредить? Или как если у вас на этапе валидации проверяется имя, которое состоит только из букв (в России по крайней мере), внезапно начнёт содержать html текст который вы не вводили?

Видимо, да, про разные. Не похоже, что вы говорите о разделении ответственности подобному MVC. — Да нормально разделяется ответственность. Группировки, сортировки, фильтры, делаются на уровне сервиса. А как выводить ( слева, справа, в столбик, таблицей или гридом ) решает представление. При изменении данных, представление сообщает это контроллеру, который в зависимости от того что изменилось дёргает тот или иной сервис, модель, метод. Я же вам не говорю, что вы должны представление готовить на уровне модели. Я говорю вам прямым текстом.

Модель -> Препроцессор -> Представление.
В препроцессоре дёргается библиотека которая приводит данные на вывод в нужный формат который использует представление.

а как вы ещё собираетесь это делать?

Передать все данные представлению, а оно пускай фильтрует сортирует и т. п. Если что

Чем ваш препроцессор отличается от вынесенной в отдельный файл части представления? Он, как я понимаю, знаёт всё о представлении и знает о модели то, что обычно о ней знает представление. Для контроллера он заменяет представление в части передачи ему данных от модели, для модели заменяет представление в части получения данных от модели. По сути имеет интерфейс представления для контроллера и пользуется интерфейсом модели от имени представления, оставляя представлению лишь формирование команд для контроллера.

Хотя это и bloody enterprise, но вот хороший пример контекстно-зависимого экранирования, в AEM. У них там всё магически работает из коробки, т.к. парсер анализирует HTML в который он встраивается. Но чисто как список возможных контекстов пригодится.

Вот то, что получается в результате действия styleToken и scriptToken
style="color: ${properties.color @ context='styleToken'};"
onclick="${properties.function @ context='scriptToken'}();"

… должно быть еще и HTML-encoded. Это написано у них в таблице
text | Default for content inside elements | Encodes all HTML special characters.
attribute | Default for attribute values | Encodes all HTML special characters.

… и об этом я и писал в статье.

Многие элементы из этой таблицы не используются совместно с PHP. Например styleComment — часто вы встречаете генерацию комментов в теге style через PHP?
Идея интересная, но как уже указали выше, зависит от контекста и тильда — не самый лучший вариант, а что если пойти с другой стороны, основная проблема — неудобство вставки, <?=h($hello)?> занимает 8 символов, длинно, неудобно и некрасиво. Как насчет сделать вызов функций и методов без скобок, <?h $hello?>
Ну то что он зависит от контекста, я сам написал в статье. Тильда это уже детали реализации. С экранирующими функциями есть некоторая проблема с автозагрузкой, я писал выше. И проблема не в длине, я про это тоже писал.
<?h $hello?>

Логической разницы с <?=h($hello)?> никакой, а анализ кода усложнится. КРоме того, как вывести значение функций php(), xml() (и так с <?xml проблемы имеются у IDE)

Логически никакой, но красивый синтаксис упростит поиск/замену, да и просто не будет вызывать боль при постоянной печати лишних скобок.


С php и xml большой проблемы быть не должно, разве что через пробел надо писать, вопрос скорее в том, не будет ли конфликтов с существующим синтаксисом.

В существующем синтаксисе три варианта после <?: <?php <? <?=
знаете, раньше все отлично делалось при помощи отвертки… а потом появились шуруповерты, только вот вкрутить шурупчик ровненько и аккуратненько при его помощи очень сложно, да и зачем и так сойдет… глядя на качество современного мира IT автоматизация только приводит в активизации лени и пофигизма человека, а вот написать самому персональный специализированый шаблонизатор или же постпроцессор для вывода или даже целый модуль под РНР который будет занимать постпроцесингом, почему нет? ЗАЧЕМ ЗАСОРЯТЬ ЯЗЫК, нет, если вы предлогаете это все динамическим модулей сделать, чтобы например все желающие могли нахрен это отключить — говно вопрос, ну тогда вы можете сами написать отдельный модуль.
в общую корзину стоит пихать только то, что однозначно будет полезно всем и не принесет отрицательных моментов (производительность, совместимость)…
Этот оператор не влияет на производительность и совместимость. Во всяком случае, не более, чем остальные новые операторы.
не скажу, что профи в написании компилятора и линковщика, но поправте, если мои суждения не верны, когда компилятор сканирует текст программы он выполняет схожую с RexExp операцией, дык вот какое правило RegEx отработает быстрее
"[+]+" или же "[набор всех символов]+".Что-то мне подсказывает, что первый вариант :). Можно также сказать, что разница не велика… и в очередной раз пренебречь ресурсами планеты (а ведь клепать новые сервера не смогут бесконечно, а перерабатывать старые детишки в африке могут устать), либо выдать мол на мой век хватит… хотя таковое суждение сравнимо с саранчой.
В РНр и так добавили хренову кучу всего (переменная переменных, премиси и прочие костыли вместо нормальной области видимости как в С), что по сути является специфическими костылями, дабы угодить популярным фрейворкам — это очевидно, кто платит, тот и заказывает музыку, в итоге язык превращает в папку system32 от винды — другими словами помойную яму. можно конешно собирать свою версию, к чему я для себя и пришел, но блин, это из разряда написания своей ОСи. «magic_quotes» и «registered_globals» добавили якобы чтобы сделать «лучше», и что вышло?

PHP постига печальная судьба IP4 тоже думали, но видимо не тем местом… конечно можно сказать, что всего не учтешь, но обычно так говорят те, кому впадлу изрядно напрягать свои булки, хотя чисто физических предград для всеучтения нету…

за отдельный модуль расширенных операторов, я толкьо за, но навешивать на ядро это все не есть верный путь.
А может вместо того чтобы плодить полумеры, в виде дополнительных операторов, взять и добавить в php шаблонизатор.
Сделать какую-нибудь requre_template(array $data). Которая будет только для шаблонов, а внутри запилить полноценный шаблонизатор.
Как именно реализовать можно уже проголосовать, взять ли готовый (имхо twig один из лучших) либо создать новый. За одно будет повод убрать укороченный синтаксис (без скобок) из php (чтобы не писали типа как в питоне), и в будущем убрать «php и так шаблонизатор».
Вот уж чего в PHP точно не нужно, так это полноценный шаблонизатор. Его можно просто взять и подключить. Я предлагаю отдельный оператор только потому, что это самая частая операция при выводе данных без шаблонизатора, а ее неиспользование приводит к проблемам в безопасности.
А что заставит использовать ваш оператор? Если выпилить из php плохой шаблонизатор, в угоду полноценного, то выбора не останется. А в полноценном шабонизаторе можно включить экранирование по умолчанию.
Я в общем не против нового оператора, пригодится. Но проблем он не совсем не решает.
Он решает проблему экранирования и проблему случайного его отсутствия, этого достаточно. Данные выведутся экранированными, либо не выведутся совсем. С остальными видами экранирования такой проблемы нет — если вывести массив или объект в JS без json_encode, то будет просто ошибка в скрипте.
Заставлять никого не надо, кому нужно — будет использовать, кому не нужно — все останется как есть.
Открывающие теги указывают на язык, а то что вы предлагаете делать с помощью нового тега заменяет функцию. То есть эти вещи в принципе не совместимы и не должны таким образом применяться.
Тег <?= ?> не только указывает на язык, но и выполняет действие.
Действие какое? Вывод (echo), а echo это не функция, а конструкция языка. htmlspecialchars – ничто иное как функция, то есть совсем другое.
Это спор о терминологии, а не аргументы за или против.
— Новый оператор тоже будет конструкцией языка.
— Оператор <?= ?> это вывод, а оператор <?~ ?> это HTML-экранированный вывод.
— Вызов функции это тоже конструкция языка. Причем в прямом смысле слова: ZEND_AST_ECHO, ZEND_AST_CALL

Попробовал реализовать. Пара мыслей вдогонку.

— В языке не место таким операторам.
Таким операторам не место в C++, или в C#, или в Java. А в самом популярном языке для веб-программирования ему самое место. Даже в исходниках PHP текст вне тегов PHP обозначается T_INLINE_HTML, а не просто T_EXTERNAL_CONTENT.
zend_language_scanner.l

Если бы в PHP не было оператора переключения контекста <?php ?>, то экранирующий оператор тоже был бы не нужен. Но именно это сделало PHP таким, какой он есть. Надо или убрать операторы переключения контекста совсем, или улучшить их использование.

— Зачем в языке нужен оператор для вызова конкретной функции?
Это не оператор для отдельной функции. Это и оператор и функция для часто встречающегося случая.
Кстати, вы не забыли, что в PHP уже есть специальный оператор для вызова конкретной функции?) Обратные кавычки и shell_exec().
zend_language_parser.y

— Зачем делать оператор, чтобы сэкономить пару нажатий на клавиши.
Проблема не в том, сколько клавиш нажимать, а в том, что очень легко сделать неправильно. Варианты <?= h($something) ?> и <?= $something ?> оба работают хорошо, небезопасный вариант работает точно так же, как и безопасный, до тех пор, пока мы не получим небезопасные данные.
Нельзя отрицать, что проблема существует. Это видно по количеству обсуждений этой темы.

— Если делать такой оператор, то нужно сделать работу с разными контекстами
Мне кажется, такой проблемы нет. Потому что — а кто об этом просит?) Нет ни одного фиче-реквеста про специальный оператор для json_encode(), зато есть куча реквестов для htmlspecialchars(). Они тянутся чуть ли не с самого появления PHP.

С другой стороны, все равно нужна возможность задавать разные флаги для html-экранирования. Заодно можно и работу с контекстами добавить. И у меня есть идея, как это можно сделать.
> Даже в исходниках PHP текст вне тегов PHP обозначается T_INLINE_HTML, а не просто T_EXTERNAL_CONTENT.
И что с того, что токен называется так, а не иначе. Там есть например T_PAAMAYIM_NEKUDOTAYIM, он тоже несет свой загадочный смысл для какого-нибудь RFC?

> Если бы в PHP не было оператора переключения контекста <?php ?>, то экранирующий оператор тоже был бы не нужен. Но именно это сделало PHP таким, какой он есть. Надо или убрать операторы переключения контекста совсем, или улучшить их использование.
Мое мнение что его нужно убрать совсем. Он появился исторически т.к. PHP встраивался в HTML. Но время прошло, и сейчас он используется только для шаблонизации. Альтернативные названия уже выпилили и то хорошо https://wiki.php.net/rfc/remove_alternative_php_tags

>Кстати, вы не забыли, что в PHP уже есть специальный оператор для вызова конкретной функции
Это опять же исторически сложилось, и было позаимствовано с Perl.

> Проблема не в том, сколько клавиш нажимать, а в том, что очень легко сделать неправильно. Варианты <?= h($something) ?> и <?= $something ?> оба работают хорошо
Настолько хорошо, что непонятно зачем еще один вариант. Но чисто статистически конечно 1/2 меньше чем 2/3.
И что с того, что токен называется так, а не иначе.

То, что любой внешний контент считается HTML. Значит должен быть оператор корректного вывода в этот HTML, так как это очень частая задача. А T_PAAMAYIM_NEKUDOTAYIM сам по себе оператор, и он тут ни при чем. Ну и да, если у кого-то будут задачи, связанные с ним, он тоже может предложить RFC.

Мое мнение что его нужно убрать совсем

Текущий код никуда не денется, его надо поддерживать, значит и операторы переключения контекста никуда не денутся.

Настолько хорошо, что непонятно зачем еще один вариант

Ну вообще-то это написано далее в предложении, в статье, и в других комментах к ней. Новый оператор либо выведет экранированное значение, либо не выведет никакое. И со стандартным оператором они взаимоисключающие, можно написать либо тот, либо другой, а с функцией опасный вариант это короткое подмножество безопасного.
Нашел хорошую аналогию. Вызывать экранирующую функцию вручную в каждом месте вывода значения — это то же самое, как вызывать конструктор вручную после каждого оператора 'new':
(new User)->__construct(...);
(new Profile)->__construct(...);
Основная идея

Оператор имеет следующую форму:
<?* $str ?>
<?* $str, 'html' ?>
<?* $str, 'js | html' ?>

Оба выражения могут быть любого типа, который может быть конвертирован в строку. Второе выражение опционально.
Я изменил знак '~' по причине отсутствия в некоторых раскладках клавиатуры, и потому что он является унарным оператором и распознается в предыдущих версиях PHP с включенными короткими тегами как битовая операция.

Оператор компилируется в следующее AST:

echo PHPEscaper::escape(first_argument, second_argument);


Это сделано аналогично обратным кавычкам и shell_exec().

Есть дефолтная реализация класса PHPEscaper. Он имеет 4 статических метода

PHPEscaper::escape($string, $context = 'html');
PHPEscaper::registerHandler($context, $escaper_function);
PHPEscaper::unregisterHandler($context);
PHPEscaper::getHandlers();


Метод PHPEscaper::escape($string, $context) разбивает строку по символу '|', для всех частей делает trim(), и потом вызывает зарегистрированный обработчик для каждого контекста в цепочке. Контекст по умолчанию 'html', он имеет специальную обработку. Если для него нет обработчика, вызывается htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE)

Можно использовать это так:
<?php
    // где-нибудь в приложении, устанавливается 1 раз
    PHPEscaper::registerHandler('html', function($str) {
        return htmlspecialchars($str, ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED | ENT_SUBSTITUTE);
    });
    PHPEscaper::registerHandler('js', [MyEscaper, 'escapeJs']);
?>
<?* $str, 'js | html' ?>


И даже больше.
В AST название PHPEscaper регистрируется как not fully qualified name (ZEND_NAME_NOT_FQ).
Это позволяет использовать пространства имен и автолоадинг.
<?php use MyEscaper as PHPEscaper; ?>
<?* $str, 'js | html' ?>

Будет вызвано MyEscaper::escape($str, 'js | html').

Таким образом, мы можем иметь автолоадинг, разные контексты, HTML экранирование по умолчанию, и полный контроль и кастомизацию.
Реализация страдает неоднозначностью. <?= 'foo', 'bar' ?> будет foobar, а <?* 'foo', 'bar' ?> Exception? Вариант пройтись автозаменой отпадает.
А причем здесь автозамена? В рабочем коде там стоит htmlspecialchars(), либо приведение к int, либо то же самое сделано выше по коду. Это другой оператор со своим синтаксисом, он не меняет поведение существующего, нет никакой неоднозначности. И я, пожалуй, ни разу не встречал вывод в этом операторе через запятую. А exception да, он при такой замене будет очень полезен.
Чтобы изменить небезопасный код на безопасный во всём проекте.
Мм. Ну вот у вас есть код:
<?= $myStringValue ?>

Можно ли заменить обычный оператор на экранирующий? Может там выше по коду уже была вызвана htmlspecialchars(). Или может у нас в переменной хранится уже проверенный HTML из CKEditor. Если в проекте вообще весь код небезопасный, то можно конечно пройтись поиском с автозаменой, но можно поиском и операторы с запятой найти и поправить их.

P.S. Кстати, а у вас в проектах часто встречается вывод через запятую? Может мне просто такие проекты попадались, где одно значение либо конкатенация.
В одном из проектов на поддержке в каждом шаблоне работает функция, экранирующая все переменные. Можно автозаменой убрать её и заменить <?= $myStringValue ?> на <?* $myStringValue ?>

Лично у меня нет. Если и встречается через запятую, то почему-то это <?php echo $var1, $var2 ?>
  1. Лучше сделать не статичным Escaper. Возможно, с методом-фабрикой статичным.
  2. * тяжело печатать. Как насчёт:

<?" $str, JS | HTML ?>

Я верно понял, что вся идея в том, чтобы при замене * или " на = или удалении у нас вываливало ошибку?

1. Я думал о случае наподобие $this->escape($str, 'html'), но не придумал, как нормально добавить переменную с объектом.
Переменная же может иметь любое имя. Третьим параметром получается слишком сложно и многословно.
Но можно сделать так:

<?php
    ...

    // в приложении, при создании объекта View
    PHPEscaper::registerHandler([$this, 'escapeHtml'], 'html');

    ...

    // после завершения рендернга
    PHPEscaper::unregisterHandler('html');
?>

<?php
    // в представлении (будет вызван PHPEscaper::escape, который вызовет callable объект [$this, 'escapeHtml'])
?>
<?* $str, 'html' ?>


2. Ну не сильно тяжелее, чем любое умножение или та же тильда)
Одиночная кавычка может сломать разные парсеры, что в общем-то и заметно.
Как вариант можно двоеточие:
<?: $str ?>

(хм, пожалуй, его удобнее набирать, чем *, надо подумать над этим)

При замене на = ошибки не будет, это валидная конструкция, стандартный оператор же никуда не девается. Да в общем-то ошибка и необязательна, варианты оператора распознаются только если включены короткие теги, и ничего опасного при этом не происходит, просто это более строгое поведение, если новый оператор не будет вообще распознаваться в предыдущих версиях.

А хорошо бы, чтобы была. Так мы бы предотвратили тучу XSS, получившихся из за привычки использовать <?=.

А если им выводились данные с HTML? <?= $form->field(...) ?>

Замена сильно сломает обратную совместимость, надо будет много кода переписать. По одной из ссылок на предыдущие обсуждения есть похожее предложение, там человек заменил обработку байт-кода для echo в виртуальной машине. Как я понял, обратная совместимость это одна из причин, почему его не приняли.

Вообще да, было бы лучше. если бы изначально короткий оператор вывода был с HTML экранированием, а все остальное выводилось через <?php echo ?>. Но имеем то, что имеем.

Обратная совместимость не ломается. Всё, что было с <?= работает как работало.

Еще подумал, что такой оператор можно использовать не только для экранирования, а для любых контекстно-зависимых трансформаций текста — переводы, форматирование чисел и дат, и т.д. Особенно если сделать возможность передавать второй аргумент массивом.
<?* $message, 'translate\category | html' ?>
Предлагаемая реализация немного изменилась. Больше нет никаких трюков с пространствами имен или магических констант. Причиной для задания PHPEscaper как ZEND_NAME_NOT_FQ была возможность использования в сторонних модулях своего обработчика вне зависимости от обработчика приложения. Но, возможно, это добавит больше проблем, чем решит.

Есть 3 функции.

    set_escape_handler($callable)
    restore_escape_handler()
    escape_handler_call($string, $context);


Они работают аналогично set_error_handler() / restore_error_handler(). Пользовательский обработчик назначается не на конкретный контекст, а на вызов оператора в целом.

Оператор (или тег) <?* $str, $context ?> компилируется в следующее AST:
    echo escape_handler_call($str, $context);


Второй аргумент также необязательный. Функция escape_handler_call() просто передает в текущий обработчик оба аргумента как есть. Контекст по умолчанию задается в пользовательском обработчике.

    set_escape_handler(function($string, $context = 'html') {
        ...
    });
    
    // или
    
    set_escape_handler([$this, 'escape']);


Наличие обработчика по умолчанию пока под вопросом, так как есть возможность 'встроенной' неправильной работы оператора с одним аргументом в не-HTML шаблонах — CSV, plain text. Возможно, стоит просто добавить возможность его разрегистрировать.

Также есть небольшой вопрос, добавлять ли эти функции в глобальное пространство имен, или лучше обернуть в статический класс.
UFO just landed and posted this here
Как этот оператор помешает консольным приложениям? В синтаксисе свалка не начнется, для того и есть процесс RFC.
UFO just landed and posted this here
http://php.net/manual/ru/intro-whatis.php
PHP (рекурсивный акроним словосочетания PHP: Hypertext Preprocessor) — это распространенный язык программирования общего назначения с открытым исходным кодом. PHP сконструирован специально для ведения Web-разработок и его код может внедряться непосредственно в HTML.


Прочитайте пожалуйста коммент выше с описанием реализации. Там есть примеры, как использовать этот оператор для любых других контекстов, не только HTML.

Про причины создания отдельного оператора для HTML, другие подобные функции, RFC для них, и сравнение их с HTML применительно к веб-разработке, написано в более ранних комментариях и в самой статье.
UFO just landed and posted this here
Sign up to leave a comment.

Articles

Change theme settings