1<?php 2 3/* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * (c) Armin Ronacher 8 * 9 * For the full copyright and license information, please view the LICENSE 10 * file that was distributed with this source code. 11 */ 12 13namespace Twig\Node; 14 15use Twig\Compiler; 16use Twig\Source; 17 18/** 19 * Represents a node in the AST. 20 * 21 * @author Fabien Potencier <fabien@symfony.com> 22 */ 23class Node implements \Countable, \IteratorAggregate 24{ 25 protected $nodes; 26 protected $attributes; 27 protected $lineno; 28 protected $tag; 29 30 private $name; 31 private $sourceContext; 32 33 /** 34 * @param array $nodes An array of named nodes 35 * @param array $attributes An array of attributes (should not be nodes) 36 * @param int $lineno The line number 37 * @param string $tag The tag name associated with the Node 38 */ 39 public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0, string $tag = null) 40 { 41 foreach ($nodes as $name => $node) { 42 if (!$node instanceof self) { 43 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.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, static::class)); 44 } 45 } 46 $this->nodes = $nodes; 47 $this->attributes = $attributes; 48 $this->lineno = $lineno; 49 $this->tag = $tag; 50 } 51 52 public function __toString() 53 { 54 $attributes = []; 55 foreach ($this->attributes as $name => $value) { 56 $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); 57 } 58 59 $repr = [static::class.'('.implode(', ', $attributes)]; 60 61 if (\count($this->nodes)) { 62 foreach ($this->nodes as $name => $node) { 63 $len = \strlen($name) + 4; 64 $noderepr = []; 65 foreach (explode("\n", (string) $node) as $line) { 66 $noderepr[] = str_repeat(' ', $len).$line; 67 } 68 69 $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr))); 70 } 71 72 $repr[] = ')'; 73 } else { 74 $repr[0] .= ')'; 75 } 76 77 return implode("\n", $repr); 78 } 79 80 public function compile(Compiler $compiler) 81 { 82 foreach ($this->nodes as $node) { 83 $node->compile($compiler); 84 } 85 } 86 87 public function getTemplateLine() 88 { 89 return $this->lineno; 90 } 91 92 public function getNodeTag() 93 { 94 return $this->tag; 95 } 96 97 /** 98 * @return bool 99 */ 100 public function hasAttribute($name) 101 { 102 return \array_key_exists($name, $this->attributes); 103 } 104 105 /** 106 * @return mixed 107 */ 108 public function getAttribute($name) 109 { 110 if (!\array_key_exists($name, $this->attributes)) { 111 throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, static::class)); 112 } 113 114 return $this->attributes[$name]; 115 } 116 117 /** 118 * @param string $name 119 * @param mixed $value 120 */ 121 public function setAttribute($name, $value) 122 { 123 $this->attributes[$name] = $value; 124 } 125 126 public function removeAttribute($name) 127 { 128 unset($this->attributes[$name]); 129 } 130 131 /** 132 * @return bool 133 */ 134 public function hasNode($name) 135 { 136 return isset($this->nodes[$name]); 137 } 138 139 /** 140 * @return Node 141 */ 142 public function getNode($name) 143 { 144 if (!isset($this->nodes[$name])) { 145 throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, static::class)); 146 } 147 148 return $this->nodes[$name]; 149 } 150 151 public function setNode($name, self $node) 152 { 153 $this->nodes[$name] = $node; 154 } 155 156 public function removeNode($name) 157 { 158 unset($this->nodes[$name]); 159 } 160 161 /** 162 * @return int 163 */ 164 #[\ReturnTypeWillChange] 165 public function count() 166 { 167 return \count($this->nodes); 168 } 169 170 /** 171 * @return \Traversable 172 */ 173 #[\ReturnTypeWillChange] 174 public function getIterator() 175 { 176 return new \ArrayIterator($this->nodes); 177 } 178 179 /** 180 * @deprecated since 2.8 (to be removed in 3.0) 181 */ 182 public function setTemplateName($name/*, $triggerDeprecation = true */) 183 { 184 $triggerDeprecation = 2 > \func_num_args() || \func_get_arg(1); 185 if ($triggerDeprecation) { 186 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use setSourceContext() instead.', \E_USER_DEPRECATED); 187 } 188 189 $this->name = $name; 190 foreach ($this->nodes as $node) { 191 $node->setTemplateName($name, $triggerDeprecation); 192 } 193 } 194 195 public function getTemplateName() 196 { 197 return $this->sourceContext ? $this->sourceContext->getName() : null; 198 } 199 200 public function setSourceContext(Source $source) 201 { 202 $this->sourceContext = $source; 203 foreach ($this->nodes as $node) { 204 $node->setSourceContext($source); 205 } 206 207 $this->setTemplateName($source->getName(), false); 208 } 209 210 public function getSourceContext() 211 { 212 return $this->sourceContext; 213 } 214} 215 216class_alias('Twig\Node\Node', 'Twig_Node'); 217 218// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. 219class_exists('Twig\Compiler'); 220