2010-07-24 4 views
5

ОбзорPHP - templating с пользовательскими тегами - это законное использование eval?

Примерно в конце 2009 года я написал простую шаблонную систему для PHP/HTML для использования в доме нашими дизайнерами для веб-сайтов типа брошюры-Ware. Целью системы является создание шаблонов в иначе чистом HTML через пользовательские теги, которые обрабатываются PHP. Например, шаблонный страница может выглядеть следующим образом:

<tt:Page template="templates/main.html"> 
    <tt:Content name="leftColumn"> 
    <p> blah blah </p> 
    ... 
    </tt:Content> 
    <tt:Content name="rightColumn"> 
    <p> blah blah </p> 
    ... 
    </tt:Content> 
</tt:Page> 

шаблон, сама может выглядеть примерно так:

<html> 
    <head>...</head> 
    <body> 
    <div style="float:left; width:45%"> 
     <tt:Container name="leftColumn" /> 
    </div> 
    <div style="width:45%"> 
     <tt:Container name="rightColumn" /> 
    </div> 
    </body> 
</html> 

Помимо страниц и контента/теги контейнеров, есть несколько других тегов включены в основном для таких вещей, как управление потоком, итерация по коллекции, вывод динамических значений и т. д. Структура разработана таким образом, что очень легко добавить свой собственный набор тегов, зарегистрированных под другим префиксом и пространством имен.

Пользовательские теги для PHP

Как мы разбираем эти пользовательские теги? Поскольку это не гарантирует, что HTML-файл является хорошо сформированным XML, такие решения, как XSLT/XPATH, не будут надежными. Вместо этого мы используем регулярное выражение для поиска тегов с зарегистрированными префиксами и заменяем их кодом PHP. PHP-код представляет собой конструкцию на основе стека ... при столкновении с открывающим тегом создается объект, представляющий тэг, который помещается в стек, и запускается его «функция инициализации» (если таковая имеется). Всякий раз, когда встречается зарегистрированный закрывающий тег, последний стек вылетает из стека и запускается его «функция рендеринга».

Таким образом, после того, как основа заменяет шаблонных тегов с PHP, наш пример страницы может выглядеть следующим образом (в недвижимости это немного уродливее):

<?php $tags->push('tt', 'Page', array('template'=>'templates/main.html')); ?> 
    <?php $tags->push('tt', 'Content', array('name'=>'leftColumn')); ?> 
    <p> blah blah </p> 
    ... 
    <?php $tags->pop(); ?> 
    <?php $tags->push('tt', 'Content', array('name'=>'rightColumn')); ?> 
    <p> blah blah </p> 
    ... 
    <?php $tags->pop(); ?> 
<?php $tags->pop(); ?> 

Хороший, плохой, и eval

Теперь, как выполнить наш недавно сгенерированный PHP-код? Я могу придумать несколько вариантов. Проще всего просто проложить строку, и это работает достаточно хорошо. Однако любой программист скажет вам, что «eval - это зло, не используйте его ...», поэтому вопрос в том, есть ли что-то более подходящее, чем eval, которое мы можем использовать здесь?

Я рассмотрел использование временного или кэшированного файла с использованием потоков вывода php:// и т. Д., Но, насколько я вижу, они не предлагают никаких реальных преимуществ перед eval. Кэширование может ускорить процесс, но на практике все сайты, которые мы имеем на эту вещь, уже невероятно быстры, поэтому я не вижу необходимости в оптимизации скорости на этом этапе.

Вопросы

Для каждой из вещей в этом списке: это хорошая идея? Можете ли вы подумать о лучшей альтернативе?

  • вся идея в целом (пользовательские теги HTML/PHP)
  • преобразования тегов в PHP кода вместо обработки непосредственно
  • подход стека на основе
  • использование eval (или аналогичный)

Спасибо за чтение и TIA за любые советы. :)

+0

+1 для хорошо отформатированного, хорошо написанного и хорошо продуманного вопроса. – gpmcadam

+0

Спасибо. OT: Мне нравится ваш аватар. Гигантор из Evol Intent был одет в футболку с таким же дизайном, когда он приехал сюда несколько лет назад. Это логотип для лейбла или что-то еще, или просто случайный дизайн? Интересно, что годами. –

ответ

2

