Как я могу получить объект _self в ветке 2 - С помощью строки атрибут (_self, function) всегда завершается ошибкой после обновления - PullRequest
0 голосов
/ 20 февраля 2020

Я обновлю нашу ветку до 2.11. В настоящее время мы используем следующий код:

{% set neededFunction = 'someting_dynamic' %}
{% import _self as functions %}
{% if attribute(functions, neededFunction ) is defined %}
    {# do something #}
{% else %}
    {# do something else #}
{% endif %}

, но после обновления «attribute (functions, requiredFunction)» всегда терпит неудачу. Я думаю, потому что _self сейчас не объект. Но что я могу сделать сейчас, чтобы проверить, существует ли динамическая c именованная функция?

update:

Я попробовал следующее:

{% set neededFunction = 'someting_dynamic' %}
{% import 'myTemplate' as functions %} 

{% if attribute(functions, neededFunction ) is defined %} 
    {# do something #}
{% endif %} 

{% if functions.neededFunction is defined %}
    {# do something #}
{% endif %}

{% if functions.someting_dynamic is defined %}
    {# do something #}
{% endif %} 

только последняя работает. Но это не динамично c

обновление: Попробуйте решить проблему с подходом DarkBee.

Теперь есть одна последняя проблема:

Перед тем, как скомпилировать макрос, я хочу проверить, существует ли макрос. Если нет, я хочу вызвать макрос по умолчанию. Но у меня проблема в том, что функция $ не скомпилирована. Могу ли я получить значение скомпилированного содержимого из парсера?

Мой TokenParser:

class DynamicMacro extends \Twig\TokenParser\AbstractTokenParser
{
    const MAX_MACROS = 2;

    /**
     * @var Node
     */
    private $firstAvailableNode;

    /**
     * {@inheritDoc}
     */
    public function getTag()
    {
        return 'dynamic_macro';
    }

    /**
     * {@inheritDoc}
     */
    public function parse(Token $token)
    {
        $stream = $this->parser->getStream();

        $name = $stream->expect(Token::NAME_TYPE)->getValue();

        for ($i = 1; $i <= self::MAX_MACROS; $i++) {
            if (!$stream->test(Token::BLOCK_END_TYPE)) {
                $this->checkMacro( $token, $name );
            }
        }

        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return $this->getFirstAvailableNode();
    }

    private function checkMacro( Token $token, string $name )
    {
        $stream = $this->parser->getStream();

        $stream->expect(\Twig_Token::PUNCTUATION_TYPE);
        $function = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();

        $arguments = $this->parser->getExpressionParser()->parseArguments(true);

        /** @todo: $function -> 'fx' has to be compiled  */
        if (is_null($this->firstAvailableNode) && $this->parser->hasMacro($function)){
            $node = new DynamicMacroNode($name, $function, $arguments, $token->getLine(), $this->getTag());
            $this->setFirstAvailableNode($node);
        }
    }

    /**
     * @return Node
     */
    public function getFirstAvailableNode(): Node
    {
        if(is_null($this->firstAvailableNode))
            return new EmptyNode();

        return $this->firstAvailableNode;
    }

    /**
     * @param Node $node
     */
    public function setFirstAvailableNode(Node $node)
    {
        if (is_null($this->firstAvailableNode))
            $this->firstAvailableNode = $node;
    }


}

Отрезанная ветка:

{% import _self as foo_macro %}
{% set fx = 'foo_function' %}
{% set fxdef = 'foo_function_default' %}

{% dynamic_macro foo_macro.fx('bar', 'foo', 42, fx).fxdef('bar', 'foo', 42, fx) %}

{% macro foo_function_default(bar, foo, int, function) %}
   Bar: {{ bar }}<br />
   Foo: {{ foo }}<br />
   Int: {{ int }}<br />
   Fx: {{ function }}<br />
{% endmacro %}

1 Ответ

0 голосов
/ 25 февраля 2020

Вы можете создать свой собственный тег в twig, который решает эту проблему

Зарегистрировать пользовательский TokenParser внутри вашего расширения:

class MyTwigExtension implements \Twig\Extension\ExtensionInterface {
    public function getTokenParsers() {
        return [
            new DynamicMacro(),
        ];
    }
}

Создать TokenParser

class DynamicMacro extends \Twig\TokenParser\AbstractTokenParser {
    /**
     * {@inheritDoc}
     */
    public function getTag()
    {
        return 'dynamic_macro';
    }
    /**
     * {@inheritDoc}
     */
    public function parse(\Twig\Token $token)
    {
        $parser = $this->parser;
        $stream = $parser->getStream();

        $name = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();
        $stream->expect(\Twig_Token::PUNCTUATION_TYPE);
        $function = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();

        $arguments = $this->parser->getExpressionParser()->parseArguments(true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new DynamicMacroNode($name, $function, $arguments, $token->getLine(), $this->getTag());
    }
}

Создайте Node

    class DynamicMacroNode extends \Twig\Node\Node {
        public function __construct($name, $function, $arguments, $lineno, $tag = null) {
            $attributes = [
                'name'      => $name,
                'function'  => $function,
                'lineno'    => $lineno,
            ];
            parent::__construct([ 'arguments' => $arguments, ], $attributes, $lineno, $tag);
        }

        public function compile(\Twig_Compiler $compiler) {
            $compiler->write('$_dynamicMacro_parms = [];')
                     ->raw("\n");
            foreach($this->getNode('arguments') as $argument) {
                $compiler->write('$_dynamicMacro_parms[] = ')
                         ->subcompile($argument)
                         ->raw(";\n");
            }
            $compiler->write('echo twig_call_macro($macros[\''.$this->getAttribute('name').'\'], \'macro_\'.$context[\''.$this->getAttribute('function').'\'] ?? null, $_dynamicMacro_parms,'.$this->getAttribute('lineno').', $context, $this->getSourceContext());');
        }
    }

Теперь вы можете использовать тег dynamic_macro внутри twig

        {% import _self as foo_macro %}
        {% set fx = 'foo_function' %}

        {% dynamic_macro foo_macro.fx('bar', 'foo', 42, fx) %}

        {% macro foo_function(bar, foo, int, function) %}
            Bar: {{ bar }}<br />
            Foo: {{ foo }}<br />
            Int: {{ int }}<br />
            Fx: {{ function }}<br />
        {% endmacro %}

пожалуйста, не это только приблизительная версия о том, как вы можете решить эту проблему и должна быть расширена с проверкой данных и таких

...