blob: 5878131bec3918b0741d67a57d09bc94d11af327 [file] [log] [blame]
Matthias Andreas Benkard12a57352021-12-28 18:02:04 +01001<?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\TokenParser;
14
15use Twig\Error\SyntaxError;
16use Twig\Node\BlockNode;
17use Twig\Node\BlockReferenceNode;
18use Twig\Node\Node;
19use Twig\Node\PrintNode;
20use Twig\Token;
21
22/**
23 * Marks a section of a template as being reusable.
24 *
25 * {% block head %}
26 * <link rel="stylesheet" href="style.css" />
27 * <title>{% block title %}{% endblock %} - My Webpage</title>
28 * {% endblock %}
29 *
30 * @internal
31 */
32final class BlockTokenParser extends AbstractTokenParser
33{
34 public function parse(Token $token): Node
35 {
36 $lineno = $token->getLine();
37 $stream = $this->parser->getStream();
38 $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue();
39 if ($this->parser->hasBlock($name)) {
40 throw new SyntaxError(sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getTemplateLine()), $stream->getCurrent()->getLine(), $stream->getSourceContext());
41 }
42 $this->parser->setBlock($name, $block = new BlockNode($name, new Node([]), $lineno));
43 $this->parser->pushLocalScope();
44 $this->parser->pushBlockStack($name);
45
46 if ($stream->nextIf(/* Token::BLOCK_END_TYPE */ 3)) {
47 $body = $this->parser->subparse([$this, 'decideBlockEnd'], true);
48 if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) {
49 $value = $token->getValue();
50
51 if ($value != $name) {
52 throw new SyntaxError(sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext());
53 }
54 }
55 } else {
56 $body = new Node([
57 new PrintNode($this->parser->getExpressionParser()->parseExpression(), $lineno),
58 ]);
59 }
60 $stream->expect(/* Token::BLOCK_END_TYPE */ 3);
61
62 $block->setNode('body', $body);
63 $this->parser->popBlockStack();
64 $this->parser->popLocalScope();
65
66 return new BlockReferenceNode($name, $lineno, $this->getTag());
67 }
68
69 public function decideBlockEnd(Token $token): bool
70 {
71 return $token->test('endblock');
72 }
73
74 public function getTag(): string
75 {
76 return 'block';
77 }
78}