Циклический движок Шаблонов PHP - С Нуля


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

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

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

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

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

Ан пример, если были предоставлены данные для механизма синтаксического анализа:

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 Похоже, что есть огромное распространение мнений по этой теме, пожалуйста, не голосуйте против людей, потому что у них другое мнение по отношению к вам. Каждый имеет право на свое собственное!

Author: Pez Cuckow, 2011-02-16

6 answers

Простой подход состоит в том, чтобы преобразовать шаблон в 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);

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

 22
Author: Thai, 2011-02-16 15:34:22

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

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

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

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

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

  • Шаблон
  • Область шаблонов

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

Вот краткий пример:

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;
    }
}

Это чрезвычайно простой материал, который вы могли бы расширить, так что о области, Это в основном класс, в котором ваши представления компилируются в интерпретаторе, это позволит вам иметь доступ к методам в классе 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; // :)
    }
}

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

$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, который хранит предварительно скомпилированный исходный код в памяти, пропуская большую часть времени компиляции/интерпретации

 4
Author: RobertPitt, 2011-02-16 15:05:45

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

$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>
 1
Author: Ericson578, 2011-02-16 14:44:21

У меня был очень простой ответ на что-то вроде этого еще до того, как я начал использовать 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 для размещения всех ваших данных.

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

Хорошо удача

 1
Author: steve, 2011-02-16 14:53:04

Умный:)...

Php:

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

Умный шаблон:

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

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

 -4
Author: Brian, 2011-02-16 14:32:11

Используйте Умный.

 -10
Author: powtac, 2011-02-16 14:32:04