PHP - создание шаблонов с пользовательскими тегами - является ли это законным использованием eval?

StackOverflow https://stackoverflow.com/questions/3326446

  •  29-09-2020
  •  | 
  •  

Вопрос

Обзор

Примерно в конце 2009 года я написал простую систему шаблонов для PHP / HTML, которая будет использоваться нашими дизайнерами для создания веб-сайтов типа брошюр.Цель системы - разрешить создание шаблонов в остальном чистом 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 - это зло, не используйте его ..." поэтому вопрос в том, есть ли что-нибудь более подходящее, чем eval что мы можем здесь использовать?

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

Вопросы

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

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

Спасибо за чтение и ТИА за любой совет.:)

Это было полезно?

Решение

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

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

  1. Найдите пользовательский тег, который вы найдете в разделе position n.
  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 КБ.

Другие советы

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

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

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

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

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

ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top