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

confguru

ExAdmin
Команда форума
Вокруг новых фич в 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:
<?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:
<?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:
<?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:
<?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 (демо), и мы увидим нечто вроде этого:

PHP:
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:
<?php
htmlspecialchars("<\x80The End\xef\xbf\xbf>", ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8');
Автор
 

флоппик

promotor fidei
Команда форума
Партнер клуба
За такие вещи нужно памятники ставить, ага
 
  • Like
Реакции: KorP

pav

Новичок
Есть небольшой минус - теперь htmlspecialchars будет "ломать" скрипты в win-кодировке, коих все же написано немало. Оптимальным решением было бы брать кодировку из параметра default_charset из конфига, но всем как всегда пофиг.
 

fixxxer

К.О.
Партнер клуба
pav
там появился некий zend.script_encoding, может оно и влияет? мне, правда, пофигу, я не вижу ни одной причины в 2012 году пользоваться однобайтными кодировками, а старье - оно на 5.2 крутится и пускай крутится
 

pav

Новичок
fixxxer, zend.script_encoding на результат работы функции никак не влияет. А совместимость ломать абсолютно на пустом месте нехорошо.
 

fixxxer

К.О.
Партнер клуба
ниже перепись тех, кто пользуется однобайтными кодировками :D
 

pav

Новичок
Ну понятно, раз разработчики не удосужились сделать поддержку чего-либо, значит "нинужна". fixxxer, вы не линуксоид?
 

fixxxer

К.О.
Партнер клуба
А вы видимо виндузятник и до сих пор мучаетесь с cp866 в консоли, которая там для той самой обратной совместимости :D

Написал бы лучше патч да завел баг с ним, там делов то на 5 минут. Если уж так беспокоит.
 

pav

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

fixxxer

К.О.
Партнер клуба
О, вот это дело. Я немножко троллю на самом деле, мне тоже не нравится BC break. Но внедрение юникода нигде не проходило без проблем, всегда жопа вылезет где-то
 

pav

Новичок
>Thank you for taking the time to write to us, but this is not a bug.
Ну да, это не баг, а фича.
 
Сверху