Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Легкой плагин для Twig либо разворачиваем константы

Anna | 31.05.2014 | нет комментариев

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

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

Сама задача — в константах внутри образцов. Бывают такие задачи, когда в образце нужно зашиться на какие-нибудь идентификаторы. Цифрами расставлять их — не вовсе отлично, а если для них еще и существуют константы — проступок не воспользоваться функцией constant. Но дело в том, что позже компиляции из образца она все равно вычисляется в рантайме.

И что же у нас может получиться? Мы на волне рефакторинга убиваем либо переименовываем константу, а о образце забываем. И IDE забывает, даже хваленый PHPStorm. Удачно компилируем перед деплоем всю нашу гору образцов, раскидываем на сервера. Ничего не упало, легко работает все не дюже, а на нашу голову сваливается большая простыня идентичных ворнингову. Нехорошо? Возмутительно!

Решение? Резолвить константы в процессе компиляции образца, на отсутствующие — браниться.

Тем, кто не знаком с Twig либо знаком не дюже отлично, расскажем (дюже коротко) что всякий образец парсится плагинами (даже базовые вероятности реализованы в шаблонизаторе с поддержкой плагинов), обрабатывается и компилируется в php-класс, у которого потом дергается способ display. Для примера возьмем такой код образца, как раз с нашей константой:

{% if usertype == constant('Users::TYPE_TROLL') %}
	Давай, до свидания!
{% else %}
	Привет!
{% endif %}

Образец разберется в касательно огромное дерево объектов.

Тут немножко укороченный, но все равно огромный итог print_r представления нашего образца

[body] => Twig_Node_Body Object (
	[nodes:protected] => Array (
		[0] => Twig_Node_If Object (
			[nodes:protected] => Array (
				[tests] => Twig_Node Object (
					[nodes:protected] => Array (
						[0] => Twig_Node_Expression_Binary_Equal Object (
							[nodes:protected] => Array (
								[left] => Twig_Node_Expression_Name Object (
									[attributes:protected] => Array (
										[name] => usertype
									)
								)
								[right] => Twig_Node_Expression_Function Object (
									[nodes:protected] => Array (
										[arguments] => Twig_Node Object (
											[nodes:protected] => Array (
												[0] => Twig_Node_Expression_Constant Object (
													[attributes:protected] => Array (
														[value] => Users::TYPE_TROLL
													)
												)
											)
										)
									)
									[attributes:protected] => Array (
										[name] => constant
									)
								)
							)
						)
						[1] => Twig_Node_Text Object (
							[attributes:protected] => Array (
								[data] => Давай, до свидания!
							)
						)
					)
				)
				[else] => Twig_Node_Text Object (
					[attributes:protected] => Array (
						[data] => Здравствуй!
					)
				)
			)
		)
	)
)

Оно добавочно обрабатывается [сюда нам необходимо вклиниться] и в выводе компилируется вот в такой файл (тоже слегка укороченный вариант):

class __TwigTemplate_long_long_hash extends Twig_Template {

    protected function doDisplay(array $context, array $blocks = array()) {
        if (((isset($context["usertype"]) ? $context["usertype"] : null) == twig_constant("Users::TYPE_TROLL"))) {
            echo "Давай, до свидания!";
        } else {
            echo "Здравствуй!";
        }
    }

}

$context тут — то, что попало в кучу переменных на вход этому образцу. Верю, все ясно и ничего пояснять не нужно. Функция twig_constant фактически не отличается от стандартной constant и резолвится в рантайме.

Дабы посмотреть на загвоздку своими глазами убираем константу из кода и на рендере ловим:
PHP Warning: constant(): Couldn't find constant Users::TYPE_TROLL in vendor/twig/twig/lib/Twig/Extension/Core.php on line 1387

Именно вызов twig_constant в компилированном варианте нам необходимо заменить на значение константы.

Для растяжений в шаблонизаторе предусмотрен класс Twig_Extension, от которого мы и наследуем наше растяжение. Растяжение может предоставлять шаблонизатору комплекты функций, фильтров и прочей ерунды, какой только дозволено придумать, через особые способы, которые вы можете сами обнаружить в интерфейсе Twig_ExtensionInterface. Нас волнует способ getNodeVisitors, тот, что возвращает массив объектов, через которых будут пропущены все элементы распарсенного дерева образца перед его компиляцией.

class Template_Extensions_ConstEvaluator extends Twig_Extension {

    public function getNodeVisitors() {
        return [
            new Template_Extensions_NodeVisitor_ConstEvaluator()
        ];
    }

    public function getName() {
        return 'const_evaluator';
    }

}

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

Вот таким наш node visitor и получается:

class Template_Extensions_NodeVisitor_ConstEvaluator implements Twig_NodeVisitorInterface {

    public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
    {
        // ищем ноду-функцию с наименованием constant и 1 доводом
        if ($node instanceof Twig_Node_Expression_Function
            && 'constant' === $node->getAttribute('name')
            && 1 === $node->count()
        ) {
            // получаем доводы функции
            $args = $node->getNode('arguments');

            if ($args instanceof Twig_Node
                && 1 === $args->count()
            ) {
                $constNode = $args->getNode(0);

                // 1 текстовый довод
                if ($constNode instanceof Twig_Node_Expression_Constant
                    && null !== $value = $constNode->getAttribute('value')
                ) {
                    if (null === $constantEvaluated = constant($value)) {
                        // не можем обнаружить константу - бранимся
                        throw new Twig_Error(
                            sprintf(
                                "Can't evaluate constant('%s')",
                                $value
                            )
                        );
                    }

                    // все нашлось, возвращаем взамен функции ноду со значением константы
                    // не введитесь в заблуждение наименованием класса :]
                    return new Twig_Node_Expression_Constant($constantEvaluated, $node->getLine());
                }
            }
        }

        // все ок, возвращаем то, что получили, в целости и сохранности
        return $node;
    }

    public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) {
        return $node;
    }

}

Вот так мы и заменили чуть ли не треть нашего дерева образца обыкновенным значением.

На каждый случай покажу, что же получилось в компилированном варианте

<source lang="php">class __TwigTemplate_long_long_hash extends Twig_Template {

    protected function doDisplay(array $context, array $blocks = array()) {
        if (((isset($context["usertype"]) ? $context["usertype"] : null) == 2)) {
            echo "Давай, до свидания!";
        } else {
            echo "Здравствуй!";
        }
    }

}

Легко, увлекательно, благотворно.

Верю, кого-то это подтолкнет покопаться в Twig и попытаться расширить его чем-то помимо функций, да фильтров. Готов ответить на всякие вопросы и выслушать всякую критику.

Источник: programmingmaster.ru
Оставить комментарий
БАЗА ЗНАНИЙ
СЛУЧАЙНАЯ СТАТЬЯ
СЛУЧАЙНЫЙ БЛОГ
СЛУЧАЙНЫЙ МОД
СЛУЧАЙНЫЙ СКИН
НОВЫЕ МОДЫ
НОВЫЕ СКИНЫ
НАКОПЛЕННЫЙ ОПЫТ
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB