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

Тема в разделе "PHP", создана пользователем babahalki, 19 дек 2017.

Метки:
Модераторы: latteo
  1. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Всем привет. Установил последнюю версию Smarty https://github.com/smarty-php/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($argsSmarty_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.

    Может кто знает способ получше?
     
  2. Nei

    Nei Nosce te ipsum

    Регистр.:
    5 сен 2009
    Сообщения:
    670
    Симпатии:
    522
    PHP:
    {if isset($keyword)}
    Не вариант использовать?
     
  3. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Так и приходится использовать. Но было бы гораздо удобнее, чтобы if $keyword становилось if isset($keyword) && $keyword
     
  4. Nei

    Nei Nosce te ipsum

    Регистр.:
    5 сен 2009
    Сообщения:
    670
    Симпатии:
    522
    Тогда как вариант:
    включить "error_reporting = E_ALL"
    ... почистить всё, что не связано с "undefined variable" ...
    выключить "error_reporting = E_ALL"

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

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    А что младшие версии иначе как-то компилируют 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($argsSmarty_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'nulltrue);
            }

            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($argsSmarty_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($argsSmarty_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'nulltrue);
            }

            
    $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($argsSmarty_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']
     
    Последнее редактирование: 19 дек 2017
  6. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    может как-то так:
    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
     
    Последнее редактирование: 19 дек 2017
  7. Nei

    Nei Nosce te ipsum

    Регистр.:
    5 сен 2009
    Сообщения:
    670
    Симпатии:
    522
    Только что проверил на Smarty 2.1 (да, довольно древняя версия, но что было под рукой) :
    PHP:
    {if ($some_random_name)}this is a test{/if}
    Нет никаких предупреждений.
     
  8. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    А в компиляторе как это сделано? Неужели он сам ставит @? Скорее всего подавление E_NOTICE включается самим смарти уже.
     
  9. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Вроде бы рабочий вариант, который я сделал. Сомнения только со сложными условиями, где есть вложенность.
     
  10. Nei

    Nei Nosce te ipsum

    Регистр.:
    5 сен 2009
    Сообщения:
    670
    Симпатии:
    522
    ну, остальные предупреждения вылазят...это на счёт подавления E_NOTICE.

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