html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

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

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

}

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

На всякий случай покажу, что же получилось в компилированном варианте
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 и попытаться расширить его чем-то кроме функций, да фильтров. Готов выслушать любую критику, а утром — даже ответить на вопросы. Дискас!
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

5

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции