Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Parser.php
001 <?php
002
003 /*
004 * This file is part of Twig.
005 *
006 * (c) Fabien Potencier
007 * (c) Armin Ronacher
008 *
009 * For the full copyright and license information, please view the LICENSE
010 * file that was distributed with this source code.
011 */
012
013 namespace Twig;
014
015 use Twig\Error\SyntaxError;
016 use Twig\Node\BlockNode;
017 use Twig\Node\BlockReferenceNode;
018 use Twig\Node\BodyNode;
019 use Twig\Node\Expression\AbstractExpression;
020 use Twig\Node\MacroNode;
021 use Twig\Node\ModuleNode;
022 use Twig\Node\Node;
023 use Twig\Node\NodeCaptureInterface;
024 use Twig\Node\NodeOutputInterface;
025 use Twig\Node\PrintNode;
026 use Twig\Node\SpacelessNode;
027 use Twig\Node\TextNode;
028 use Twig\TokenParser\TokenParserInterface;
029
030 /**
031 * Default parser implementation.
032 *
033 * @author Fabien Potencier <fabien@symfony.com>
034 */
035 class Parser
036 {
037 private $stack = [];
038 private $stream;
039 private $parent;
040 private $handlers;
041 private $visitors;
042 private $expressionParser;
043 private $blocks;
044 private $blockStack;
045 private $macros;
046 private $env;
047 private $importedSymbols;
048 private $traits;
049 private $embeddedTemplates = [];
050 private $varNameSalt = 0;
051
052 public function __construct(Environment $env)
053 {
054 $this->env = $env;
055 }
056
057 public function getVarName()
058 {
059 return sprintf('__internal_parse_%d', $this->varNameSalt++);
060 }
061
062 public function parse(TokenStream $stream, $test = null, $dropNeedle = false)
063 {
064 $vars = get_object_vars($this);
065 unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
066 $this->stack[] = $vars;
067
068 // tag handlers
069 if (null === $this->handlers) {
070 $this->handlers = [];
071 foreach ($this->env->getTokenParsers() as $handler) {
072 $handler->setParser($this);
073
074 $this->handlers[$handler->getTag()] = $handler;
075 }
076 }
077
078 // node visitors
079 if (null === $this->visitors) {
080 $this->visitors = $this->env->getNodeVisitors();
081 }
082
083 if (null === $this->expressionParser) {
084 $this->expressionParser = new ExpressionParser($this, $this->env);
085 }
086
087 $this->stream = $stream;
088 $this->parent = null;
089 $this->blocks = [];
090 $this->macros = [];
091 $this->traits = [];
092 $this->blockStack = [];
093 $this->importedSymbols = [[]];
094 $this->embeddedTemplates = [];
095
096 try {
097 $body = $this->subparse($test, $dropNeedle);
098
099 if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) {
100 $body = new Node();
101 }
102 } catch (SyntaxError $e) {
103 if (!$e->getSourceContext()) {
104 $e->setSourceContext($this->stream->getSourceContext());
105 }
106
107 if (!$e->getTemplateLine()) {
108 $e->setTemplateLine($this->stream->getCurrent()->getLine());
109 }
110
111 throw $e;
112 }
113
114 $node = new ModuleNode(new BodyNode([$body]), $this->parent, new Node($this->blocks), new Node($this->macros), new Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext());
115
116 $traverser = new NodeTraverser($this->env, $this->visitors);
117
118 $node = $traverser->traverse($node);
119
120 // restore previous stack so previous parse() call can resume working
121 foreach (array_pop($this->stack) as $key => $val) {
122 $this->$key = $val;
123 }
124
125 return $node;
126 }
127
128 public function subparse($test, $dropNeedle = false)
129 {
130 $lineno = $this->getCurrentToken()->getLine();
131 $rv = [];
132 while (!$this->stream->isEOF()) {
133 switch ($this->getCurrentToken()->getType()) {
134 case /* Token::TEXT_TYPE */ 0:
135 $token = $this->stream->next();
136 $rv[] = new TextNode($token->getValue(), $token->getLine());
137 break;
138
139 case /* Token::VAR_START_TYPE */ 2:
140 $token = $this->stream->next();
141 $expr = $this->expressionParser->parseExpression();
142 $this->stream->expect(/* Token::VAR_END_TYPE */ 4);
143 $rv[] = new PrintNode($expr, $token->getLine());
144 break;
145
146 case /* Token::BLOCK_START_TYPE */ 1:
147 $this->stream->next();
148 $token = $this->getCurrentToken();
149
150 if (/* Token::NAME_TYPE */ 5 !== $token->getType()) {
151 throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext());
152 }
153
154 if (null !== $test && $test($token)) {
155 if ($dropNeedle) {
156 $this->stream->next();
157 }
158
159 if (1 === \count($rv)) {
160 return $rv[0];
161 }
162
163 return new Node($rv, [], $lineno);
164 }
165
166 if (!isset($this->handlers[$token->getValue()])) {
167 if (null !== $test) {
168 $e = new SyntaxError(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
169
170 if (\is_array($test) && isset($test[0]) && $test[0] instanceof TokenParserInterface) {
171 $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno));
172 }
173 } else {
174 $e = new SyntaxError(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
175 $e->addSuggestions($token->getValue(), array_keys($this->env->getTags()));
176 }
177
178 throw $e;
179 }
180
181 $this->stream->next();
182
183 $subparser = $this->handlers[$token->getValue()];
184 $node = $subparser->parse($token);
185 if (null !== $node) {
186 $rv[] = $node;
187 }
188 break;
189
190 default:
191 throw new SyntaxError('Lexer or parser ended up in unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext());
192 }
193 }
194
195 if (1 === \count($rv)) {
196 return $rv[0];
197 }
198
199 return new Node($rv, [], $lineno);
200 }
201
202 public function getBlockStack()
203 {
204 return $this->blockStack;
205 }
206
207 public function peekBlockStack()
208 {
209 return isset($this->blockStack[\count($this->blockStack) - 1]) ? $this->blockStack[\count($this->blockStack) - 1] : null;
210 }
211
212 public function popBlockStack()
213 {
214 array_pop($this->blockStack);
215 }
216
217 public function pushBlockStack($name)
218 {
219 $this->blockStack[] = $name;
220 }
221
222 public function hasBlock($name)
223 {
224 return isset($this->blocks[$name]);
225 }
226
227 public function getBlock($name)
228 {
229 return $this->blocks[$name];
230 }
231
232 public function setBlock($name, BlockNode $value)
233 {
234 $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine());
235 }
236
237 public function hasMacro($name)
238 {
239 return isset($this->macros[$name]);
240 }
241
242 public function setMacro($name, MacroNode $node)
243 {
244 $this->macros[$name] = $node;
245 }
246
247 /**
248 * @deprecated since Twig 2.7 as there are no reserved macro names anymore, will be removed in 3.0.
249 */
250 public function isReservedMacroName($name)
251 {
252 @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.7 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED);
253
254 return false;
255 }
256
257 public function addTrait($trait)
258 {
259 $this->traits[] = $trait;
260 }
261
262 public function hasTraits()
263 {
264 return \count($this->traits) > 0;
265 }
266
267 public function embedTemplate(ModuleNode $template)
268 {
269 $template->setIndex(mt_rand());
270
271 $this->embeddedTemplates[] = $template;
272 }
273
274 public function addImportedSymbol($type, $alias, $name = null, AbstractExpression $node = null)
275 {
276 $this->importedSymbols[0][$type][$alias] = ['name' => $name, 'node' => $node];
277 }
278
279 public function getImportedSymbol($type, $alias)
280 {
281 // if the symbol does not exist in the current scope (0), try in the main/global scope (last index)
282 return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null);
283 }
284
285 public function isMainScope()
286 {
287 return 1 === \count($this->importedSymbols);
288 }
289
290 public function pushLocalScope()
291 {
292 array_unshift($this->importedSymbols, []);
293 }
294
295 public function popLocalScope()
296 {
297 array_shift($this->importedSymbols);
298 }
299
300 /**
301 * @return ExpressionParser
302 */
303 public function getExpressionParser()
304 {
305 return $this->expressionParser;
306 }
307
308 public function getParent()
309 {
310 return $this->parent;
311 }
312
313 public function setParent($parent)
314 {
315 $this->parent = $parent;
316 }
317
318 /**
319 * @return TokenStream
320 */
321 public function getStream()
322 {
323 return $this->stream;
324 }
325
326 /**
327 * @return Token
328 */
329 public function getCurrentToken()
330 {
331 return $this->stream->getCurrent();
332 }
333
334 private function filterBodyNodes(Node $node, bool $nested = false)
335 {
336 // check that the body does not contain non-empty output nodes
337 if (
338 ($node instanceof TextNode && !ctype_space($node->getAttribute('data')))
339 ||
340 // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
341 (!$node instanceof TextNode && !$node instanceof BlockReferenceNode && ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode))
342 ) {
343 if (false !== strpos((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) {
344 $t = substr($node->getAttribute('data'), 3);
345 if ('' === $t || ctype_space($t)) {
346 // bypass empty nodes starting with a BOM
347 return;
348 }
349 }
350
351 throw new SyntaxError('A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext());
352 }
353
354 // bypass nodes that "capture" the output
355 if ($node instanceof NodeCaptureInterface) {
356 // a "block" tag in such a node will serve as a block definition AND be displayed in place as well
357 return $node;
358 }
359
360 // to be removed completely in Twig 3.0
361 if (!$nested && $node instanceof SpacelessNode) {
362 @trigger_error(sprintf('Using the spaceless tag at the root level of a child template in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), \E_USER_DEPRECATED);
363 }
364
365 // "block" tags that are not captured (see above) are only used for defining
366 // the content of the block. In such a case, nesting it does not work as
367 // expected as the definition is not part of the default template code flow.
368 if ($nested && ($node instanceof BlockReferenceNode || $node instanceof \Twig_Node_BlockReference)) {
369 //throw new SyntaxError('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext());
370 @trigger_error(sprintf('Nesting a block definition under a non-capturing node in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), \E_USER_DEPRECATED);
371
372 return;
373 }
374
375 // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
376 if ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode) {
377 return;
378 }
379
380 // here, $nested means "being at the root level of a child template"
381 // we need to discard the wrapping "Twig_Node" for the "body" node
382 $nested = $nested || ('Twig_Node' !== \get_class($node) && Node::class !== \get_class($node));
383 foreach ($node as $k => $n) {
384 if (null !== $n && null === $this->filterBodyNodes($n, $nested)) {
385 $node->removeNode($k);
386 }
387 }
388
389 return $node;
390 }
391 }
392
393 class_alias('Twig\Parser', 'Twig_Parser');
394