PHP Looping Template Engine - от царапин

Для группового проекта я пытаюсь создать механизм шаблонов для PHP для людей с меньшим опытом работы с языком, который может использовать теги, такие как {name} в своем HTML, и PHP заменит этот тег на предопределенную переменную из массива. А также поддерживающие петли.

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

Мои главные вопросы: как мне сделать часть цикла анализатора, и это лучший способ реализовать такую ​​систему. Прежде чем просто рекомендовать существующую систему шаблонов, я бы предпочел создать ее самостоятельно для получения опыта и потому, что все в нашем проекте должно быть нашим.

В настоящий момент основной синтаксический анализ выполняется с помощью регулярных выражений и preg_replace_callback, он проверяет, существует ли $data [name] и если он его заменяет.

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

Пример, если данные, которые был предоставлен механизмом анализа, были следующими:

Array
(
    [title] => The Title
    [subtitle] => Subtitle
    [footer] => Foot
    [people] => Array
        (
            [0] => Array
                (
                    [name] => Steve
                    [surname] => Johnson
                )

            [1] => Array
                (
                    [name] => James
                    [surname] => Johnson
                )

            [2] => Array
                (
                    [name] => josh
                    [surname] => Smith
                )

        )

    [page] => Home
)

И страница, на которой он разбирался, была чем-то вроде:

<html>
<title>{title}</title>
<body>
<h1>{subtitle}</h1>
{LOOP:people}
<b>{name}</b> {surname}<br />
{ENDLOOP:people}
<br /><br />
<i>{footer}</i>
</body>
</html>

Это создаст нечто похожее на:

<html>
<title>The Title</title>
<body>
<h1>Subtitle</h1>
<b>Steve</b> Johnson<br />
<b>James</b> Johnson<br />
<b>Josh</b> Smith<br />
<br /><br />
<i>Foot</i>
</body>
</html>

Ваше время невероятно ценится с этим!

Большое спасибо,

P.s. Я полностью не согласен с тем, что, поскольку я ищу, чтобы создать нечто похожее на то, что уже существует для опыта, мой хорошо отформатированный и простой для понимания вопрос проголосовал.

P.p.s Похоже, что существует огромное распространение мнений по этой теме, пожалуйста, не голосуйте, потому что у них другое мнение. Каждый человек имеет право на свои права!

Ответы

Ответ 1

Простым подходом является преобразование шаблона в PHP и его запуск.

$template = preg_replace('~\{(\w+)\}~', '<?php $this->showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', '<?php foreach ($this->data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', '<?php $this->unwrap(); endforeach; ?>', $template);

Например, это преобразует теги шаблона во встроенные теги PHP.

Вы увидите, что я сделал ссылки на $this->showVariable(), $this->data, $this->wrap() и $this->unwrap(). Это то, что я собираюсь реализовать.

Функция showVariable показывает содержимое переменной. wrap и unwrap вызывается на каждой итерации, чтобы обеспечить закрытие.

Вот моя реализация:

class TemplateEngine {
    function showVariable($name) {
        if (isset($this->data[$name])) {
            echo $this->data[$name];
        } else {
            echo '{' . $name . '}';
        }
    }
    function wrap($element) {
        $this->stack[] = $this->data;
        foreach ($element as $k => $v) {
            $this->data[$k] = $v;
        }
    }
    function unwrap() {
        $this->data = array_pop($this->stack);
    }
    function run() {
        ob_start ();
        eval (func_get_arg(0));
        return ob_get_clean();
    }
    function process($template, $data) {
        $this->data = $data;
        $this->stack = array();
        $template = str_replace('<', '<?php echo \'<\'; ?>', $template);
        $template = preg_replace('~\{(\w+)\}~', '<?php $this->showVariable(\'$1\'); ?>', $template);
        $template = preg_replace('~\{LOOP:(\w+)\}~', '<?php foreach ($this->data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
        $template = preg_replace('~\{ENDLOOP:(\w+)\}~', '<?php $this->unwrap(); endforeach; ?>', $template);
        $template = '?>' . $template;
        return $this->run($template);
    }
}

В функции wrap() и unwrap() я использую стек, чтобы отслеживать текущее состояние переменных. Точно, wrap($ELEMENT) сохраняет текущие данные в стек, а затем добавляет переменные внутри $ELEMENT в текущие данные, а unwrap() восстанавливает данные из стека назад.

Для дополнительной безопасности я добавил этот дополнительный бит, чтобы заменить < на PHP echos:

$template = str_replace('<', '<?php echo \'<\'; ?>', $template);

В основном, чтобы предотвратить любые инъекции PHP-кода напрямую, либо <?, <%, либо <script language="php">.

Использование выглядит примерно так:

$engine = new TemplateEngine();
echo $engine->process($template, $data);

Это не лучший метод, но это один из способов сделать это.

Ответ 2

Хорошо, сначала позвольте мне объяснить, что-то скажет вам, что PHP - ПАРАМЕТР ШАБЛОНА.

Выполняя то, что вы делаете, как создание парсера шаблонов из парсера шаблонов, бессмысленно, и, честно говоря, он повторяет мне, что парсер шаблонов, такой как smarty, стал так хорошо в бесцельной задаче.

То, что вы должны делать, это создать вспомогательный элемент шаблона, а не синтаксический анализатор, поскольку он является избыточным, в терминах программирования файл шаблона называется view, и одна из причин, по которым им была дана конкретная это то, что люди знали бы там отдельно от моделей, Domain Logic и т.д.

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

Примером этого является использование 2 классов

