PHP Looping Template Engine - с нуля - PullRequest
       14

PHP Looping Template Engine - с нуля

16 голосов
/ 16 февраля 2011

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

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

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

В настоящий момент основной синтаксический анализ выполняется с помощью regex и 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>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>

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

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

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

Pps Кажется, что существует огромное количество мнений по этой теме, пожалуйста, не гголосуйте за людей, потому что у них другое мнение к вам.Каждый имеет право на свое!

Ответы [ 6 ]

22 голосов
/ 16 февраля 2011

Простой подход - преобразовать шаблон в 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:

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

В основном для предотвращения любого вида прямого ввода PHP-кода, либо <?, <%, либо <script language="php">.

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

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

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

5 голосов
/ 16 февраля 2011

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

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

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

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

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

  • Template
  • TemplateScope

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

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

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

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

1 голос
/ 16 февраля 2011

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

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

удачи

1 голос
/ 16 февраля 2011

Если я не беспокоюсь о кешировании или других сложных темах, которые могут подтолкнуть меня к устоявшемуся движку шаблонов, например, 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>
0 голосов
/ 16 февраля 2011

Smarty :) ...

php:

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

smarty template:

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

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

0 голосов
/ 16 февраля 2011

Использование Smarty .

...