Позвольте мне выступать иной подход. Вместо того, чтобы генерировать PHP-код динамически, а затем пытаться выяснить, как его безопасно выполнить, выполните его непосредственно, когда вы сталкиваетесь с тегами. Вы можете обрабатывать весь блок HTML за один проход и обрабатывать каждый тег, когда вы сталкиваетесь с ним сразу.

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

  1. Посмотрите на пользовательский тег, который вы найдете в положении п.
  2. Все до позиции n должно быть простым HTML, поэтому либо сэкономьте его для обработки или вывода его немедленно (если у вас нет тегов в стеке $tags, вам, вероятно, не нужно его сохранять в любом месте).
  3. Выполните соответствующий код для тега. Вместо того, чтобы генерировать код, который вызывает $tags->push, просто позвоните по номеру $tags->push.
  4. Вернитесь к шагу 1.

При таком подходе вы только называете PHP функции напрямую, вы никогда не строить PHP-код на лету, а затем выполнить его позже. Потребность в eval исчезла.

В шаге 3 в основном есть два случая. Когда вы встретите открывающий тег, вы сразу же выполните push. Затем, когда вы нажмете на закрывающий тег, вы можете сделать pop, а затем обработать тег соответствующим образом, теперь, когда вы обработали все содержимое настраиваемого элемента.

Это также более эффективно обрабатывать HTML таким образом. Выполнение множественного поиска и замещения на длинной строке HTML неэффективно, так как каждый поиск и каждая замена - это O ( n) по длине строки.Это означает, что вы повторно просматриваете строку снова и снова, и каждый раз, когда вы делаете замену, вы должны генерировать целые новые строки одинаковой длины. Если у вас есть 20 КБ HTML, то каждая замена включает в себя поиск через этот 20 КБ, а затем создание новой строки 20 КБ впоследствии.

+0

Джон, это звучит как правильный способ сделать это для строго чистого HTML (без PHP), и я сделаю снимок. Однако, когда я разработал систему шаблонов, я подумал, что было бы неплохо, если бы вы могли смешивать PHP, и я думаю, что некоторые из сайтов теперь смешиваются. Возможно, это была плохая идея дизайна, но я думаю, что сейчас это требует использования 'eval' или аналогичный. Кроме того, во время разработки я думал, что кэширование сгенерированного PHP будет хорошей идеей. Считаете ли вы, что это может быть сопоставимо или быстрее, чем ваш подход здесь? –

+0

Кэширование смягчит проблемы с производительностью, поэтому вам придется беспокоиться только об аспекте безопасности. –

+0

Если вы разрешаете пользователям смешиваться в PHP-коде, тогда это цельный «шарик из воска». Когда вы выполняете произвольный код, беспокоиться о вызове eval довольно бессмысленно. Тогда ваши соображения безопасности изменятся на песочницу PHP-кода, используя безопасный PHP-код, блокируя права пользователя, чтобы они не могли делать случайные вызовы системы() и т. Д. –

0

Да, вместо Eval, вы можете делать то, что Зенд и другие основные рамки этого, использовать буферизацию вывода:

ob_start(); 
include($template_file); //has some HTML and output generating PHP 
$result = ob_get_contents(); 
ob_end_clean(); 
+0

Это означало бы сохранение промежуточного PHP-кода (который генерируется при разборе разметки) в файл, а затем его включение. Это связано с большим объемом доступа к диску, который просто использует «eval». В чем преимущество этого? –

+0

Я понимаю, что вы имеете в виду. Этот ответ не ошибается, но тогда не реалистичен. Я отправлю еще один, более разумный ответ –

+0

@no, мой новый ответ опубликован. –

2

Я отправил еще один ответ, потому что это радикально отличается от первой, которая также может быть ценный.

По существу, этот вопрос задает вопрос о том, как выполнить PHP-код с регулярным выражением. Возможно, это не так очевидно, но это то, что намеревается выполнить eval.

С учетом сказанного вместо того, чтобы выполнять проход preg_replace, а затем делать eval, вы можете просто использовать функцию preg_replace_callback PHP для выполнения части кода при согласовании.

Смотрите здесь, как работает эта функция: http://us.php.net/manual/en/function.preg-replace-callback.php

+0

Майк, см. Мой ответ Джону (вы, ребята, говорите почти то же самое, что я думаю). –