  • Шаблон
  • TemplateScope

Функциональность класса шаблона заключается в том, что Domain Logic устанавливает данные в представление и обрабатывает его.

Вот пример:

class Template
{
    private $_tpl_data = array();

    public function __set($key,$data)
    {
        $this->_tpl_data[$key] = $data;
    }

    public function display($template,$display = true)
    {
        $Scope = new TemplateScope($template,$this->_tpl_data); //Inject into the view
        if($display === true) 
        {
            $Scope->Display();
            exit;
        }
        return $Scope;
    }
}

Это простейший базовый материал, который вы можете расширить, oko так о Scope. Это в основном класс, в котором ваши представления компилируются в интерпретаторе, это позволит вам получить доступ к методам в классе TemplateScope, но не за пределами области класс, то есть имя.

class TemplateScope
{
    private $__data = array();
    private $compiled;
    public function __construct($template,$data)
    {
        $this->__data = $data;
        if(file_exists($template))
        {
            ob_start();
            require_once $template;
            $this->compiled = ob_get_contents();
            ob_end_clean();
        }
    }

    public function __get($key)
    {
        return isset($this->__data[$key]) ? $this->__data[$key] : null;
    }

    public function _Display()
    {
        if($this->compiled !== null)
        {
             return $this->compiled;
        }
    }

    public function bold($string)
    {
        return sprintf("<strong>%s</strong>",$string);
    }

    public function _include($file)
    { 
        require_once $file; // :)
    }
}

Это только базовый и не работает, но концепция есть, Heres пример использования:

$Template = new Template();

$Template->number = 1;
$Template->strings = "Hello World";
$Template->arrays = array(1,2,3,4)
$Template->resource = mysql_query("SELECT 1");
$Template->objects = new stdClass();
$Template->objects->depth - new stdClass();

$Template->display("index.php");

и внутри шаблона вы должны использовать традиционный php, например:

<?php $this->_include("header.php") ?>
<ul>
    <?php foreach($this->arrays as $a): ?>
        <li><?php echo $this->bold($a) ?></li>
    <?php endforeach; ?>
</ul>

Это также позволяет вам включать в шаблоны, которые по-прежнему имеют доступ к ключевому слову $this, а затем включать сами себя, своего рода рекурсию (но не ее).

Затем не программируйте создание кеша, так как нечего кэшировать, вы должны использовать memcached, который хранит предварительно скомпилированный исходный код в памяти, пропускающий большую часть времени компиляции/интерпретации

Ответ 3

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

$name = 'Eric';
$locations = array('Germany', 'Panama', 'China');

include('templates/main.template.php');

main.tempate.php использует альтернативный синтаксис тегов php, который довольно легко использовать для пользователей, не имеющих PHP, просто попросите их игнорировать что-либо завернутый в тег php:)

<h2>Your name is <?php echo $name; ?></h2>
<?php if(!empty($locations)): ?>
  <ol>
    <?php foreach($locations as $location): ?>
    <li><?php echo $location; ?></li>
    <?php endforeach; ?>
  </ol>
<?php endif; ?>
<p> ... page continues ... </p>

Ответ 4

У меня был очень простой ответ на что-то вроде KINDA, как это было до того, как я начал использовать DOMXPath.

класс - это что-то вроде этого (не уверен, что он работает совсем как то, что вы хотите, но пища для размышлений, поскольку он работает очень просто

<?php
class template{
private $template;

function __CONSTRUCT($template)
{
    //load a template
    $this->template = file_get_contents($template);
}

function __DESTRUCT()
{
    //echo it on object destruction
    echo $this->template;
}

function set($element,$data)
{
    //replace the element formatted however you like with whatever data
    $this->template = str_replace("[".$element."]",$data,$this->template);
}
}
?>

с этим классом вы просто создадите объект с любым шаблоном, который хотите, и используйте функцию set, чтобы поместить все ваши данные.

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

удача

Ответ 5

Smarty:)...

PHP:

$smarty->assign("people",$peopleArray)

smarty template:

{foreach $people as $person}
<b>{$person.name}</b> {$person.surname}<br />
{/foreach}

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

Ответ 6

Используйте Smarty.