Passed
Pull Request — master (#1676)
by Arnaud
05:15 queued 14s
created

Twig::addGlobal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Cecil\Renderer;
15
16
use Cecil\Builder;
17
use Cecil\Renderer\Extension\Core as CoreExtension;
18
use Cecil\Util;
19
use Symfony\Bridge\Twig\Extension\TranslationExtension;
20
use Symfony\Component\Translation\Formatter\MessageFormatter;
21
use Symfony\Component\Translation\IdentityTranslator;
22
use Symfony\Component\Translation\Translator;
23
use Twig\Extra\Intl\IntlExtension;
24
25
/**
26
 * Class Twig.
27
 */
28
class Twig implements RendererInterface
29
{
30
    /** @var Builder */
31
    private $builder;
32
33
    /** @var \Twig\Environment */
34
    private $twig;
35
36
    /** @var Translator */
37
    private $translator = null;
38
39
    /** @var \Twig\Profiler\Profile */
40
    private $profile = null;
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 1
    public function __construct(Builder $builder, $templatesPath)
46
    {
47 1
        $this->builder = $builder;
48
        // load layouts
49 1
        $loader = new \Twig\Loader\FilesystemLoader($templatesPath);
50
        // default options
51 1
        $loaderOptions = [
52 1
            'debug'            => $this->builder->isDebug(),
53 1
            'strict_variables' => true,
54 1
            'autoescape'       => false,
55 1
            'auto_reload'      => true,
56 1
            'cache'            => false,
57 1
        ];
58
        // use Twig cache?
59 1
        if ((bool) $this->builder->getConfig()->get('cache.templates.enabled')) {
60 1
            $loaderOptions = array_replace($loaderOptions, ['cache' => $this->builder->getConfig()->getCacheTemplatesPath()]);
61
        }
62
        // create the Twig instance
63 1
        $this->twig = new \Twig\Environment($loader, $loaderOptions);
64
        // set date format
65 1
        $this->twig->getExtension(\Twig\Extension\CoreExtension::class)
66 1
            ->setDateFormat((string) $this->builder->getConfig()->get('date.format'));
67
        // set timezone
68 1
        if ($this->builder->getConfig()->has('date.timezone')) {
69
            $this->twig->getExtension(\Twig\Extension\CoreExtension::class)
70
                ->setTimezone((string) $this->builder->getConfig()->get('date.timezone') ?? date_default_timezone_get());
71
        }
72
        /*
73
         * adds extensions
74
         */
75
        // Cecil core extension
76 1
        $this->twig->addExtension(new CoreExtension($this->builder));
77
        // required by `template_from_string()`
78 1
        $this->twig->addExtension(new \Twig\Extension\StringLoaderExtension());
79
        // l10n
80 1
        $this->translator = new Translator(
81 1
            $this->builder->getConfig()->getLanguageProperty('locale'),
82 1
            new MessageFormatter(new IdentityTranslator()),
83 1
            (bool) $this->builder->getConfig()->get('cache.templates.enabled') ? $this->builder->getConfig()->getCacheTranslationsPath() : null,
84 1
            $this->builder->isDebug()
85 1
        );
86 1
        if (\count($this->builder->getConfig()->getLanguages()) > 0) {
87 1
            foreach ((array) $this->builder->getConfig()->get('translations.formats') as $format) {
88
                $loader = sprintf('Symfony\Component\Translation\Loader\%sFileLoader', ucfirst($format));
89
                if (class_exists($loader)) {
90
                    $this->translator->addLoader($format, new $loader());
91
                    $this->builder->getLogger()->debug(sprintf('Translation loader for format "%s" found', $format));
92
                }
93
            }
94 1
            foreach ($this->builder->getConfig()->getLanguages() as $lang) {
95
                // internal
96 1
                $this->addTransResource($this->builder->getConfig()->getTranslationsInternalPath(), $lang['locale']);
97
                // themes
98 1
                if ($themes = $this->builder->getConfig()->getTheme()) {
99 1
                    foreach ($themes as $theme) {
100 1
                        $this->addTransResource($this->builder->getConfig()->getThemeDirPath($theme, 'translations'), $lang['locale']);
101
                    }
102
                }
103
                // site
104 1
                $this->addTransResource($this->builder->getConfig()->getTranslationsPath(), $lang['locale']);
105
            }
106
        }
107 1
        $this->twig->addExtension(new TranslationExtension($this->translator));
108
        // intl
109 1
        $this->twig->addExtension(new IntlExtension());
110 1
        if (\extension_loaded('intl')) {
111 1
            $this->builder->getLogger()->debug('Intl extension is loaded');
112
        }
113
        // filters fallback
114 1
        $this->twig->registerUndefinedFilterCallback(function ($name) {
115
            switch ($name) {
116 1
                case 'localizeddate':
117 1
                    return new \Twig\TwigFilter($name, function (\DateTime $value = null) {
118 1
                        return date($this->builder->getConfig()->get('date.format') ?? 'F j, Y', $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

118
                        return date($this->builder->getConfig()->get('date.format') ?? 'F j, Y', $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...
119 1
                    });
120
            }
121
122
            return false;
123 1
        });
124
        // debug
125 1
        if ($this->builder->isDebug()) {
126
            // dump()
127 1
            $this->twig->addExtension(new \Twig\Extension\DebugExtension());
128
            // profiler
129 1
            $this->profile = new \Twig\Profiler\Profile();
130 1
            $this->twig->addExtension(new \Twig\Extension\ProfilerExtension($this->profile));
131
        }
132
        // loads custom extensions
133 1
        if ($this->builder->getConfig()->has('layouts.extensions')) {
134 1
            Util::autoload($builder, 'extensions');
135 1
            foreach ((array) $this->builder->getConfig()->get('layouts.extensions') as $name => $class) {
136 1
                $this->twig->addExtension(new $class($this->builder));
137 1
                $this->builder->getLogger()->debug(sprintf('Extension "%s" (%s) added.', $name, $class));
138
            }
139
        }
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 1
    public function addGlobal(string $name, $value): void
146
    {
147 1
        $this->twig->addGlobal($name, $value);
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 1
    public function render(string $template, array $variables): string
154
    {
155 1
        return $this->twig->render($template, $variables);
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161 1
    public function setLocale(string $locale): void
162
    {
163 1
        if (\extension_loaded('intl')) {
164 1
            \Locale::setDefault($locale);
165
        }
166 1
        $this->translator === null ?: $this->translator->setLocale($locale);
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172 1
    public function addTransResource(string $translationsDir, string $locale): void
173
    {
174 1
        $locales = [$locale];
175
        // if locale is 'fr_FR', trying to load ['fr', 'fr_FR']
176 1
        if (\strlen($locale) > 2) {
177 1
            array_unshift($locales, substr($locale, 0, 2));
178
        }
179 1
        foreach ($locales as $locale) {
180 1
            foreach ((array) $this->builder->getConfig()->get('translations.formats') as $format) {
181
                $translationFile = Util::joinPath($translationsDir, sprintf('messages.%s.%s', $locale, $format));
182
                if (Util\File::getFS()->exists($translationFile)) {
183
                    $this->translator->addResource($format, $translationFile, $locale);
184
                    $this->builder->getLogger()->debug(sprintf('Translation file "%s" added', $translationFile));
185
                }
186
            }
187
        }
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193 1
    public function getDebugProfile(): ?\Twig\Profiler\Profile
194
    {
195 1
        return $this->profile;
196
    }
197
}
198