Twig::__construct()   C
last analyzed

Complexity

Conditions 16
Paths 160

Size

Total Lines 106
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 64
CRAP Score 16.023

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 16
eloc 64
c 1
b 0
f 1
nc 160
nop 2
dl 0
loc 106
ccs 64
cts 67
cp 0.9552
crap 16.023
rs 5.0666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of Cecil.
5
 *
6
 * (c) Arnaud Ligny <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Cecil\Renderer;
15
16
use Cecil\Builder;
17
use Cecil\Exception\RuntimeException;
18
use Cecil\Renderer\Extension\Core as CoreExtension;
19
use Cecil\Util;
20
use Performing\TwigComponents\Configuration;
21
use Symfony\Bridge\Twig\Extension\TranslationExtension;
22
use Symfony\Component\Translation\Formatter\MessageFormatter;
23
use Symfony\Component\Translation\IdentityTranslator;
24
use Symfony\Component\Translation\Translator;
25
use Twig\Extra\Cache\CacheExtension;
26
use Twig\Extra\Intl\IntlExtension;
27
use Twig\Extra\String\StringExtension;
28
29
/**
30
 * Twig renderer.
31
 *
32
 * This class is responsible for rendering templates using the Twig templating engine.
33
 * It initializes Twig with the necessary configurations, loads extensions, and provides methods
34
 * to render templates, add global variables, and manage translations.
35
 * It also supports debugging and profiling when in debug mode.
36
 */
