Супер-light templating system в PHP, который не позволяет PHP-код внутри шаблонов или использовать eval

Я ищу очень базовую систему шаблонов PHP. Сейчас я использую:

/**
 * Renders a single line. Looks for {{ var }}
 *
 * @param string $string
 * @param array $parameters
 *
 * @return string
 */
function renderString($string, array $parameters)
{
    $replacer = function ($match) use ($parameters)
    {
        return isset($parameters[$match[1]]) ? $parameters[$match[1]] : $match[0];
    };

    return preg_replace_callback('/{{\s*(.+?)\s*}}/', $replacer, $string);
}

(отсюда: PHP - очень легкая система шаблонов)

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

Я нашел Rain TPL - http://www.raintpl.com/Quick-Start/#if - это очень близко к тому, что я ищу, но есть несколько вещей что мне это не нравится:

  • он позволяет чуваку, который пишет шаблон, запускать функции PHP (внутри условия IF).
  • он пишет файлы кеша и php, которые я не хочу

Итак, что-то похожее на это, но еще более "базовое", строгое и безопасное?

Ответы

Ответ 1

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

Я думаю, это лучше для вас, если вы измените существующий механизм шаблонов на ваши нужды.

Вы можете изменить Rain TPL, чтобы отключить некоторые из его функций, которые вам не нужны. Например, вы можете сделать...

  • Отключить функцию в операторах IF:
    а. Найдите elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){

    б. Замените $this->function_check( $tag ); новым способом что-то вроде $this->ifcondition_function_check( $tag );

    с. Создайте новый метод, который отключит все функции в операторах IF.

    private function ifcondition_function_check($code)
    {
        $preg = '/[a-zA-z0-9]+\((.*?)\)/';
        if (preg_match( $preg, $code, $match ) ){
            // find the line of the error
            $line = 0;
            $rows=explode("\n",$this->tpl['source']);
            while( !strpos($rows[$line],$code) )
                    $line++;
    
            // draw the error line
            $error = str_replace( array('<','>'), array( '&lt;','&gt;' ), array($code,$rows[$line]) );
            $error = str_replace( $code, "<font color=red>$code</font>", $rows[$line] );
    
            // debug the error and stop the execution of the script
            die( "<div>RainTPL Sandbox Error in template <b>{$this->tpl['tpl_filename']}</b> at line $line : <i>$error</i></b>" );
        }
    }
    

    д. Теперь функции отключены.

    • Удалить файл кеша. (Кэш файл в Rain TPL - это PHP файл с заменой тэгов шаблонов на PHP-код)
      а. Перейти к методу draw()
      б. Найдите unset( $this->tpl );
      с. Перед этой строкой удалите файл с соблюдением (cache) @unlink($this->tpl['compiled_filename']);.
      д. Теперь файл кеша - это всего лишь временный файл для выполнения кода PHP.

Надеюсь, что это поможет

Ответ 2

Twig может быть для вас.

Он может выполнять условия и имеет режим песочницы для ненадежного кода.

Он выполняет компиляцию и кэширование, но это, похоже, можно отключить.

Ответ 3

Также есть порт Mustache для PHP. Здесь находится порт PHP . Синтаксис подобен тому, что вы уже делаете, и поддерживает простые циклы IF и FOREACH.

И это делает без eval.

Ответ 6

Когда вы хотите, чтобы он был действительно маленьким и гибким, возможно, лучше всего было бы остаться со своими вещами? Мне нравится ручная работа ;-) Вы можете расширить существующие функции. Далее, ваша функция плюс оператор if и loop и экранирование переменных для безопасности:

<?php
function renderString($str, $parms)
{
    // if
    $str = preg_replace_callback('/{{if (?P<name>\w+)}}(?P<inner>.*?){{endif}}/is', function($match) use ($parms) {
        if( isset($parms[$match['name']])) {
            // recursive
            return renderString($match['inner'], $parms);
        }
    }, $str);
    // loop
    $str = preg_replace_callback('/{{loop (?P<name>\w+)}}(?P<inner>.*?){{endloop}}/is', function($match) use ($parms) {
        if( isset($parms[$match['name']]) && is_array($parms[$match['name']])) {
            $str = '';
            foreach ($parms[$match['name']] as $value) {
                $parms['loop'] = $value;
                // recursive
                $str .= renderString($match['inner'], $parms);
            }
            return $str;
        }
    }, $str);
    // var
    $str = preg_replace_callback('/{{(?P<name>\w+)}}/is', function($match) use ($parms) {
        if( isset($parms[$match['name']])) {
            return htmlspecialchars($parms[$match['name']]);
        }
    }, $str);
    return $str;
}

$template = "<h1>{{title}}</h1>

{{if optional}}
<p>Optional: {{optional}}</p>
{{endif}}

{{if noop}}I'm not there{{endif}}

<ul>
{{loop numbers}}
<li>{{symbol}} {{loop}}</li>
{{endloop}}
</ul>";

echo renderString($template, array(
    'title'    => 'The Title',
    'optional' => 'I am optional',
    'numbers'  => array( 'one', 'two', 'three'),
    'symbol'   => '>',
));

Этот скрипт протестирован в PHP 5.3, и вы можете скопировать его 1:1 в файл для воспроизведения с ним.