Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
57 / 57
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
TwigServiceProvider
100.00% covered (success)
100.00%
57 / 57
100.00% covered (success)
100.00%
4 / 4
8
100.00% covered (success)
100.00%
1 / 1
 register
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 boot
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 registerTwigEngine
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
1 / 1
2
 registerTwigExtensions
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Renderer;
6
7use Engelsystem\Config\Config as EngelsystemConfig;
8use Engelsystem\Container\ServiceProvider;
9use Engelsystem\Renderer\Twig\Extensions\Assets;
10use Engelsystem\Renderer\Twig\Extensions\Authentication;
11use Engelsystem\Renderer\Twig\Extensions\Config;
12use Engelsystem\Renderer\Twig\Extensions\Csrf;
13use Engelsystem\Renderer\Twig\Extensions\Develop;
14use Engelsystem\Renderer\Twig\Extensions\Globals;
15use Engelsystem\Renderer\Twig\Extensions\Legacy;
16use Engelsystem\Renderer\Twig\Extensions\Markdown;
17use Engelsystem\Renderer\Twig\Extensions\Notification;
18use Engelsystem\Renderer\Twig\Extensions\Qr;
19use Engelsystem\Renderer\Twig\Extensions\Session;
20use Engelsystem\Renderer\Twig\Extensions\StringExtension;
21use Engelsystem\Renderer\Twig\Extensions\Translation;
22use Engelsystem\Renderer\Twig\Extensions\Url;
23use Engelsystem\Renderer\Twig\Extensions\Uuid;
24use Symfony\Component\VarDumper\VarDumper;
25use Twig\Environment as Twig;
26use Twig\Extension\CoreExtension as TwigCore;
27use Twig\Loader\FilesystemLoader;
28use Twig\Loader\LoaderInterface as TwigLoaderInterface;
29use TwigBridge\Extension\Laravel\Model as TwigModel;
30
31class TwigServiceProvider extends ServiceProvider
32{
33    /** @var array<string, class-string> */
34    protected array $extensions = [
35        'assets'         => Assets::class,
36        'authentication' => Authentication::class,
37        'config'         => Config::class,
38        'csrf'           => Csrf::class,
39        'develop'        => Develop::class,
40        'globals'        => Globals::class,
41        'notification'   => Notification::class,
42        'twigmodel'      => TwigModel::class,
43        'session'        => Session::class,
44        'string'         => StringExtension::class,
45        'legacy'         => Legacy::class,
46        'markdown'       => Markdown::class,
47        'qr'             => Qr::class,
48        'translation'    => Translation::class,
49        'url'            => Url::class,
50        'uuid'           => Uuid::class,
51    ];
52
53    public function register(): void
54    {
55        $this->registerTwigEngine();
56
57        foreach ($this->extensions as $alias => $class) {
58            $this->registerTwigExtensions($class, $alias);
59        }
60    }
61
62    public function boot(): void
63    {
64        /** @var Twig $renderer */
65        $renderer = $this->app->get('twig.environment');
66        /** @var Twig $textRenderer */
67        $textRenderer = $this->app->get('twig.textEnvironment');
68
69        foreach ($this->app->tagged('twig.extension') as $extension) {
70            $renderer->addExtension($extension);
71            $textRenderer->addExtension($extension);
72        }
73
74        if (class_exists(VarDumper::class)) {
75            /** @var Develop $dev */
76            $dev = $this->app->get('twig.extension.develop');
77            $dev->setDumper($this->app->make(VarDumper::class));
78        }
79    }
80
81    protected function registerTwigEngine(): void
82    {
83        // Paths are added twice to allow "more specific" overwrites with "views/" prefix from plugin templates
84        $viewsPath = [$this->app->get('path.views'), $this->app->get('path.views') . '/..'];
85        /** @var EngelsystemConfig $config */
86        $config = $this->app->get('config');
87
88        $twigLoader = $this->app->make(TwigLoader::class, ['paths' => $viewsPath]);
89        $this->app->instance(TwigLoader::class, $twigLoader);
90        $this->app->instance(FilesystemLoader::class, $twigLoader);
91        $this->app->instance(TwigLoaderInterface::class, $twigLoader);
92        $this->app->instance('twig.loader', $twigLoader);
93        $this->app->tag('twig.loader', ['twig.loader']);
94
95        $twigTextLoader = $this->app->make(TwigTextLoader::class, ['paths' => $viewsPath]);
96        $this->app->instance('twig.textLoader', $twigTextLoader);
97        $this->app->tag('twig.textLoader', ['twig.loader']);
98
99        $cache = $this->app->get('path.cache.views');
100        $twigDebug = false;
101        if ($config->get('environment') == 'development') {
102            $cache = false;
103            $twigDebug = true;
104        }
105
106        $twigOptions =  [
107            'cache'            => $cache,
108            'auto_reload'      => true,
109            'debug'            => $twigDebug,
110            'strict_variables' => $twigDebug,
111        ];
112
113        /** @var Twig $twig */
114        $twig = $this->app->make(Twig::class, ['options' => $twigOptions]);
115        $this->app->instance(Twig::class, $twig);
116        $this->app->instance('twig.environment', $twig);
117
118        /** @var TwigCore $twigCore */
119        $twigCore = $twig->getExtension(TwigCore::class);
120        $twigCore->setTimezone($config->get('timezone'));
121
122        $twigEngine = $this->app->make(TwigEngine::class);
123        $this->app->instance('renderer.twigEngine', $twigEngine);
124
125        /** @var Twig $textTwig */
126        $textTwig = $this->app->make(
127            Twig::class,
128            ['loader' => $twigTextLoader, 'options' => [...$twigOptions, 'autoescape' => false]]
129        );
130        $this->app->instance('twig.textEnvironment', $textTwig);
131        $twigTextEngine = $this->app->make(TwigEngine::class, ['twig' => $textTwig]);
132        $this->app->instance('renderer.twigTextEngine', $twigTextEngine);
133
134        // Text is tagged first to catch .text.twig files
135        $this->app->tag('renderer.twigTextEngine', ['renderer.engine']);
136        $this->app->tag('renderer.twigEngine', ['renderer.engine']);
137
138        // Add token parsers
139        $extendsTokenParser = $this->app->make(
140            ExtendsTokenParser::class,
141            ['basePath' => $this->app->get('path.resources')],
142        );
143        $twig->addTokenParser($extendsTokenParser);
144    }
145
146    protected function registerTwigExtensions(string $class, string $alias): void
147    {
148        $alias = 'twig.extension.' . $alias;
149        $this->app->alias($class, $alias);
150
151        $this->app->tag($alias, ['twig.extension']);
152    }
153}