Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в ответ несколько комментов про шаблонизаторы и задумался. Я знаю про шаблонизаторы, но есть много проектов, в которых они не используются, которые написаны на самописных движках, на CMS, либо на фреймворках, в которых нет шаблонизатора по умолчанию. Эти проекты продолжают развиваться и требуют писать код.
В этой статье я хочу изложить некоторые аргументы за то, что такой оператор будет полезен. И, возможно, получить обоснованные аргументы против.
Я предлагаю оператор
Поискав на bugs.php.net, я нашел один активный feature request с 2012 года. Там в комментариях была предложена форма записи
Я пообщался с разработчиками в рассылке PHP Internals list и получил ответ, что это уже обсуждалось много раз.
Ссылки:
marc.info/?t=145851323800001
marc.info/?t=135082660600002
marc.info/?t=144225546000001
marc.info/?t=101129596100006
wiki.php.net/rfc/escaper
wiki.php.net/rfc/taint
bugs.php.net/bug.php?id=62574
bugs.php.net/bug.php?id=20310
bugs.php.net/bug.php?id=16007
bugs.php.net/bug.php?id=3284
Основной аргумент в том, что экранирование контекстно-зависимо. У нас есть разные контексты — HTML/URL/JS/CSS — и делать какой-то оператор только для одного контекста неправильно, а для всех сразу сложно.
На самом деле это немного не так. Это не взаимоисключающие контексты, HTML может присутствовать или нет независимо от остальных. Кроме того, для JS и CSS контекста нельзя применить понятие «экранирование» (escaping), потому что это другие языки со своим синтаксисом. Правильная запись для них не сводится к добавлению слэшей и замене спец-символов.
Рассмотрим пример.
На первый взгляд может показаться, что тут везде нужно разное экранирование. Но это не так. Вызов htmlspecialchars() нужен во всех трех случаях.
Несмотря на то, что urlencode() возвращает безопасную для HTML строку, и можно не использовать htmlspecialchars(), сочетание htmlspecialchars() + urlencode() встречается в обработке различных фильтров.
Пример:
При обработке веб-страниц htmlspecialchars() не нужен только внутри тегов
Пункт 5 — отдельно в HTML-документе не встречается, только в составе других контекстов.
Пункт 6 — встречается довольно часто, это единственный практический случай, когда нам не надо применять html-экранирование. Но такая связка PHP+JS считается не очень хорошим стилем, лучше использовать data-атрибуты, особенно в мультиязычных приложениях. А для них нужен htmlspecialchars().
Пункт 7 — очень редкий случай, обычно PHP там не используется.
Пункт 8 — для этого случая безопасное экранирование принципиально невозможно придумать заранее.
Таким образом, для наиболее часто встречающихся на практике случаев экранировать HTML не надо только в одном случае, в остальных четырех экранирование необходимо. Следовательно, специальный оператор будет полезен, и многочисленные дискуссии тому подтверждение.
Оператор
UPD:
Описание реализации
Ссылки:
RFC: https://wiki.php.net/rfc/escaping_operator
Коммит с изменениями: github
Первоначальное обсуждение: http://marc.info/?t=146619199100001
Обсуждение RFC: http://marc.info/?t=146868366400003
Внимание. Если вы пишете приложения, к примеру, только на Twig и Symfony, или последние N лет делаете только API, то большая просьба не голосовать, а то получится нерепрезентативная выборка. Для вас сделан третий опрос. В первых двух интересует мнение людей, которые реально с этим работают.
Аргументированные комментарии за и против приветствуются.
В этой статье я хочу изложить некоторые аргументы за то, что такой оператор будет полезен. И, возможно, получить обоснованные аргументы против.
Я предлагаю оператор
<?~ $value ?>
как короткую запись для <?= htmlspecialchars($value, ENT_QUOTES) ?>
. Это позволит уменьшить копи-пасту вызовов и улучшить безопасность приложений без движков шаблонизации, так как в таких приложениях это частая операция. Обычно можно перевести приложение на новую версию языка программирования, но практически невозможно переписать все имеющиеся PHP-шаблоны на специальный шаблонизатор. Вариант сделать функцию <?= h($value) ?>
это не выход, так как копи-паста все равно остается, и можно когда-нибудь забыть ее вызвать.Поискав на bugs.php.net, я нашел один активный feature request с 2012 года. Там в комментариях была предложена форма записи
<?~ ?>
, которая показалась мне удобной. Написав там примерно то же самое, что и в статье про PHP7, получил ответ, что это потребует процесса RFC.Я пообщался с разработчиками в рассылке PHP Internals list и получил ответ, что это уже обсуждалось много раз.
Ссылки:
marc.info/?t=145851323800001
marc.info/?t=135082660600002
marc.info/?t=144225546000001
marc.info/?t=101129596100006
wiki.php.net/rfc/escaper
wiki.php.net/rfc/taint
bugs.php.net/bug.php?id=62574
bugs.php.net/bug.php?id=20310
bugs.php.net/bug.php?id=16007
bugs.php.net/bug.php?id=3284
Основной аргумент в том, что экранирование контекстно-зависимо. У нас есть разные контексты — HTML/URL/JS/CSS — и делать какой-то оператор только для одного контекста неправильно, а для всех сразу сложно.
На самом деле это немного не так. Это не взаимоисключающие контексты, HTML может присутствовать или нет независимо от остальных. Кроме того, для JS и CSS контекста нельзя применить понятие «экранирование» (escaping), потому что это другие языки со своим синтаксисом. Правильная запись для них не сводится к добавлению слэшей и замене спец-символов.
Рассмотрим пример.
<a href="/things/<?= $thing['name'] ?>" onclick="alert('<?= $thing['name'] ?>');">
<?= $thing['name'] ?>
</a>
На первый взгляд может показаться, что тут везде нужно разное экранирование. Но это не так. Вызов htmlspecialchars() нужен во всех трех случаях.
<?php $thing = ['name' => 'Say "Hello")']; ?>
<a
href="/things/<?= htmlspecialchars(urlencode($thing['name'])) ?>"
onclick="alert(<?= htmlspecialchars(json_encode($thing['name']), ENT_QUOTES) ?>);"
>
<?= htmlspecialchars($thing['name']) ?>
</a>
Несмотря на то, что urlencode() возвращает безопасную для HTML строку, и можно не использовать htmlspecialchars(), сочетание htmlspecialchars() + urlencode() встречается в обработке различных фильтров.
Пример:
<?php
$postData = ['contains_text' => 'Say "Hello")'];
$filterUrl = '/my_route/?state=active';
if ($postData['contains_text']) $filterUrl .= '&' . 'contains_text=' . urlencode($postData['contains_text']);
$pageNumber = 1;
?>
<a
href="<?= htmlspecialchars($filterUrl) ?>"
onclick="alert(<?= htmlspecialchars(json_encode($postData['contains_text']), ENT_QUOTES) ?>);"
>
<?= $pageNumber ?>
</a>
При обработке веб-страниц htmlspecialchars() не нужен только внутри тегов
style
и script
.1 HTML Содержимое тега, любые атрибуты тега (включает следующие 3 варианта)
2 HTML + URL Атрибуты href, action, различные варианты типа data-url
3 HTML + JS Атрибуты on-event - onclick, onkeypress и т.д.
4 HTML + CSS Атрибут style
5 URL -
6 JS Тег <script></script>
7 CSS Тег <style></style>
8 non-HTML Экранирование может зависеть не только от формата, но и от задачи.
Пункт 5 — отдельно в HTML-документе не встречается, только в составе других контекстов.
Пункт 6 — встречается довольно часто, это единственный практический случай, когда нам не надо применять html-экранирование. Но такая связка PHP+JS считается не очень хорошим стилем, лучше использовать data-атрибуты, особенно в мультиязычных приложениях. А для них нужен htmlspecialchars().
Пункт 7 — очень редкий случай, обычно PHP там не используется.
Пункт 8 — для этого случая безопасное экранирование принципиально невозможно придумать заранее.
Таким образом, для наиболее часто встречающихся на практике случаев экранировать HTML не надо только в одном случае, в остальных четырех экранирование необходимо. Следовательно, специальный оператор будет полезен, и многочисленные дискуссии тому подтверждение.
Оператор
<?~ ?>
не требует настроек в «php.ini» и не влияет на остальные теги и операторы. Его удобно набирать, все символы набираются с Shift, и меньше вероятность написать <?= ?>
вместо него, так как тильда находится с другой стороны клавиатуры. Если вы по каким-то причинам не используете htmlspecialchars()
, то для вас ничего не поменяется. Он не является универсальным оператором для экранирования любых данных, это просто короткая запись для вызова <?php echo htmlspecialchars($str, ENT_QUOTES) ?>
, так же как <?= ?>
для <?php echo $str; ?>
UPD:
Описание реализации
Ссылки:
RFC: https://wiki.php.net/rfc/escaping_operator
Коммит с изменениями: github
Первоначальное обсуждение: http://marc.info/?t=146619199100001
Обсуждение RFC: http://marc.info/?t=146868366400003
Внимание. Если вы пишете приложения, к примеру, только на Twig и Symfony, или последние N лет делаете только API, то большая просьба не голосовать, а то получится нерепрезентативная выборка. Для вас сделан третий опрос. В первых двух интересует мнение людей, которые реально с этим работают.
Аргументированные комментарии за и против приветствуются.
Only registered users can participate in poll. Log in, please.
Насколько часто вы работаете с проектами с рендерингом шаблонов на PHP, в которых не используются шаблонизаторы?
37.23% Постоянно223
23.04% Довольно часто138
17.53% Довольно редко105
22.2% Почти никогда133
599 users voted. 138 users abstained.
Only registered users can participate in poll. Log in, please.
Как вы считаете, такой оператор был бы полезен?
58.78% Да348
41.22% Нет244
592 users voted. 155 users abstained.
Only registered users can participate in poll. Log in, please.
Я не использую рендеринг шаблонов на PHP…
48.67% и я считаю, что такой оператор не нужен165
51.33% но я считаю, что такой оператор пригодится174
339 users voted. 326 users abstained.