diff --git a/src/AbstractTwigCallable.php b/src/AbstractTwigCallable.php index f5739529b..756425a99 100644 --- a/src/AbstractTwigCallable.php +++ b/src/AbstractTwigCallable.php @@ -39,6 +39,11 @@ abstract class AbstractTwigCallable implements TwigCallableInterface ], $options); } + public function __toString(): string + { + return sprintf('%s(%s)', static::class, $this->name); + } + public function getName(): string { return $this->name; diff --git a/src/Node/Node.php b/src/Node/Node.php index 5ef661f5e..a9bd84a3e 100644 --- a/src/Node/Node.php +++ b/src/Node/Node.php @@ -15,6 +15,7 @@ namespace Twig\Node; use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Source; +use Twig\TwigCallableInterface; /** * Represents a node in the AST. @@ -58,7 +59,14 @@ class Node implements \Countable, \IteratorAggregate { $attributes = []; foreach ($this->attributes as $name => $value) { - $attributes[] = \sprintf('%s: %s', $name, \is_callable($value) ? '\Closure' : str_replace("\n", '', var_export($value, true))); + if (\is_callable($value)) { + $v = '\Closure'; + } elseif ($value instanceof \Stringable) { + $v = (string) $value; + } else { + $v = str_replace("\n", '', var_export($value, true)); + } + $attributes[] = \sprintf('%s: %s', $name, $v); } $repr = [static::class.'('.implode(', ', $attributes)]; diff --git a/src/TwigCallableInterface.php b/src/TwigCallableInterface.php index 7e5021242..7706eb4f2 100644 --- a/src/TwigCallableInterface.php +++ b/src/TwigCallableInterface.php @@ -14,7 +14,7 @@ namespace Twig; /** * @author Fabien Potencier */ -interface TwigCallableInterface +interface TwigCallableInterface extends \Stringable { public function getName(): string; diff --git a/tests/Node/NodeTest.php b/tests/Node/NodeTest.php index b4f8eee02..b224c3636 100644 --- a/tests/Node/NodeTest.php +++ b/tests/Node/NodeTest.php @@ -15,6 +15,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Twig\Node\NameDeprecation; use Twig\Node\Node; +use Twig\TwigFilter; +use Twig\TwigFunction; +use Twig\TwigTest; class NodeTest extends TestCase { @@ -28,6 +31,17 @@ class NodeTest extends TestCase $this->assertEquals('Twig\Node\Node(value: \Closure)', (string) $node); } + public function testToStringWithTwigCallables() + { + $node = new Node([], [ + 'function' => new TwigFunction('a_function'), + 'filter' => new TwigFilter('a_filter'), + 'test' => new TwigTest('a_test'), + ], 1); + + $this->assertEquals('Twig\Node\Node(function: Twig\TwigFunction(a_function), filter: Twig\TwigFilter(a_filter), test: Twig\TwigTest(a_test))', (string) $node); + } + public function testAttributeDeprecationIgnore() { $node = new Node([], ['foo' => false]);