twigphp / twigphp/Twig

Macros defined in parent are considered as not defined

Open
#3,174 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
8.4k
Forks
1.3k
Avg merge
1d 13h
Merged PRs (30d)
37

Description

When a macro is defined in a parent template, it is considered as not defined even though it is available in the template:

{% extends "parent.twig" %}

{% block f %}
    {% import _self as s %}

    {{ s.bar() }} // works

    {% if (s.bar is defined) %} // returns false
        TRUE
    {% else %}
        FALSE
    {% endif %}
{% endblock %}
  • parent.twig
{% block f %}
{% endblock %}

{% macro bar() %}
BAR
{% endmacro %}

https://twigfiddle.com/ckttfv

This is due to the fact that when calling a macro, parent macros are also considered:

/**
 * @internal
 */
function twig_call_macro(Template $template, string $method, array $args, int $lineno, array $context, Source $source)
{
    if (!method_exists($template, $method)) {
        $parent = $template;
        while ($parent = $parent->getParent($context)) {
            if (method_exists($parent, $method)) {
                return $parent->$method(...$args);
            }
        }

        throw new RuntimeError(sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source);
    }

    return $template->$method(...$args);
}

But when a macro is tested for existence (via the is defined test), parent macros are not considered:

if ($this->getAttribute('is_defined_test')) {
            $compiler
                ->raw('method_exists($macros[')
                ->repr($this->getNode('node')->getAttribute('name'))
                ->raw('], ')
                ->repr($this->getAttribute('method'))
                ->raw(')')
            ;

            return;
        }

It looks inconsistent to me. The purpose of the defined test is to ensure that the template can safely call a macro. But it is actually impossible to know if a macro is defined before using it - macros coming from parents will always be considered as not defined even though they are perfectly usable.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the parent.twig and child template example, then inspect the twig_call_macro runtime path and the compiler branch handling is_defined_test. Add a regression test covering a macro inherited from a parent and verify that the defined test agrees with the existing callable behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.