30 января 2012

Улучшения в htmlspecialchars() в версии 5.4

PHP
Перевод
Автор оригинала: Nikita Popov
Вокруг новых фич в PHP 5.4 было много разговоров, как например про traits, короткий синтаксис массивов.

Но одни особенно важные изменения, которые часто забывают для PHP 5.4, героически переписал cataphract (Artefacto на StackOverflow) большую часть htmlspecialchars.

Изменения о которых идет речь, относятся не только к htmlspecialchars, но еще и к htmlentities, htmlspecialchars_decode, html_entity_decode, get_html_translation_table.

Вот краткий обзор наиболее важных изменений:
  • UTF-8 кодировка по-умолчанию
  • Улучшенная обработка ошибок (ENT_SUBSTITUTE)
  • Обработка Doctype (ENT_HTML401, …)




UTF-8 кодировка по умолчанию



Как вы знаете, третий аргумент для htmlspecialchars это кодировка. Большинство людей просто упускают этот аргумент, таким образом, получая кодировку по умолчанию. Это значение было ISO-8859-1 до PHP 5.4. Новая версия исправляет это, сделав UTF-8 по умолчанию.

Улучшенная обработка ошибок


Обработка ошибок в htmlspecialchars до 5.4 была… хм, назовем ее «неинтуитивной»:

Если вы указали строку содержащую «некорректную кодовую последовательность» (для Unicode это «некоректно закодированя строка») htmlspecialchars вернет пустую строку. Ну, ладно, пока все хорошо. Забавно то, что она дополнительно выдаст ошибку, но только если отображения ошибок было отключено. Чудесно, не так ли?

В основном это означало, что на вашем dev-компьютере, вы не увидите никаких ошибок, но на production среде журнал ошибок будет заполнен вместе с ними. Удивительно.

В PHP 5.4 к счастью, такое поведение уже история. Ошибки больше не будут генерироваться.

Кроме того есть две опции, которые позволяют указать альтернативу возвращаемой пустой строке:

  • ENT_IGNORE: Этот вариант (который на самом деле не новый, он был и в PHP 5.3) просто отбросит всю некорректную последовательность кода. Это плохо по двум причинам: во-первых, вы не увидите недопустимых символов. Во-вторых, это накладывает определенный риск безопасности.
  • ENT_SUBSTITUTE: Это новая альтернативная опция. Вместо того чтобы просто удалять символы, они будут заменены на символ замены Юникод � (U + FFFD).


Давайте посмотрим на различные поведения (демо):

<?php // "\80" некоректно UTF-8 в этом контексте
var_dump(htmlspecialchars("a\x80b"));                 // string(0) ""
var_dump(htmlspecialchars("a\x80b", ENT_IGNORE));     // string(2) "ab"
var_dump(htmlspecialchars("a\x80b", ENT_SUBSTITUTE)); // string(5) "a�b"


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

<?php

// это в bootstrap, чтобы небыло сообщения об ошибке в 5.3
if (!defined('ENT_SUBSTITUTE')) {
    define('ENT_SUBSTITUTE', 0);          // если хотите пустые строки в 5.3
    // или
    define('ENT_SUBSTITUTE', ENT_IGNORE); // если хотите удаления символов в 5.3
}

// не забудьте кодировку в 5.3
$escaped = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');


Обработка Doctype



В PHP 5.4 есть четыре дополнительных флага для указания doctype который нужно использовать:

  • ENT_HTML401 (HTML 4.01) => используется по умолчанию
  • ENT_HTML5 (HTML 5)
  • ENT_XML1 (XML 1)
  • ENT_XHTML (XHTML)


В зависимости какой doctype вы укажите htmlspecialchars (и другие родственные функции) будут использовать разные таблицы сущностей.

Пример (демо):
<?php
var_dump(htmlspecialchars("'", ENT_HTML401)); // string(6) "&#039;"
var_dump(htmlspecialchars("'", ENT_HTML5));   // string(6) "&apos;"


Таким образом, для HTML 5 сущность &apos; будет возвращена, а для HTML 4.01 — потому что он не поддерживает &apos; — числовой код &#039;.
Разница становится более очевидной при использовании htmlentities, потому что там различий больше.
Вы можете легко убедиться в этом, когда посмотрите на сырые таблицы перевода.

Чтобы сделать это, можно использовать get_html_translation_table функцию. Вот пример для XML 1 doctype (демо):


<?php
var_dump(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES | ENT_XML1));


Результат выполнения:
array(5) {
["""]=>
string(6) "&quot;"
["&"]=>
string(5) "&amp;"
["'"]=>
string(6) "&apos;"
["<"]=>
string(4) "&lt;"
[">"]=>
string(4) "&gt;"
}


Это соответствует нашим ожиданиям: XML сам по себе определяет только пять основных сущностей.
А теперь попробуем то же самое для HTML 5 (демо), и мы увидим нечто вроде этого:

array(1510) {
[" "]=>
string(5) "&Tab;"
["
"]=>
string(9) "&NewLine;"
["!"]=>
string(6) "&excl;"
["""]=>
string(6) "&quot;"
["#"]=>
string(5) "&num;"
["$"]=>
string(8) "&dollar;"
["%"]=>
string(8) "&percnt;"
["&"]=>
string(5) "&amp;"
["'"]=>
string(6) "&apos;"
// ...
}


HTML 5 определяет большое количество сущностей — 1510, если быть точным. Вы также можете попробовать указать HTML 4.01 и XHTML, они оба определяют 253 сущности.

Также затронутым от выбранного типа документа является еще одний новый флаг обработки ошибок, который я не упомянул выше: ENT_DISALLOWED. Этот флаг будет заменять символы на Unicode символы замены, которые формально являются корректными последовательностями кода, но недопустимы в данном DOCTYPE.

Таким образом, вы можете гарантировать, что возвращаемая строка будет всегда хорошо сформирована в отношении кодирования (в данном типе документа). Хотя я не уверен, много ли дает использование этого флага. Браузер обрабатывает недействительные символы изящно любом случае, так что это мне кажется ненужным (хотя я наверное не прав).


Это еще не все


… но я не хочу перечислять здесь все. Думаю, что три изменения, упомянутые выше, наиболее важные из улучшений.

<?php
htmlspecialchars("<\x80The End\xef\xbf\xbf>", ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8');
Теги:php 5.4htmlspecialchars
Хабы: PHP
+66
20,5k 91
Комментарии 30
Лучшие публикации за сутки