2024-06-20 14:10:42 +00:00
< ? php
/*
* This file is part of Twig .
*
* ( c ) Fabien Potencier
* ( c ) Armin Ronacher
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Twig\Node ;
use Twig\Attribute\YieldReady ;
use Twig\Compiler ;
use Twig\Source ;
/**
* Represents a node in the AST .
*
* @ author Fabien Potencier < fabien @ symfony . com >
2025-01-13 09:56:01 +00:00
*
* @ implements \IteratorAggregate < int | string , Node >
2024-06-20 14:10:42 +00:00
*/
#[YieldReady]
class Node implements \Countable , \IteratorAggregate
{
2025-01-13 09:56:01 +00:00
/**
* @ var array < string | int , Node >
*/
2024-06-20 14:10:42 +00:00
protected $nodes ;
protected $attributes ;
protected $lineno ;
protected $tag ;
private $sourceContext ;
2024-09-05 17:51:48 +00:00
/** @var array<string, NameDeprecation> */
private $nodeNameDeprecations = [];
/** @var array<string, NameDeprecation> */
private $attributeNameDeprecations = [];
2024-06-20 14:10:42 +00:00
/**
2025-01-13 09:56:01 +00:00
* @ param array < string | int , Node > $nodes An array of named nodes
* @ param array $attributes An array of attributes ( should not be nodes )
* @ param int $lineno The line number
2024-06-20 14:10:42 +00:00
*/
2025-01-13 09:56:01 +00:00
public function __construct ( array $nodes = [], array $attributes = [], int $lineno = 0 )
2024-06-20 14:10:42 +00:00
{
2025-01-13 09:56:01 +00:00
if ( self :: class === static :: class ) {
trigger_deprecation ( 'twig/twig' , '3.15' , \sprintf ( 'Instantiating "%s" directly is deprecated; the class will become abstract in 4.0.' , self :: class ));
}
2024-06-20 14:10:42 +00:00
foreach ( $nodes as $name => $node ) {
if ( ! $node instanceof self ) {
2025-01-13 09:56:01 +00:00
throw new \InvalidArgumentException ( \sprintf ( 'Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.' , get_debug_type ( $node ), $name , static :: class ));
2024-06-20 14:10:42 +00:00
}
}
$this -> nodes = $nodes ;
$this -> attributes = $attributes ;
$this -> lineno = $lineno ;
2025-01-13 09:56:01 +00:00
if ( \func_num_args () > 3 ) {
trigger_deprecation ( 'twig/twig' , '3.12' , \sprintf ( 'The "tag" constructor argument of the "%s" class is deprecated and ignored (check which TokenParser class set it to "%s"), the tag is now automatically set by the Parser when needed.' , static :: class , func_get_arg ( 3 ) ? : 'null' ));
}
2024-06-20 14:10:42 +00:00
}
public function __toString ()
{
2025-01-13 09:56:01 +00:00
$repr = static :: class ;
if ( $this -> tag ) {
$repr .= \sprintf ( " \n tag: %s " , $this -> tag );
}
2024-06-20 14:10:42 +00:00
$attributes = [];
foreach ( $this -> attributes as $name => $value ) {
2025-01-13 09:56:01 +00:00
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 );
2024-06-20 14:10:42 +00:00
}
2025-01-13 09:56:01 +00:00
if ( $attributes ) {
$repr .= \sprintf ( " \n attributes: \n %s " , implode ( " \n " , $attributes ));
}
2024-06-20 14:10:42 +00:00
if ( \count ( $this -> nodes )) {
2025-01-13 09:56:01 +00:00
$repr .= " \n nodes: " ;
2024-06-20 14:10:42 +00:00
foreach ( $this -> nodes as $name => $node ) {
2025-01-13 09:56:01 +00:00
$len = \strlen ( $name ) + 6 ;
2024-06-20 14:10:42 +00:00
$noderepr = [];
foreach ( explode ( " \n " , ( string ) $node ) as $line ) {
$noderepr [] = str_repeat ( ' ' , $len ) . $line ;
}
2025-01-13 09:56:01 +00:00
$repr .= \sprintf ( " \n %s: %s " , $name , ltrim ( implode ( " \n " , $noderepr )));
2024-06-20 14:10:42 +00:00
}
}
2025-01-13 09:56:01 +00:00
return $repr ;
2024-06-20 14:10:42 +00:00
}
/**
* @ return void
*/
public function compile ( Compiler $compiler )
{
foreach ( $this -> nodes as $node ) {
$compiler -> subcompile ( $node );
}
}
public function getTemplateLine () : int
{
return $this -> lineno ;
}
public function getNodeTag () : ? string
{
return $this -> tag ;
}
2025-01-13 09:56:01 +00:00
/**
* @ internal
*/
public function setNodeTag ( string $tag ) : void
{
if ( $this -> tag ) {
throw new \LogicException ( 'The tag of a node can only be set once.' );
}
$this -> tag = $tag ;
}
2024-06-20 14:10:42 +00:00
public function hasAttribute ( string $name ) : bool
{
return \array_key_exists ( $name , $this -> attributes );
}
public function getAttribute ( string $name )
{
if ( ! \array_key_exists ( $name , $this -> attributes )) {
2024-09-05 17:51:48 +00:00
throw new \LogicException ( \sprintf ( 'Attribute "%s" does not exist for Node "%s".' , $name , static :: class ));
}
$triggerDeprecation = \func_num_args () > 1 ? func_get_arg ( 1 ) : true ;
if ( $triggerDeprecation && isset ( $this -> attributeNameDeprecations [ $name ])) {
$dep = $this -> attributeNameDeprecations [ $name ];
if ( $dep -> getNewName ()) {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Getting attribute "%s" on a "%s" class is deprecated, get the "%s" attribute instead.' , $name , static :: class , $dep -> getNewName ());
} else {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Getting attribute "%s" on a "%s" class is deprecated.' , $name , static :: class );
}
2024-06-20 14:10:42 +00:00
}
return $this -> attributes [ $name ];
}
public function setAttribute ( string $name , $value ) : void
{
2024-09-05 17:51:48 +00:00
$triggerDeprecation = \func_num_args () > 2 ? func_get_arg ( 2 ) : true ;
if ( $triggerDeprecation && isset ( $this -> attributeNameDeprecations [ $name ])) {
$dep = $this -> attributeNameDeprecations [ $name ];
if ( $dep -> getNewName ()) {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Setting attribute "%s" on a "%s" class is deprecated, set the "%s" attribute instead.' , $name , static :: class , $dep -> getNewName ());
} else {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Setting attribute "%s" on a "%s" class is deprecated.' , $name , static :: class );
}
}
2024-06-20 14:10:42 +00:00
$this -> attributes [ $name ] = $value ;
}
2024-09-05 17:51:48 +00:00
public function deprecateAttribute ( string $name , NameDeprecation $dep ) : void
{
$this -> attributeNameDeprecations [ $name ] = $dep ;
}
2024-06-20 14:10:42 +00:00
public function removeAttribute ( string $name ) : void
{
unset ( $this -> attributes [ $name ]);
}
2025-01-13 09:56:01 +00:00
/**
* @ param string | int $name
*/
2024-06-20 14:10:42 +00:00
public function hasNode ( string $name ) : bool
{
return isset ( $this -> nodes [ $name ]);
}
2025-01-13 09:56:01 +00:00
/**
* @ param string | int $name
*/
2024-06-20 14:10:42 +00:00
public function getNode ( string $name ) : self
{
if ( ! isset ( $this -> nodes [ $name ])) {
2024-09-05 17:51:48 +00:00
throw new \LogicException ( \sprintf ( 'Node "%s" does not exist for Node "%s".' , $name , static :: class ));
}
$triggerDeprecation = \func_num_args () > 1 ? func_get_arg ( 1 ) : true ;
if ( $triggerDeprecation && isset ( $this -> nodeNameDeprecations [ $name ])) {
$dep = $this -> nodeNameDeprecations [ $name ];
if ( $dep -> getNewName ()) {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Getting node "%s" on a "%s" class is deprecated, get the "%s" node instead.' , $name , static :: class , $dep -> getNewName ());
} else {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Getting node "%s" on a "%s" class is deprecated.' , $name , static :: class );
}
2024-06-20 14:10:42 +00:00
}
return $this -> nodes [ $name ];
}
2025-01-13 09:56:01 +00:00
/**
* @ param string | int $name
*/
2024-06-20 14:10:42 +00:00
public function setNode ( string $name , self $node ) : void
{
2024-09-05 17:51:48 +00:00
$triggerDeprecation = \func_num_args () > 2 ? func_get_arg ( 2 ) : true ;
if ( $triggerDeprecation && isset ( $this -> nodeNameDeprecations [ $name ])) {
$dep = $this -> nodeNameDeprecations [ $name ];
if ( $dep -> getNewName ()) {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Setting node "%s" on a "%s" class is deprecated, set the "%s" node instead.' , $name , static :: class , $dep -> getNewName ());
} else {
trigger_deprecation ( $dep -> getPackage (), $dep -> getVersion (), 'Setting node "%s" on a "%s" class is deprecated.' , $name , static :: class );
}
}
2024-06-20 14:10:42 +00:00
if ( null !== $this -> sourceContext ) {
$node -> setSourceContext ( $this -> sourceContext );
}
$this -> nodes [ $name ] = $node ;
}
2025-01-13 09:56:01 +00:00
/**
* @ param string | int $name
*/
2024-06-20 14:10:42 +00:00
public function removeNode ( string $name ) : void
{
unset ( $this -> nodes [ $name ]);
}
2025-01-13 09:56:01 +00:00
/**
* @ param string | int $name
*/
2024-09-05 17:51:48 +00:00
public function deprecateNode ( string $name , NameDeprecation $dep ) : void
{
$this -> nodeNameDeprecations [ $name ] = $dep ;
}
2024-06-20 14:10:42 +00:00
/**
* @ return int
*/
#[\ReturnTypeWillChange]
public function count ()
{
return \count ( $this -> nodes );
}
public function getIterator () : \Traversable
{
return new \ArrayIterator ( $this -> nodes );
}
public function getTemplateName () : ? string
{
return $this -> sourceContext ? $this -> sourceContext -> getName () : null ;
}
public function setSourceContext ( Source $source ) : void
{
$this -> sourceContext = $source ;
foreach ( $this -> nodes as $node ) {
$node -> setSourceContext ( $source );
}
}
public function getSourceContext () : ? Source
{
return $this -> sourceContext ;
}
}