37
class Twig implements RendererInterface
38
{
39
    /**
40
     * Builder object.
41
     * @var Builder
42
     */
43
    protected $builder;
44
    /**
45
     * Twig environment instance.
46
     * @var \Twig\Environment
47
     */
48
    private $twig;
49
    /**
50
     * Translator instance for translations.
51
     * @var Translator
52
     */
53
    private $translator = null;
54
    /**
55
     * Profile for debugging and profiling.
56
     * @var \Twig\Profiler\Profile
57
     */
58
    private $profile = null;
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 1
    public function __construct(Builder $builder, $templatesPath)
64
    {
65 1
        $this->builder = $builder;
66
        // load layouts
67 1
        $loader = new \Twig\Loader\FilesystemLoader($templatesPath);
68
        // default options
69 1
        $loaderOptions = [
70 1
            'debug'            => $this->builder->isDebug(),
71 1
            'strict_variables' => true,
72 1
            'autoescape'       => false,
73 1
            'auto_reload'      => true,
74 1
            'cache'            => false,
75 1
        ];
76
        // use Twig cache?
77 1
        if ($this->builder->getConfig()->isEnabled('cache.templates')) {
78 1
            $loaderOptions = array_replace($loaderOptions, ['cache' => $this->builder->getConfig()->getCacheTemplatesPath()]);
79
        }
80
        // create the Twig instance
81 1
        $this->twig = new \Twig\Environment($loader, $loaderOptions);
82
        // set date format
83 1
        $this->twig->getExtension(\Twig\Extension\CoreExtension::class)
84 1
            ->setDateFormat((string) $this->builder->getConfig()->get('date.format'));
85
        // set timezone
86 1
        if ($this->builder->getConfig()->has('date.timezone')) {
87
            $this->twig->getExtension(\Twig\Extension\CoreExtension::class)
88
                ->setTimezone($this->builder->getConfig()->get('date.timezone') ?? date_default_timezone_get());
89
        }
90
        /*
91
         * adds extensions
92
         */
93
        // Cecil core extension
94 1
        $this->twig->addExtension(new CoreExtension($this->builder));
95
        // required by `template_from_string()`
96 1
        $this->twig->addExtension(new \Twig\Extension\StringLoaderExtension());
97
        // the `u` filter (https://github.com/twigphp/string-extra)
98 1
        $this->twig->addExtension(new StringExtension());
99
        // l10n
100 1
        $this->translator = new Translator(
101 1
            $this->builder->getConfig()->getLanguageProperty('locale'),
102 1
            new MessageFormatter(new IdentityTranslator()),
103 1
            $this->builder->getConfig()->isEnabled('cache.translations') ? $this->builder->getConfig()->getCacheTranslationsPath() : null,
104 1
            $this->builder->isDebug()
105 1
        );
106 1
        if (\count($this->builder->getConfig()->getLanguages()) > 0) {
107 1
            foreach ((array) $this->builder->getConfig()->get('layouts.translations.formats') as $format) {
108 1
                $loader = \sprintf('Symfony\Component\Translation\Loader\%sFileLoader', ucfirst($format));
109 1
                if (class_exists($loader)) {
110 1
                    $this->translator->addLoader($format, new $loader());
111 1
                    $this->builder->getLogger()->debug(\sprintf('Translation loader for format "%s" found', $format));
112
                }
113
            }
114 1
            foreach ($this->builder->getConfig()->getLanguages() as $lang) {
115
                // internal
116 1
                $this->addTransResource($this->builder->getConfig()->getTranslationsInternalPath(), $lang['locale']);
117
                // themes
118 1
                if ($themes = $this->builder->getConfig()->getTheme()) {
119 1
                    foreach ($themes as $theme) {
120 1
                        $this->addTransResource($this->builder->getConfig()->getThemeDirPath($theme, 'translations'), $lang['locale']);
121
                    }
122
                }
123
                // site
124 1
                $this->addTransResource($this->builder->getConfig()->getTranslationsPath(), $lang['locale']);
125
            }
126
        }
127 1
        $this->twig->addExtension(new TranslationExtension($this->translator));
128
        // intl
129 1
        $this->twig->addExtension(new IntlExtension());
130 1
        if (\extension_loaded('intl')) {
131 1
            $this->builder->getLogger()->debug('PHP Intl extension is loaded');
132
        }
133
        // filters fallback
134 1
        $this->twig->registerUndefinedFilterCallback(function ($name) {
135
            switch ($name) {
136 1
                case 'localizeddate':
137 1
                    return new \Twig\TwigFilter($name, function (?\DateTime $value = null) {
138 1
                        return date($this->builder->getConfig()->get('date.format'), $value->getTimestamp());
0 ignored issues
show
Bug introduced by
The method getTimestamp() does not exist on null. ( Ignorable by Annotation )

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

138
                        return date($this->builder->getConfig()->get('date.format'), $value->/** @scrutinizer ignore-call */ getTimestamp());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
It seems like $this->builder->getConfig()->get('date.format') can also be of type null; however, parameter $format of date() does only seem to accept string, 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

138
                        return date(/** @scrutinizer ignore-type */ $this->builder->getConfig()->get('date.format'), $value->getTimestamp());
Loading history...
139 1
                    });
140
            }
141
142
            return false;
143 1
        });
144
        // components
145 1
        Configuration::make($this->twig)
146 1
            ->setTemplatesPath($this->builder->getConfig()->get('layouts.components.dir') ?? 'components')
147 1
            ->setTemplatesExtension($this->builder->getConfig()->get('layouts.components.ext') ?? 'twig')
148 1
            ->useCustomTags()
149 1
            ->setup();
150
        // cache
151 1
        $this->twig->addExtension(new CacheExtension());
152 1
        $this->twig->addRuntimeLoader(new TwigCacheRuntimeLoader($this->builder->getConfig()->getCacheTemplatesPath()));
153
        // debug
154 1
        if ($this->builder->isDebug()) {
155
            // dump()
156 1
            $this->twig->addExtension(new \Twig\Extension\DebugExtension());
157
            // profiler
158 1
            $this->profile = new \Twig\Profiler\Profile();
159 1
            $this->twig->addExtension(new \Twig\Extension\ProfilerExtension($this->profile));
160
        }
161
        // loads custom extensions
162 1
        if ($this->builder->getConfig()->has('layouts.extensions')) {
163 1
            foreach ((array) $this->builder->getConfig()->get('layouts.extensions') as $name => $class) {
164
                try {
165 1
                    $this->twig->addExtension(new $class($this->builder));
166 1
                    $this->builder->getLogger()->debug(\sprintf('Twig extension "%s" added', $name));
167 1
                } catch (RuntimeException | \Error $e) {
168 1
                    $this->builder->getLogger()->error(\sprintf('Unable to add Twig extension "%s": %s', $name, $e->getMessage()));
169
                }
170
            }
171
        }
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 1
    public function addGlobal(string $name, $value): void
178
    {
179 1
        $this->twig->addGlobal($name, $value);
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185 1
    public function render(string $template, array $variables): string
186
    {
187 1
        return $this->twig->render($template, $variables);
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193 1
    public function setLocale(string $locale): void
194
    {
195 1
        if (\extension_loaded('intl')) {
196 1
            \Locale::setDefault($locale);
197
        }
198 1
        $this->translator === null ?: $this->translator->setLocale($locale);
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204 1
    public function addTransResource(string $translationsDir, string $locale): void
205
    {
206 1
        $locales = [$locale];
207
        // if locale is 'fr_FR', trying to load ['fr', 'fr_FR']
208 1
        if (\strlen($locale) > 2) {
209 1
            array_unshift($locales, substr($locale, 0, 2));
210
        }
211 1
        foreach ($locales as $locale) {
212 1
            foreach ((array) $this->builder->getConfig()->get('layouts.translations.formats') as $format) {
213 1
                $translationFile = Util::joinPath($translationsDir, \sprintf('messages.%s.%s', $locale, $format));
214 1
                if (Util\File::getFS()->exists($translationFile)) {
215 1
                    $this->translator->addResource($format, $translationFile, $locale);
216 1
                    $this->builder->getLogger()->debug(\sprintf('Translation file "%s" added', $translationFile));
217
                }
218
            }
219
        }
220
    }
221
222
    /**
223
     * Returns the Twig instance.
224
     */
225
    public function getTwig(): \Twig\Environment
226
    {
227
        return $this->twig;
228
    }
229
230
    /**
231
     * Returns debug profile.
232
     */
233 1
    public function getDebugProfile(): ?\Twig\Profiler\Profile
234
    {
235 1
        return $this->profile;
236
    }
237
}
238