Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
ExtendsTokenParser
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
4 / 4
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parse
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
 getTag
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNextParentFile
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Renderer;
6
7use Illuminate\Support\Str;
8use Twig\Error\SyntaxError;
9use Twig\Loader\FilesystemLoader as FilesystemLoader;
10use Twig\Node\EmptyNode;
11use Twig\Node\Node;
12use Twig\Token;
13use Twig\TokenParser\AbstractTokenParser;
14
15class ExtendsTokenParser extends AbstractTokenParser
16{
17    public function __construct(protected FilesystemLoader $loader, protected string $basePath)
18    {
19    }
20
21    /**
22     * Allows multi-inheritance by searching for the file to be extended in views and views of all plugins
23     */
24    public function parse(Token $token): Node
25    {
26        $stream = $this->parser->getStream();
27
28        if ($this->parser->peekBlockStack() || !$this->parser->isMainScope()) {
29            throw new SyntaxError(
30                'Cannot use "extend" in a block or macro.',
31                $token->getLine(),
32                $stream->getSourceContext(),
33            );
34        }
35
36        $parent = $this->parser->parseExpression();
37        $templateFile = $stream->getSourceContext()->getPath();
38        $extendsFile = $parent->getAttribute('value');
39
40        $extendsFile = $this->getNextParentFile($templateFile, $extendsFile);
41
42        $parent->setAttribute('value', $extendsFile);
43
44        $this->parser->setParent($parent);
45
46        $stream->expect(Token::BLOCK_END_TYPE);
47
48        return new EmptyNode($token->getLine());
49    }
50
51    public function getTag(): string
52    {
53        return 'extends';
54    }
55
56    protected function getNextParentFile(string $templateFile, string $extendsFile): string
57    {
58        $found = false;
59        foreach ($this->loader->getPaths() as $path) {
60            // Ignore file itself
61            if (Str::startsWith($templateFile, $path . '/')) {
62                $found = true;
63                continue;
64            }
65
66            // Skip all paths "above" the currently extending file
67            if (!$found) {
68                continue;
69            }
70
71            // Use file if it is loadable
72            $parentFilePath = Str::replaceStart($this->basePath . '/', '', $path . '/' . $extendsFile);
73            if ($this->loader->exists($parentFilePath)) {
74                return $parentFilePath;
75            }
76        }
77
78        return $extendsFile;
79    }
80}