Статьи

Динамические глобальные функции в PHP

Как и многие другие, я предпочитаю использовать процедурный PHP в качестве языка шаблонов . Хотя синтаксис PHP делает его практичным выбором, существует проблема с встраиванием динамического контента. Большинство PHP-приложений генерируют HTML-вывод, поэтому вы в конечном итоге много пишете <?php echo htmlspecialchars($foo);?> , Используя эту технику. Или вы забудете об этом и сделаете ваше приложение подверженным всевозможным неприятным атакам XSS

Помимо раздражения от лишней типизации, есть опасность стать ленивым, увидев, что <?php echo $foo;?> Заметно короче типа. В некоторых ситуациях это также не проявляется как проблема, поскольку некоторые типы содержимого никогда не содержат специальных символов HTML (например, Numbers). Это особенно неприятно, потому что ошибки в слое представления общеизвестно трудно отследить, и в отличие от SQL-инъекций — похожая проблема — последствия, как правило, наносят ущерб пользователям сайта, а не непосредственно сайту.

ПОЦЕЛУЙ

Недавно я посмотрел код, написанный для CakePHP . Мой взгляд поймал функцию e , которая является сокращением для echo . Обычная функция, состоящая из одной буквы, несомненно, является самым простым способом расширения синтаксиса PHP. Думая об этом, это довольно очевидно, но мне это никогда не приходило в голову.
Что ж, разработчики CakePHP допустили ошибку, так как это должно было быть сокращением для echo htmlspecialchars . Тем не менее, синтаксис работает хорошо. Поэтому я начал использовать глобально определенную функцию, которая выглядит примерно так:

function e($string) { echo htmlspecialchars($string); }
function e($string) { echo htmlspecialchars($string); } 

И это тоже работает. Это избавляет меня от необходимости печатать и не забывает выходить из выходных данных, потому что мне нужно больше работать, чтобы выводить строки без экранирования, чем нет. Простой, но мощный.

Столкновение

Есть проблема, хотя; Поскольку это такое хорошее название для функции, есть вероятность, что кто-то другой будет использовать ее для чего-то другого или, возможно, даже для того же самого. Я уже знаю по крайней мере один потенциальный конфликт имен, так как я получил идею от CakePHP.

Обычный способ справиться с этим — использовать пространства имен, но, увы, PHP не имеет пространств имен ( пока ), и общее решение псевдо-пространств имен (например, имен с префиксами) здесь не работает, так как оно не поддается Цель функции в первую очередь.

Есть и другая проблема. В тех немногих случаях, когда мы не визуализируем вывод HTML / XML, мы не хотим экранировать строки для встраивания в HTML / XML — вместо этого мы хотим экранировать их для встраивания в этот целевой язык. Даже в HTML может потребоваться экранирование строк с htmlentities , а не htmlspecialchars , если кодировка текста не соответствует ISO-8859-1. Или закодируйте строку в UTF-8, если шаблон находится в UTF-8.

Создание статической динамики

Проблема всего этого в том, что функция является статической — такова природа глобальных функций в PHP. Другие интерпретируемые языки позволяют нам переопределять функции во время выполнения, но с PHP не повезло (ну, строго говоря, runkit это позволяет, но никто в здравом уме не будет использовать его в производственной среде).
Однако есть лазейка; Используя обратный вызов , мы можем делегировать динамически определенный обработчик:

if (!function_exists('e')) { function e($args) { $args = func_get_args(); return call_user_func_array($GLOBALS['_global_function_handler_e'], $args); } }
if (!function_exists('e')) { function e($args) { $args = func_get_args(); return call_user_func_array($GLOBALS['_global_function_handler_e'], $args); } } 

Намного лучше — теперь я могу определить свой собственный обработчик вывода как:

$GLOBALS['_global_function_handler_e'] = 'my_global_function_handler_e'; function my_global_function_handler_e($string) { echo htmlspecialchars($string); }
$GLOBALS['_global_function_handler_e'] = 'my_global_function_handler_e'; function my_global_function_handler_e($string) { echo htmlspecialchars($string); } 

И CakePHP может использовать:

$GLOBALS['_global_function_handler_e'] = 'cakephp_global_function_handler_e'; function cakephp_global_function_handler_e($string) { echo htmlspecialchars($string); }
$GLOBALS['_global_function_handler_e'] = 'cakephp_global_function_handler_e'; function cakephp_global_function_handler_e($string) { echo htmlspecialchars($string); } 

Мы все еще используем глобальный символ, но, по крайней мере, он динамический, а не статический.

Итак, вот призыв к создателям фреймворков в мире PHP: если бы мы все согласились сделать это для любой функции, определенной в глобальной области видимости, которая имеет очевидный риск столкновения имен , я думаю, что нам всем было бы лучше.

Просто скромное предложение, конечно.