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
![]() |
|||
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 |