Passed
Push — master ( a9f58f...13a36e )
by Divine Niiquaye
38:13 queued 25:24
created

TwigRender::withLoader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Biurad opensource projects.
7
 *
8
 * PHP version 7.2 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Biurad\UI\Renders;
19
20
use Biurad\UI\Exceptions\LoaderException;
21
use Biurad\UI\Interfaces\CacheInterface as RenderCacheInterface;
22
use Biurad\UI\Interfaces\RenderInterface;
23
use Biurad\UI\Template;
24
use Twig;
25
use Twig\Extension\ExtensionInterface;
26
use Twig\Loader\LoaderInterface;
27
use Twig\NodeVisitor\NodeVisitorInterface;
28
use Twig\RuntimeLoader\RuntimeLoaderInterface;
29
30
/**
31
 * Render for Twig templating.
32
 *
33
 * @author Divine Niiquaye Ibok <[email protected]>
34
 */
35
final class TwigRender extends AbstractRender implements RenderCacheInterface
36
{
37
    protected const EXTENSIONS = ['twig'];
38
39
    /** @var Twig\Environment */
40
    protected $environment;
41
42
    /**
43
     * TwigEngine constructor.
44
     *
45
     * @param string[] $extensions
46
     */
47 4
    public function __construct(Twig\Environment $environment = null, array $extensions = self::EXTENSIONS)
48
    {
49 4
        $this->environment = $environment ?? new Twig\Environment(new Twig\Loader\ArrayLoader());
50 4
        $this->extensions = $extensions;
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56 4
    public function withCache(?string $cacheDir): void
57
    {
58 4
        if (null !== $cacheDir) {
59 3
            if (false !== $this->environment->getCache()) {
60
                throw new LoaderException('The Twig render has an existing cache implementation which must be removed.');
61
            }
62
63 3
            $this->environment->setCache(new Twig\Cache\FilesystemCache($cacheDir));
64
        }
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70 4
    public function withLoader(Template $loader): RenderInterface
71
    {
72 4
        $this->environment->addFilter(new Twig\TwigFilter('template', [$loader, 'find'], ['is_safe' => ['all']]));
73 4
        $this->environment->addFunction(new Twig\TwigFunction('template', [$loader, 'render'], ['is_safe' => ['all']]));
74
75 4
        return parent::withLoader($loader);
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 4
    public function render(string $template, array $parameters): string
82
    {
83 4
        $source = self::loadHtml($template) ?? $template;
84
85 4
        if ($source !== $template || !\file_exists($template)) {
86
            $loader = new Twig\Loader\ArrayLoader([$template => $source]);
87
        } else {
88 4
            $loader = new class ($this->loader) extends Twig\Loader\FilesystemLoader {
0 ignored issues
show
Bug introduced by
It seems like $this->loader can also be of type null; however, parameter $loader of anonymous//src/Renders/T...er.php$0::__construct() does only seem to accept Biurad\UI\Template, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
            $loader = new class (/** @scrutinizer ignore-type */ $this->loader) extends Twig\Loader\FilesystemLoader {
Loading history...
89
                /** @var Template */
90
                private $loader;
91
92
                public function __construct(Template $loader, $paths = [], string $rootPath = null)
93
                {
94 4
                    $this->loader = $loader;
95 4
                    parent::__construct($paths, $rootPath);
96
                }
97
98
                protected function findTemplate(string $name, bool $throw = true): ?string
99
                {
100 4
                    if (isset($this->cache[$name])) {
101 3
                        return $this->cache[$name];
102
                    }
103
104 4
                    if (isset($this->errorCache[$name])) {
105
                        if (!$throw) {
106
                            return null;
107
                        }
108
109
                        throw new Twig\Error\LoaderError($this->errorCache[$name]);
110
                    }
111
112 4
                    if (!\is_file($name)) {
113
                        $newName = $this->loader->find($name);
114
115
                        if (null === $newName) {
116
                            $this->errorCache[$name] = \sprintf('The template "%s" is not a valid file path.', $name);
117
118
                            if (!$throw) {
119
                                return null;
120
                            }
121
122
                            throw new Twig\Error\LoaderError($this->errorCache[$name]);
123
                        }
124
                        $name = $newName;
125
                    }
126
127 4
                    return $this->cache[$name] = $name;
128
                }
129 4
            };
130
        }
131 4
        $this->addLoader($loader);
132
133 4
        return $this->environment->load($template)->render($parameters);
134
    }
135
136
    public function setCharset(string $charset): void
137
    {
138
        $this->environment->setCharset($charset);
139
    }
140
141 4
    public function addLoader(LoaderInterface $loader): void
142
    {
143 4
        $templateLoader = $this->environment->getLoader();
144
145 4
        if ($templateLoader instanceof Twig\Loader\ChainLoader) {
146
            $templateLoader->addLoader($loader);
147
148
            return;
149
        }
150
151 4
        if ($loader instanceof Twig\Loader\ChainLoader) {
152
            $loader->addLoader($templateLoader);
153 4
        } elseif (!$templateLoader instanceof Twig\Loader\ArrayLoader) {
154 3
            $loader = new Twig\Loader\ChainLoader([$loader, $templateLoader]);
155
        }
156
157 4
        $this->environment->setLoader($loader);
158
    }
159
160
    public function addRuntimeLoader(RuntimeLoaderInterface $loader): void
161
    {
162
        $this->environment->addRuntimeLoader($loader);
163
    }
164
165
    public function addExtension(ExtensionInterface $extension): void
166
    {
167
        $this->environment->addExtension($extension);
168
    }
169
170
    /**
171
     * @param ExtensionInterface[] $extensions An array of extensions
172
     */
173
    public function setExtensions(array $extensions): void
174
    {
175
        $this->environment->setExtensions($extensions);
176
    }
177
178
    public function addNodeVisitor(NodeVisitorInterface $visitor): void
179
    {
180
        $this->environment->addNodeVisitor($visitor);
181
    }
182
183
    public function addFilter(Twig\TwigFilter $filter): void
184
    {
185
        $this->environment->addFilter($filter);
186
    }
187
188
    public function registerUndefinedFilterCallback(callable $callable): void
189
    {
190
        $this->environment->registerUndefinedFilterCallback($callable);
191
    }
192
193
    public function addFunction(Twig\TwigFunction $function): void
194
    {
195
        $this->environment->addFunction($function);
196
    }
197
}
198