Последнее обновление:
Как преобразовать символы текста в html-сущности в РНР?
Иногда необходимо, чтобы содержимое html-файла был представлено не символами (q, r, 6 ,1, а, т, ю, *, ., ?
, ну, и т.д.), а так называемыми html-сущностями: < , & , ×
и т.д.). Кроме того, и обычные символы (буквы, цифры) также можно представить в виде html-сущностей.
Для чего это может понадобиться?
Для представления содержимого файла html исключительно символами, входящими во множество символов ASCII. Например, если на сайте используется кодировка, отличная от UTF-8.
При всей ее удобстве и универсальности, эта кодировка, все-таки, обладает недостатками, как минимум:
- Увеличивает объем вебстраницы (в байтах), если она содержит нелатинский шрифт (например, китайский или кириллицу);
- Создает сложности при распределении оперативной памяти для размещения строк, так как символы, закодированные в UTF-8 имеют разное количество байт. При этом невозможно определить объем требуемой памяти, исходя только из количества символов в строке;
- В силу существования многочисленных некорректных последовательностей символов utf-8, возможна так называемая «атака неверной кодировкой», которая, при отсутствии надлежащих фильтров на сервере, может привести к его «взлому». Ряд программ может завершиться с ошибкой. Наличие некорректной последовательности UTF-8 в коде javascript при обработке его браузером вызывает ошибку.
Проверено в РНР 5.3
Все, о чем пойдет речь ниже, протестировано в PHP5.3. Как оно будет работать в более высоких версиях РНР – сказать крайне сложно. Необходимо будет повторять тестирование.
Преобразовываем символы в html-сущности в кодировке UTF-8
Для этого используем функцию htmlentities (РНР):
<?php
header('Content-Type: text/html; charset=utf-8');
echo htmlentities('ア " привет hell × $& '. '\x8F2G', ENT_QUOTES, "UTF-8"). "<br/>";
// Выводит: ア " привет hell × $& \x8F2G
echo htmlentities("ア \" привет hell × $& ". '\x8F2G', ENT_QUOTES, "UTF-8"). "<br/>";
// Выводит: ア " привет hell × $& \x8F
echo htmlentities("ア \" привет hell × $& ". "\x8F2G", ENT_QUOTES, "UTF-8"). "<br/>";
// Выводит:
echo htmlentities("ア \" привет hell × $& ". "\x8F2G", ENT_IGNORE, "UTF-8"). "<br/>"; // Не рекомендуется!!
// Выводит: ア " привет hell × $& 2G
А вот что на самом деле было прислано браузеру от сервера:
ア " привет hell × $& \x8F2G<br/>
ア " привет hell × $& \x8F2G<br/>
<br/>
ア " привет hell × $& 2G<br/>
Т.е. преобразовались кавычки, амперсанд и крестик.
Кстати, файл, в котором сохранен этот скрипт, должен быть в кодировке UTF-8. Поэтому и браузеру отсылаем заголовок, в котором указываем ту же самую кодировку. Иначе браузер, скорее всего, неверно отобразит ответ сервера, т.е. отобразит некую абракадабру. Тогда для целей читаемости, придется устанавливать в браузере кодировку вручную.
Как видим, при указании параметра ENT_IGNORE
сервер все-таки что-то отвечает браузеру, но не полностью. Такое поведение, конечно, может являться возможностью для реализации уязвимостей. Поэтому этот параметр целесообразно применять, разве что, для целей тестирования, когда заведомо известно, что сервер не получит от клиента (браузера) вредоносного кода, при необходимости вывести полный результат работы сервера. В рабочих вариантах сервера этот параметр применять не следует.
Также видим здесь разницу между одиночными (апострофами) и двойными кавычками в PHP: если одиночные кавычки отображают содержащиеся в них символы в состоянии «как есть», то двойные делают соответствующие преобразования.
Преобразовываем символы в html-сущности в кодировке Windows-1251
Для проверки, попробуем то же самое сделать, используя кодировку Windows-1251 (синоним: cp1251). Для этого отсылаем серверу соответствующий заголовок и соответствующим же образом перекодируем сообщения сервера:
<?php
header('Content-Type: text/html; charset=cp1251');
echo htmlentities(mb_convert_encoding('ア " привет hell × $& '. '\x8F2G', "cp1251", "utf-8"), ENT_QUOTES, "cp1251"). "<br/>";
// Выводит: ? " привет hell ? $& \x8F2G
echo htmlentities(mb_convert_encoding("ア \" привет hell × $& ". '\x8F2G', "cp1251", "utf-8"), ENT_QUOTES, "cp1251"). "<br/>";
// Выводит: ? " привет hell ? $& \x8F2G
echo htmlentities(mb_convert_encoding("ア \" привет hell × $& ". "\x8F2G", "cp1251", "utf-8"), ENT_QUOTES, "cp1251"). "<br/>";
// Выводит: ? " привет hell ? $& 2G
echo htmlentities(mb_convert_encoding("ア \" привет hell × $& ". "\x8F2G", "cp1251", "utf-8"), ENT_IGNORE, "cp1251"). "<br/>"; // Не рекомендуется!!
// Выводит: ? " привет hell ? $& 2G
Как видно, символы ア
и ×
выведены в качестве знаков вопроса (т.е. не распознаны). Установка внутренней кодировки скрипта
mb_internal_encoding("cp1251");
абсолютно не помогает.
Если убрать htmlentities, то будет то же самое. При этом, естественно, символ амперсанда (&
) будет присутствовать на странице именно как &
, а не как &
, т.е. не будет преобразован.
А вот что будет в самом ответе сервера (посмотреть ответ сервера, например, в браузере Firefox можно, посмотрев исходный код страницы или нажав правую кнопку мыши ->
Исследовать элемент ->
вкладка Сеть ->
Ответ):
? " привет hell ? $& \x8F2G
? " привет hell ? $& \x8F2G
? " привет hell ? $& 2G
? " привет hell ? $& 2G
Таким образом, в специальные сущности преобразовались только кавычки, амперсанд и кириллица. Ни ア
, ×
– не преобразовались. Что противоречит так называемому «мануалу» (по ссылке, приведенной выше). Вот фраза оттуда:
«htmlentities(
) преобразует все символы в соответствующие HTML-сущности (для тех символов, для которых HTML-сущности существуют)».
Однако, функция htmlentities
закодировала только так называемые специальные символы и кириллицу, тогда как ア, × не преобразовались. Хотя сущности для них – существуют.
Если убрать htmlentities из кода, то сервер даст такой ответ:
? " привет hell ? $& \x8F2G
? " привет hell ? $& \x8F2G
? " привет hell ? $& 2G
? " привет hell ? $& 2G
При этом в браузере на открытой странице ничего не изменится. Теперь кириллица присутствует, как положено. В обычном своем незакодированном виде.
Таким образом:
- Функция
htmlentities
работает, как минимум, ЗАВИСИМО от указания кодировки. В случае кодировки UTF-8 кириллица не преобразуется. А вот в случае Windows-1251 – она преобразуется в html-сущности. - Если символ не содержится во множестве кодировки, указанной в качестве параметра в htmlentities, он, видимо, не будет отображен (точнее, вместо него будут отображаться знаки вопроса), что видно на примере кодировки Windows-1251. В «мануале» об этом почему-то речи не идет.
По всей видимости, программист должен телепатически догадаться обо всем этом или лезть в исходные коды функций РНР. Зачем так сделано, и, главное, почему в «мануале» (на который почему-то любят ссылаться на форумах в интернете) об этом нет ни слова – непонятно.
Кодировка HTML-ENTITIES
Это – достаточно редко используемая кодировка, но она есть в РНР. Используется она там не везде. А, например, в функции mb_convert_encoding. Можно попробовать преобразовать строку в эту кодировку:
header('Content-Type: text/html; charset=utf-8');
$x = mb_convert_encoding("ア \" привет hell × $& ". "\x8F2G", "HTML-ENTITIES", "utf-8"). "<br/>";
echo $x;
Вот что сервер выдаст:
ア " привет hell × $& 2G<br/>
А вот что будет наблюдаться в браузере:
ア " привет hell × $& 2G
Как видно, успешно перекодировались символы кириллицы, а также ア , ×. Результат отличается от результата работы функции htmlentities.
Таким образом, использование функции mb_convert_encoding
с кодировкой HTML-ENTITIES предпочтительнее, чем использование функции htmlentities в случае, если необходимо перекодировать символы национального языка (в данном случае – русского) и специальные символы типа ×
, не трогая служебные символы типа кавычек, амперсанда и т.п.
Причем, результат будет таким же и в случае использования кодировки Windows-1251 или даже в случае, например, кодировки JIS, т.е. когда будет
header('Content-Type: text/html; charset=cp1251');
или
header('Content-Type: text/html; charset=JIS');
Напоследок
К сожалению, УВЫ, вот так приходится, что называется, «иметь дело» со многими, если не практически со всеми функциями РНР. В целом-то, да, они как-то там даже работают. Но, чуть специфические условия использования – и всё. Приходится отлаживать, выяснять, насколько приемлем результат.
Так что же, PHP и в самом деле ужасен?
Как сказать… смотря, с чем сравнивать. Если с языком С – то да, конечно. Правда, в отличие от С, у РНР есть одно неоспоримое достоинство: программа, написанная на нем, имеет объем раза в 2…3 меньший, чем аналогичная программа на С. Что же касается порога вхождения – он практически одинаков что в РНР, что в С.
Ну, а если говорить о разных новомодных Haskell, Kotlin, Go, Go! и иже с ними – так, видимо, они не кажутся столь «ужасными» ТОЛЬКО потому, что еще не столь сильно распространены, еще не столь много опыта их использования. В конце концов, еще лет 10…15 назад многие думали, что и операционная система Linux абсолютно защищена от вирусов. РНР-то, по крайней мере, более-менее апробирован и основные ошибки (баги) в нем так или иначе устраняются.