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 /** 81 * @return void 82 */ 83 public function compile(Compiler $compiler) 84 { 85 foreach ($this->nodes as $node) { 86 $node->compile($compiler); 87 } 88 } 89 90 public function getTemplateLine(): int 91 { 92 return $this->lineno; 93 } 94 95 public function getNodeTag(): ?string 96 { 97 return $this->tag; 98 } 99 100 public function hasAttribute(string $name): bool 101 { 102 return \array_key_exists($name, $this->attributes); 103 } 104 105 public function getAttribute(string $name) 106 { 107 if (!\array_key_exists($name, $this->attributes)) { 108 throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, static::class)); 109 } 110 111 return $this->attributes[$name]; 112 } 113 114 public function setAttribute(string $name, $value): void 115 { 116 $this->attributes[$name] = $value; 117 } 118 119 public function removeAttribute(string $name): void 120 { 121 unset($this->attributes[$name]); 122 } 123 124 public function hasNode(string $name): bool 125 { 126 return isset($this->nodes[$name]); 127 } 128 129 public function getNode(string $name): self 130 { 131 if (!isset($this->nodes[$name])) { 132 throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, static::class)); 133 } 134 135 return $this->nodes[$name]; 136 } 137 138 public function setNode(string $name, self $node): void 139 { 140 $this->nodes[$name] = $node; 141 } 142 143 public function removeNode(string $name): void 144 { 145 unset($this->nodes[$name]); 146 } 147 148 /** 149 * @return int 150 */ 151 #[\ReturnTypeWillChange] 152 public function count() 153 { 154 return \count($this->nodes); 155 } 156 157 public function getIterator(): \Traversable 158 { 159 return new \ArrayIterator($this->nodes); 160 } 161 162 public function getTemplateName(): ?string 163 { 164 return $this->sourceContext ? $this->sourceContext->getName() : null; 165 } 166 167 public function setSourceContext(Source $source): void 168 { 169 $this->sourceContext = $source; 170 foreach ($this->nodes as $node) { 171 $node->setSourceContext($source); 172 } 173 } 174 175 public function getSourceContext(): ?Source 176 { 177 return $this->sourceContext; 178 } 179} 180