Комментарии 149
<?== $q ?>
? Более привычно выглядит, и где-то уже видел подобное использование в шаблонах.the reason is that ~ is often located near the 'ESC' on the keyboard, so it feels more like escape
Ну и набирать удобнее, и перепутать сложнее.
<?==8 $q ?>. Простите ;)
Вариант сделать функцию <?= h($value) ?> это не выход, так как копи-паста все равно остается, и можно когда-нибудь забыть ее вызвать.А что мешает забыть поставить тильду?
Зачем засорять язык?
??
или <=>
. Много ли людей пишут свою сортировку на PHP? А данные из БД выводят практически все.P.S. Все это выглядит как из пушки по воробьям. Желание многих разработчиков решить свои проблемы путем «исправления» языка вызывает у меня лишь негодование.
если каждый будет решать свои проблемы
Это не какая-то моя локальная проблема, это операция, которая часто встречается во многих проектах. И чтобы каждый не решал таким способом свои проблемы, существует процесс RFC. Данная статья нужна для того, чтобы решить, стоит ли его начинать, узнать мнение сообщества, аргументы за и против. Потому что могут быть причины, с которыми я не сталкивался и о которых не подумал. Пока что все аргументы против сводятся к вариантам «мне не нравится странный синтаксис» и «используйте шаблонизаторы, PHP уже не шаблонизатор», либо к контекстам экранирования. По поводу шаблонизаторов и контекста я и написал в статье, и пока что нет аргументов в пользу того, что я не прав.
Но проблема с функцией немного шире. Как обеспечить глобальную доступность экранирующей функции в каждом файле представления? Делать дополнительный include_once / require_once при каждом рендеринге шаблона? Подключать один раз при старте приложения, независимо от того, будем мы рендерить HTML или нет? Автозагрузки функций в PHP еще нет, это пока только на стадии RFC.
Логичное и простое развитие языка в стиле «чтобы стоять на ногах, мне надо держаться корней» с неплохим потенциалом в плане повышения безопасности при написании простых приложений.
<?= e($anyString) ?>
Отличий от предложенного варианта нет, но сущности не плодятся.
Плодится копи-паста. И если случайно убрать символ «e», то выведутся небезопасные данные. А если убрать специальный оператор, то не выведется ничего.
Пример ошибки с XSS?
<?= $anyString ?>
Тут вместо <?~ ?> использовали <?= ?> из-за невнимательности, вызванной привычным использованием <?= ?>
Ну так сейчас только так и выводится. Небезопасный вывод не должен быть привычным использованием. В 90% случаев данные нужно экранировать для HTML, поэтому с новым оператором привычным будет его использование.
Шаблонизаторы же такой проблемы лишены, и в них нужно задумываться не для экранирования, а для отмены экранирования. Хочу заметить, что с шаблонами работают фронтендеры, и их навыки в PHP минимальны.
То есть надо каждому новому фронтендеру объяснять, что у нас есть специальная функция e(), которую надо вызывать всегда и везде, и что им надо забыть про функциию h(), которая была у них на предыдущем проекте? Кроме того, я имею в виду в первую очередь уже существующие проекты, в которых уже не используются шаблонизаторы. С тем, что шаблонизаторы это хорошо, никто и не спорит.
Или мы заведём <?~ ?> для HTML вне тегов, <?# ?> для строк в json, <?@#$*&^% ?> для LIKE выражений в SQL и т.д.
Нет. Это оператор специально для HTML-контекста. Потому что это самая частая операция, и она применяется совместно с другими контекстами. И вывод данных из PHP напрямую в JS это не настолько частая операция, как вывод данных в HTML-контекст. С LIKE-выражениями вполне справляются движки для работы с БД, они используются везде, в отличие от шаблонизаторов.
И если заменить "~" на "="
Дело не в том, что и где можно заменить. а в том, что часто повторяется одно и то же действие, а если случайно его не повторить, то это приводит к проблемам в безопасности.
Этот способ в бесконечное количество раз лучше того, который предложили вы.
Предложите пожалуйста такой же бесконечно простой способ перевести 100500 работающих шаблонов в большом внутреннем проекте компании с PHP на Twig.
И оба эти способа без автоматического экранирования плохие
<?~ ?> и есть способ с автоматическим экранированием.
Последний раз я <?= ?> встречал три года назад, работая с легаси-проектом.
Поэтому в статье я специально обратил внимание на то, что требуется мнение людей, которые реально работают с проектами без шаблонизаторов.
А давайте введём register_globals обратно, объявим неймспейсы и классы deprecated
Не вижу связи. Для введения неймспейсов и других нововведений были свои причины. Причины для появления такого оператора я изложил в статье. Он не уменьшает безопасность и не увеличивает длину названий в коде, как раз наоборот.
а если случайно его не повторить, то это приводит к проблемам в безопасности.
Именно поэтому я против <?~ ?>
Это взаимоисключающие операторы, можно написать либо <?~ ?>, либо <?= ?>, иначе не будет работать.
А с отдельной функцией это совместное использование, можно написать начало <?=, а потом либо продолжить, либо нет.
Я вообще не очень понимаю вашу логику. Введение специального оператора для экранирования в каком-либо контексте как минимум не уменьшит безопасность, потому что это не влияет на старый код и старые операторы.
Это не будет работать: если кто-то использует "=" вместо "~" по ошибке, то автоэкранирования не будет.
А если кто-то в Twig по ошибке скопипастит длинный оператор с | raw в конце, то тоже экранирования не будет.
От ошибок никто не застрахован, это не аргумент. Я упомянул про это не в качестве аргумента за или против, а чтобы показать отличие. Если кто-то использует =, то будет точно так же, как сейчас. А новый оператор позволит уменьшить количество вариантов, где можно совершить ошибку.
Элементарно. Ставится директива: все новые шаблоны и исправление старых — только на twig.
Это абсолютно никак не уменьшает сложность перевода. Как нарисовать сову? Элементарно — ставится директива: нарисовать сову.
Почему тогда просто не использовать нормальный качественный шаблонизатор?
Есть такие проекты, это факт. Для них нужно писать код, это тоже факт. Не думайте только о себе.
Речь не о том, нужны шаблонизаторы или нет. Шаблонизаторы вещь полезная, это тоже факт.
Вы предлагаете ввести средство, которое пригодится лишь в коде плохого качества.
Отсутствие шаблонизатора не означает код плохого качества.
Прекрасная идея, обоими клешнями за это нововведение. Те господа, что голосуют против, видимо слишком сильно любят свои шаблонизаторы, эгоистично полагая что это единственно правильный путь гуру, а те кто с этим не согласен — должны страдать.
Тем временем MVC в Битриксе решает эту проблему следующим образом:
Компонент формирует данные для шаблона:
array(
'TITLE' => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
'CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
);
Движок перед подключением шаблона экранирует данные:
array(
'TITLE' => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
'CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
'~TITLE' => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
'~CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
);
Теперь в шаблоне вы можете использовать:
<?=$arResult['TITLE']?>
<?=$arResult['~TITLE']?>
Конечно экранирование абсолютно всех данных для шаблона — избыточно, но вот такая идея жертвует производительностью в угоду удобству разработки кода, тем более что после кеширования потери производительности практически не будет.
По самому же вопросу — да, с одной стороны это удобно. Но, на мой взгляд, введение оператора прозвучит как призыв опять размыть границы между логикой и шаблоном, яркий пример чего — печально известный код CMS-Которую-Нельзя-Называть.
Похоже, что нет, так как 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 настройка.
В самой схеме классов заложен механизм преобразования и не очень понятно зачем снимать с него ответственность и перекладывая на шаблоны которых может быть гораздо больше чем один
>>> Перекладывая ответственность на шаблон вы порождаете кучу спагетти кода
каким образом? уж не думаете ли вы, что преобразования должны быть сделаны непосредственно в коде вьюшки?
>>> А ещё не будем забывать про рефакторинг
вот-вот. Что-то вы забыли совсем о нем, раздувая модель засчет каких-то хелперов.
>>> Если заголовок новости используется в шаблоне 3 раза (сам заголовок, alt и title для фото то зачем 3 раза вызывать эскейп, пусть даже и в короткой форме чем один раз в модели. Тем более что если сложно представить что где-то может понадобиться не экранированный заголовок.
ну как где? В апи например. В выхлопе консольной команды. В логах. Везде кроме html-вьюшки на самом деле.
>>> В самой схеме классов заложен механизм преобразования
в какой схеме классов? какой механизм? Вы же сейчас про yii? Там есть некая событийная модель, позволяющая подписываться на события до и после сохранения например. Как вы этим будете пользоваться, дело ваше. Но возможность подписаться !== заложенный механизм преобразования.
Ну и что касается контекстно-зависимого экранирования:
https://github.com/symfony/symfony/blob/303f05baafc2267b72812c44670493433b7acb0f/src/Symfony/Component/Templating/PhpEngine.php#L419
- новая синтаксическая конструкция для тривиальной задачи. Лучше вообще убрать эти
<?php
к чертям или сделать необязательными (RFC для этого вроде бы была). Вот тогда заживем и вариантов кроме как использовать адекватные шаблонизаторы не останется. - "забыть" ее применить можно с той же долей вероятности что и хелпер функцию
- целая новая синтаксическая конструкция которая обрабатывает только ОДИН частный случай экранирования
- лишний пробел — мы проиграли.
Вывод — бесполезное нововведение которым никто не будет пользоваться и которое будет приводить к бОльшим проблемам.
p.s. Аргумент мол "php и так шаблонизатор" можно рассматривать конечно, но это очень плохой шаблонизатор.
- Проекты с его использованием никуда не денутся, и для них также надо будет писать код.
- Можно написать либо одну конструкцию, либо другую. А хелпер-функцию можно написать либо нет независимо от начала конструкции. И вероятность забыть применить все-таки немного меньше, потому что эта конструкция должна применяться при любом выводе в HTML, и если у вас везде в файле встречается <?~ ?>, то надо задуматься, зачем ставить другой оператор. Также обычно просто копи-пастят, и копи-паста в данном случае будет помогать.
- Это один частный случай, но и самый частый. Он встречается даже чаще, чем конструкции для ?? и <=>.
Вообще, слово "частный" здесь не очень подходит. Если бы контексты были взаимоисключающими, тогда да. А так этот частный случай используется вместе с другими частными случаями. Причем используется он всегда, за исключением тега script, но внутри тега скрипт ничего и работать не будет, если мы выведем туда объект через htmlspecialchars. - Если будет лишний пробел, то ничего не выведется, и это будет заметно. А также для этого надо короткие теги включить.
Можете привести конкретный пример возможных проблем с кодом на PHP?
Ваши примеры с экранированием URL и JS некорректны
Ок, как по-вашему они должны быть написаны? Приведите конкретный код, пожалуйста.
Пример использования 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)
Вот, кстати, да. Тоже важный момент.
& copy ;
? Это полезное замечание конечно. Но основная цель — уменьшить повторяющиеся действия и повысить безопасность. Поэтому я и попросил привести пример, в котором будет видно именно проблему с применением.Это не универсальный оператор экранирования
сделайте свой препроцессор и используйте в свое удовольствие. Операторы только для 80% случаев не нужны в языке программирования.
Универсальное экранирование для всех случаев в принципе невозможно придумать. Делать возможность добавлять свои эскейперы технически сложно, да и не нужно. Это как раз к тому вопросу, что не стоит превращать язык в шаблонизатор. Раз-другой можно и вручную указать. Если это часто используется в каком-то отдельном проекте, то нужно действительно взять специализированный движок для шаблонов. Но на фоне общего числа проектов на 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 минут и отсутствие необходимости расширять синтаксические конструкции языка.
вам уже в рамках этой статьи приводили минимум 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-шаблонов, и там вводите свой новый синтаксис. Нечего подобным вещам делать на уровне языка
Вот если бы у меня такая необходимость встретилась в одном проекте, я бы так и сделал. Но она встречается во многих проектах. И результаты опросов показывают, что не у меня одного. Этот оператор не помешает никому, а поможет многим.
string $encoding = ini_get("default_charset")
, так что и данные в этой кодировке будут считаться валидными. Много ли таких проектов, у которых в базе одна кодировка, на странице другая, в приложении третья, и для всего этого еще и не сделана нормальная конвертация? Много ли таких «некоторых» от общего числа, кто использует четвертый параметр? Это наверное те, кто экранирует данные при сохранении в базу.Как-то даже не очень хорошо получается. «Мы используем нестандартные параметры, поэтому из-за нас не должно быть оператора для стандартных параметров. И все обязаны делать так же, как мы, несмотря на то, что стандартных случаев гораздо больше».
Нестандартные — значит встречающиеся очень редко и специфичные для задачи. Думаю, стандартными в данном случае можно считать параметры, использующиеся по умолчанию в большинстве фреймворков.
Я вот, html экранирую так:
htmlspecialchars($str, ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8');
Заведем еще один тег?
Ставим прослойку между контроллером и представлением и обрабатываем нужные данные, так как нужно (экранирование, преобразование даты и прочее)?
Тем самым в представлении уже подготовленные данные, которые можно выводить как угодно.
Прослойка мигрирует из проекта в проект.
Даже в Bitrix можно воткнуть данное решение ( костылём правда ).
Так что я лично не вижу смысла в добавлении оператора, так как экранировать можно в миллионах разных вариаций и одним оператором это не решить.
так как экранировать можно в миллионах разных вариаций
Этот оператор не является супер-универсальным оператором на все случаи жизни. Как раз потому что такой оператор сделать нельзя. В этих вариациях есть 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 объектов
— экранированные значения выражений из всего этого, причём разница между экранированием результата выражения и экранированием его операндов может оказаться критической.
Представляете такую абстракцию, готовящую данные для представления заранее, а не декорированием по месту использования?
— экранированные значения элементов массивов ( вы предлагаете мусорить в представлении? хотя по сути да, лишние хопы на двойной обход массива, но опять же мы, если массив содержит неоднородные данные, то переносится логика в представление, что не есть хорошо. Если однородные то можно пожертвовать одним лишним проходом для приведения значений к нужным данным )
— экранированные значения публичных свойств объектов ( для этого существуют геттеры и сеттеры, инкапсуляция наше всё )
— экранированные значения публичных методов объектов ( тут согласен, есть проблемы, хотя опять же вызывайте получающие данные методы ранее, и передавайте во вьюху только данные, тут вопрос где вызывать, с какими условиями и зависит конкретно от логики приложения и архитектуры)
— экранированные значения выражений из всего этого, причём разница между экранированием результата выражения и экранированием его операндов может оказаться критической. ( опять же, зачем вам операции во вьюхе, готовьте данные в сервисе, отдавайте во вьюху результаты, вы снова пытаетесь логику закинуть туда, где её не должно быть).
Представляете такую абстракцию, готовящую данные для представления заранее, а не декорированием по месту использования? (Да, фильтры, не вижу ничего страшного чтобы ввести ещё один уровень для подготовки данных)
вы снова пытаетесь логику закинуть туда, где её не должно быть
Это тема отдельного разговора. У представления есть своя внутренняя логика отображения. Не нужно путать ее с бизнес-логикой.
Вы всё равно представляете готовый данные, в чём проблема в генерации чарта, таблицы или любой другой части используя уже преобразованные данные?
Итого посчитать? Это задача бизнес логики, а никак не представления, или если у вас в 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.
Вот именно, задача представления отобразить данные как оно хочет. Ни модели, ни контроллеру незачем знать как оно хочет, их задача обеспечить представлению данные, которые оно хочет отобразить, а уж оно само решает как их отображать. — вот и обеспечьте эти данные, ещё раз говорю абстракция над представлением, а не над моделью или контроллером. Разговор, вообще шёл про библиотеку helper которая дёргается там где нужно и экранирует то что нужно.
Экранировать нужно всё по умолчанию, выводить сырые данные в каком-то контексте только при стопроцентной уверенности, что это данные уже готовы для этого контекста. Это требование безопасности. — ключевое слово выводить. Вот то что выводите и экранируйте на этапе прекомпиляции представления. А индексы, и прочее не вижу смысла экранировать если они не выводятся, если они числовые. Расскажите мне как не экранированное число в выводе может навредить? Или как если у вас на этапе валидации проверяется имя, которое состоит только из букв (в России по крайней мере), внезапно начнёт содержать html текст который вы не вводили?
Видимо, да, про разные. Не похоже, что вы говорите о разделении ответственности подобному MVC. — Да нормально разделяется ответственность. Группировки, сортировки, фильтры, делаются на уровне сервиса. А как выводить ( слева, справа, в столбик, таблицей или гридом ) решает представление. При изменении данных, представление сообщает это контроллеру, который в зависимости от того что изменилось дёргает тот или иной сервис, модель, метод. Я же вам не говорю, что вы должны представление готовить на уровне модели. Я говорю вам прямым текстом.
Модель -> Препроцессор -> Представление.
В препроцессоре дёргается библиотека которая приводит данные на вывод в нужный формат который использует представление.
а как вы ещё собираетесь это делать?
Передать все данные представлению, а оно пускай фильтрует сортирует и т. п. Если что
Чем ваш препроцессор отличается от вынесенной в отдельный файл части представления? Он, как я понимаю, знаёт всё о представлении и знает о модели то, что обычно о ней знает представление. Для контроллера он заменяет представление в части передачи ему данных от модели, для модели заменяет представление в части получения данных от модели. По сути имеет интерфейс представления для контроллера и пользуется интерфейсом модели от имени представления, оставляя представлению лишь формирование команд для контроллера.
Хотя это и bloody enterprise, но вот хороший пример контекстно-зависимого экранирования, в AEM. У них там всё магически работает из коробки, т.к. парсер анализирует HTML в который он встраивается. Но чисто как список возможных контекстов пригодится.
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 большой проблемы быть не должно, разве что через пробел надо писать, вопрос скорее в том, не будет ли конфликтов с существующим синтаксисом.
в общую корзину стоит пихать только то, что однозначно будет полезно всем и не принесет отрицательных моментов (производительность, совместимость)…
"[+]+" или же "[набор всех символов]+".Что-то мне подсказывает, что первый вариант :). Можно также сказать, что разница не велика… и в очередной раз пренебречь ресурсами планеты (а ведь клепать новые сервера не смогут бесконечно, а перерабатывать старые детишки в африке могут устать), либо выдать мол на мой век хватит… хотя таковое суждение сравнимо с саранчой.
В РНр и так добавили хренову кучу всего (переменная переменных, премиси и прочие костыли вместо нормальной области видимости как в С), что по сути является специфическими костылями, дабы угодить популярным фрейворкам — это очевидно, кто платит, тот и заказывает музыку, в итоге язык превращает в папку system32 от винды — другими словами помойную яму. можно конешно собирать свою версию, к чему я для себя и пришел, но блин, это из разряда написания своей ОСи. «magic_quotes» и «registered_globals» добавили якобы чтобы сделать «лучше», и что вышло?
PHP постига печальная судьба IP4 тоже думали, но видимо не тем местом… конечно можно сказать, что всего не учтешь, но обычно так говорят те, кому впадлу изрядно напрягать свои булки, хотя чисто физических предград для всеучтения нету…
за отдельный модуль расширенных операторов, я толкьо за, но навешивать на ядро это все не есть верный путь.
Сделать какую-нибудь requre_template(array $data). Которая будет только для шаблонов, а внутри запилить полноценный шаблонизатор.
Как именно реализовать можно уже проголосовать, взять ли готовый (имхо twig один из лучших) либо создать новый. За одно будет повод убрать укороченный синтаксис (без скобок) из php (чтобы не писали типа как в питоне), и в будущем убрать «php и так шаблонизатор».
Я в общем не против нового оператора, пригодится. Но проблем он не совсем не решает.
Заставлять никого не надо, кому нужно — будет использовать, кому не нужно — все останется как есть.
— Новый оператор тоже будет конструкцией языка.
— Оператор <?= ?> это вывод, а оператор <?~ ?> это 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-экранирования. Заодно можно и работу с контекстами добавить. И у меня есть идея, как это можно сделать.
И что с того, что токен называется так, а не иначе. Там есть например 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 User)->__construct(...);
(new Profile)->__construct(...);
RFC: https://wiki.php.net/rfc/escaping_operator
Коммит с изменениями: github
Первоначальное обсуждение: http://marc.info/?t=146619199100001
Обсуждение RFC: http://marc.info/?t=146868366400003
Оператор имеет следующую форму:
<?* $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 экранирование по умолчанию, и полный контроль и кастомизацию.
<?= $myStringValue ?>
Можно ли заменить обычный оператор на экранирующий? Может там выше по коду уже была вызвана htmlspecialchars(). Или может у нас в переменной хранится уже проверенный HTML из CKEditor. Если в проекте вообще весь код небезопасный, то можно конечно пройтись поиском с автозаменой, но можно поиском и операторы с запятой найти и поправить их.
P.S. Кстати, а у вас в проектах часто встречается вывод через запятую? Может мне просто такие проекты попадались, где одно значение либо конкатенация.
- Лучше сделать не статичным Escaper. Возможно, с методом-фабрикой статичным.
*
тяжело печатать. Как насчёт:
<?" $str, JS | HTML ?>
Я верно понял, что вся идея в том, чтобы при замене *
или "
на =
или удалении у нас вываливало ошибку?
Переменная же может иметь любое имя. Третьим параметром получается слишком сложно и многословно.
Но можно сделать так:
<?php
...
// в приложении, при создании объекта View
PHPEscaper::registerHandler([$this, 'escapeHtml'], 'html');
...
// после завершения рендернга
PHPEscaper::unregisterHandler('html');
?>
<?php
// в представлении (будет вызван PHPEscaper::escape, который вызовет callable объект [$this, 'escapeHtml'])
?>
<?* $str, 'html' ?>
2. Ну не сильно тяжелее, чем любое умножение или та же тильда)
Одиночная кавычка может сломать разные парсеры, что в общем-то и заметно.
Как вариант можно двоеточие:
<?: $str ?>
(хм, пожалуй, его удобнее набирать, чем
*
, надо подумать над этим)При замене на = ошибки не будет, это валидная конструкция, стандартный оператор же никуда не девается. Да в общем-то ошибка и необязательна, варианты оператора распознаются только если включены короткие теги, и ничего опасного при этом не происходит, просто это более строгое поведение, если новый оператор не будет вообще распознаваться в предыдущих версиях.
А хорошо бы, чтобы была. Так мы бы предотвратили тучу XSS, получившихся из за привычки использовать <?=
.
<?= $form->field(...) ?>
Замена сильно сломает обратную совместимость, надо будет много кода переписать. По одной из ссылок на предыдущие обсуждения есть похожее предложение, там человек заменил обработку байт-кода для echo в виртуальной машине. Как я понял, обратная совместимость это одна из причин, почему его не приняли.
Вообще да, было бы лучше. если бы изначально короткий оператор вывода был с HTML экранированием, а все остальное выводилось через
<?php echo ?>
. Но имеем то, что имеем.<?* $message, 'translate\category | html' ?>
Есть 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. Возможно, стоит просто добавить возможность его разрегистрировать.
Также есть небольшой вопрос, добавлять ли эти функции в глобальное пространство имен, или лучше обернуть в статический класс.
PHP (рекурсивный акроним словосочетания PHP: Hypertext Preprocessor) — это распространенный язык программирования общего назначения с открытым исходным кодом. PHP сконструирован специально для ведения Web-разработок и его код может внедряться непосредственно в HTML.
Прочитайте пожалуйста коммент выше с описанием реализации. Там есть примеры, как использовать этот оператор для любых других контекстов, не только HTML.
Про причины создания отдельного оператора для HTML, другие подобные функции, RFC для них, и сравнение их с HTML применительно к веб-разработке, написано в более ранних комментариях и в самой статье.
Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP