Smarty 3.1.3 как сделать шаблон читабельнее?

babahalki

Постоялец
Регистрация
6 Май 2016
Сообщения
247
Реакции
107
Всем привет. Установил последнюю версию Smarty Для просмотра ссылки Войди или Зарегистрируйся , оказывается разрабатывается до сих пор.

Для тех кто не в курсе, Smarty - библиотека шаблонизатор для php.

Сейчас мой проект сильно усложнился, поэтому я решил хорошенько почистить все до чего дотянутся руки, для этого сначала я включил error_reporting = E_ALL и тут началось...
Практически каждая страница пестрила десятком ошибок notice undefined variable, undefined index.

Оказалось, что в эстетических целях в шаблонах повсеместно используются конcтрукции {if $keyword}, которые потом в скомпилированном файле превращаются в if($_tpl_vars['keyword']->value), что и приводит к ошибкам.

Я долго ковырялся и единственное где мне удалось срезать угол это тут:
libs/sysplugins/smarty_internal_compile_private_print_expression.php метод compile()
PHP:
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
    {
        // check and get attributes
        $_attr = $this->getAttributes($compiler, $args);
        $output = '@'.$parameter[ 'value' ]; //to avoid notice on undefined variables
...

В 3 строке я добавил к $output @.
После этих манипуляций ошибки типа: "undefined variable" в конструкции {$variable} больше не появляются.

к сожалению с if конструкциями такую хрень проделать не удалось. smarty собирает такой блок {if $a && $b}, как '$a && $b'. Поэтому просто дописать @ не выйдет без сложного парсинга всего массива if statement.

Может кто знает способ получше?
 
PHP:
{if isset($keyword)}
Не вариант использовать?
 
Так и приходится использовать. Но было бы гораздо удобнее, чтобы if $keyword становилось if isset($keyword) && $keyword
Тогда как вариант:
включить "error_reporting = E_ALL"
... почистить всё, что не связано с "undefined variable" ...
выключить "error_reporting = E_ALL"

Ну, или Smarty младшей версии использовать :)
 
Тогда как вариант:
включить "error_reporting = E_ALL"
... почистить всё, что не связано с "undefined variable" ...
выключить "error_reporting = E_ALL"

Ну, или Smarty младшей версии использовать :)

А что младшие версии иначе как-то компилируют if?

Отключить можно будет для продакшеша, но это все равно решение не очень. Хороший способ заставить смарти быть умнее, учитывая что смарти использует кеш, можно даже умно проверять каждый if statement. Вчера подумал, что при выборе удобства и скорости, скорость - лучше.

Кто бы помог проапгрейдить смарти. Я нашел класс, который за это отвечает. Вот он.
PHP:
<?php
/**
* Smarty Internal Plugin Compile If
* Compiles the {if} {else} {elseif} {/if} tags
*
* @package    Smarty
* @subpackage Compiler
* @author     Uwe Tews
*/

/**
* Smarty Internal Plugin Compile If Class
*
* @package    Smarty
* @subpackage Compiler
*/
class Smarty_Internal_Compile_If extends Smarty_Internal_CompileBase
{
    /**
     * Compiles code for the {if} tag
     *
     * @param array                                 $args      array with attributes from parser
     * @param \Smarty_Internal_TemplateCompilerBase $compiler  compiler object
     * @param array                                 $parameter array with compilation parameter
     *
     * @return string compiled code
     * @throws \SmartyCompilerException
     */
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
    {
        // check and get attributes
        $_attr = $this->getAttributes($compiler, $args);
        $this->openTag($compiler, 'if', array(1, $compiler->nocache));
        // must whole block be nocache ?
        $compiler->nocache = $compiler->nocache | $compiler->tag_nocache;

        if (!isset($parameter['if condition'])) {
            $compiler->trigger_template_error('missing if condition', null, true);
        }

        if (is_array($parameter[ 'if condition' ])) {
            if (is_array($parameter[ 'if condition' ][ 'var' ])) {
                $var = $parameter[ 'if condition' ][ 'var' ][ 'var' ];
            } else {
                $var = $parameter[ 'if condition' ][ 'var' ];
            }
            if ($compiler->nocache) {
                // create nocache var to make it know for further compiling
                $compiler->setNocacheInVariable($var);
            }
            $prefixVar = $compiler->getNewPrefixVariable();
            $_output = "<?php {$prefixVar} = {$parameter[ 'if condition' ][ 'value' ]};?>\n";
            $assignAttr = array();
            $assignAttr[][ 'value' ] = $prefixVar;
            $assignCompiler = new Smarty_Internal_Compile_Assign();
            if (is_array($parameter[ 'if condition' ][ 'var' ])) {
                $assignAttr[][ 'var' ] = $parameter[ 'if condition' ][ 'var' ][ 'var' ];
                $_output .= $assignCompiler->compile($assignAttr, $compiler,
                                                    array('smarty_internal_index' => $parameter[ 'if condition' ][ 'var' ][ 'smarty_internal_index' ]));
            } else {
                $assignAttr[][ 'var' ] = $parameter[ 'if condition' ][ 'var' ];
                $_output .= $assignCompiler->compile($assignAttr, $compiler, array());
            }
            $_output .= "<?php if ({$prefixVar}) {?>";
            return $_output;
        } else {
            return "<?php if ({$parameter['if condition']}) {?>";
        }
    }
}

/**
* Smarty Internal Plugin Compile Else Class
*
* @package    Smarty
* @subpackage Compiler
*/
class Smarty_Internal_Compile_Else extends Smarty_Internal_CompileBase
{
    /**
     * Compiles code for the {else} tag
     *
     * @param array                                 $args     array with attributes from parser
     * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
     *
     * @return string compiled code
      */
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
    {
        list($nesting, $compiler->tag_nocache) = $this->closeTag($compiler, array('if', 'elseif'));
        $this->openTag($compiler, 'else', array($nesting, $compiler->tag_nocache));

        return '<?php } else { ?>';
    }
}

/**
* Smarty Internal Plugin Compile ElseIf Class
*
* @package    Smarty
* @subpackage Compiler
*/
class Smarty_Internal_Compile_Elseif extends Smarty_Internal_CompileBase
{
    /**
     * Compiles code for the {elseif} tag
     *
     * @param array                                 $args      array with attributes from parser
     * @param \Smarty_Internal_TemplateCompilerBase $compiler  compiler object
     * @param array                                 $parameter array with compilation parameter
     *
     * @return string compiled code
     * @throws \SmartyCompilerException
     */
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
    {
        // check and get attributes
        $_attr = $this->getAttributes($compiler, $args);

        list($nesting, $compiler->tag_nocache) = $this->closeTag($compiler, array('if', 'elseif'));

        if (!isset($parameter['if condition'])) {
            $compiler->trigger_template_error('missing elseif condition', null, true);
        }

        $assignCode = '';
        $var = '';
        if (is_array($parameter[ 'if condition' ])) {
            $condition_by_assign = true;
            if (is_array($parameter[ 'if condition' ][ 'var' ])) {
                $var = $parameter[ 'if condition' ][ 'var' ][ 'var' ];
            } else {
                $var = $parameter[ 'if condition' ][ 'var' ];
            }
            if ($compiler->nocache) {
                // create nocache var to make it know for further compiling
                $compiler->setNocacheInVariable($var);
            }
            $prefixVar = $compiler->getNewPrefixVariable();
            $assignCode = "<?php {$prefixVar} = {$parameter[ 'if condition' ][ 'value' ]};?>\n";
            $assignCompiler = new Smarty_Internal_Compile_Assign();
            $assignAttr = array();
            $assignAttr[][ 'value' ] = $prefixVar;
            if (is_array($parameter[ 'if condition' ][ 'var' ])) {
                $assignAttr[][ 'var' ] = $parameter[ 'if condition' ][ 'var' ][ 'var' ];
                $assignCode .= $assignCompiler->compile($assignAttr, $compiler,
                                                       array('smarty_internal_index' => $parameter[ 'if condition' ][ 'var' ][ 'smarty_internal_index' ]));
            } else {
                $assignAttr[][ 'var' ] = $parameter[ 'if condition' ][ 'var' ];
                $assignCode .= $assignCompiler->compile($assignAttr, $compiler, array());
            }
        } else {
            $condition_by_assign = false;
        }

        $prefixCode = $compiler->getPrefixCode();
        if (empty($prefixCode)) {
            if ($condition_by_assign) {
                $this->openTag($compiler, 'elseif', array($nesting + 1, $compiler->tag_nocache));
                $_output = $compiler->appendCode("<?php } else {\n?>", $assignCode);
                return $compiler->appendCode($_output, "<?php if ({$prefixVar}) {?>");
            } else {
                $this->openTag($compiler, 'elseif', array($nesting, $compiler->tag_nocache));
                return "<?php } elseif ({$parameter['if condition']}) {?>";
            }
        } else {
            $_output = $compiler->appendCode("<?php } else {\n?>", $prefixCode);
            $this->openTag($compiler, 'elseif', array($nesting + 1, $compiler->tag_nocache));
            if ($condition_by_assign) {
                $_output = $compiler->appendCode($_output, $assignCode);
                return $compiler->appendCode($_output, "<?php if ({$prefixVar}) {?>");
            } else {
                return $compiler->appendCode($_output, "<?php if ({$parameter['if condition']}) {?>");
            }
        }
    }
}

/**
* Smarty Internal Plugin Compile Ifclose Class
*
* @package    Smarty
* @subpackage Compiler
*/
class Smarty_Internal_Compile_Ifclose extends Smarty_Internal_CompileBase
{
    /**
     * Compiles code for the {/if} tag
     *
     * @param array                                 $args     array with attributes from parser
     * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
     *
     * @return string compiled code
     */
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
    {
        // must endblock be nocache?
        if ($compiler->nocache) {
            $compiler->tag_nocache = true;
        }
        list($nesting, $compiler->nocache) = $this->closeTag($compiler, array('if', 'else', 'elseif'));
        $tmp = '';
        for ($i = 0; $i < $nesting; $i ++) {
            $tmp .= '}';
        }

        return "<?php {$tmp}?>";
    }
}

В первую функцию compile в переменной $parameter и передается if condition в виде строки внутри элемента массива $parameter['if condition']
 
Последнее редактирование:
может как-то так:
PHP:
...
$parameter['if condition'] = implode('@$', explode('$', $parameter['if condition']));
...

Так не пойдет для всех сложных условий if, у меня сразу вышибло ошибки на if isset(@$var)
Для функций надо пропускать модификацию.

Вот так вроде норм.
PHP:
$parameter['if condition'] = preg_replace('/(?<!\()(\$)/i', '@\1', $parameter['if condition']);

Вот что получается при компиляции.

до:
PHP:
Smarty_Internal_Compile_If::compilearray ( 'if condition' => '$_smarty_tpl->tpl_vars[\'unknownvariable\']->value', )
после:
PHP:
Smarty_Internal_Compile_If::compilearray ( 'if condition' => '@$_smarty_tpl->tpl_vars[\'unknownvariable\']->value', )

до:

PHP:
Smarty_Internal_Compile_If::compilearray ( 'if condition' => 'isset($_smarty_tpl->tpl_vars[\'canonical\']->value)', )
после:
PHP:
Smarty_Internal_Compile_If::compilearray ( 'if condition' => 'isset($_smarty_tpl->tpl_vars[\'canonical\']->value)', )

К сожалению так тоже не пойдет, так как не охвачены следующие варианты:

if isset( $a) - пробел мешает
if isset( $a,$b) - у переменной $b будет замена


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


Вот такой блок работает, но может есть что-то что будет работать криво?
PHP:
        //avoid 'undefined variable' notice error
      
        preg_match_all('/([^\(\)]*)(\(.*?\))([^\(]*)/i', $parameter['if condition'], $m);
        dtimer::log(__METHOD__ . var_export($m,true));
        $s = ''; //result string
      
        if(!empty($m[0])){
            foreach($m[2] as $k=>$keep){
                $s .= str_replace('$', '@$', $m[1][$k]);
                $s .= $keep;
                $s .= str_replace('$', '@$', $m[3][$k]);
            }
        } else {
            $s = str_replace('$', '@$', $parameter['if condition']);
        }
      
        $parameter['if condition'] = $s;
 
        //avoid 'undefined variable' notice error END
 
Последнее редактирование:
А что младшие версии иначе как-то компилируют if?
Только что проверил на Smarty 2.1 (да, довольно древняя версия, но что было под рукой) :
PHP:
{if ($some_random_name)}this is a test{/if}
Нет никаких предупреждений.
 
Только что проверил на Smarty 2.1 (да, довольно древняя версия, но что было под рукой) :
PHP:
{if ($some_random_name)}this is a test{/if}
Нет никаких предупреждений.

А в компиляторе как это сделано? Неужели он сам ставит @? Скорее всего подавление E_NOTICE включается самим смарти уже.
 
Вроде бы рабочий вариант, который я сделал. Сомнения только со сложными условиями, где есть вложенность.
 
А в компиляторе как это сделано? Неужели он сам ставит @? Скорее всего подавление E_NOTICE включается самим смарти уже.
ну, остальные предупреждения вылазят...это на счёт подавления E_NOTICE.

как в компиляторе это устроено особого желания ковыряться нет) с текущими задачами бы разобраться %)
в принципе ничего не мешает скачать эту версию и глянуть, если есть большое желание разобраться :)
 
Назад
Сверху