Completed
Push — master ( a574b2...d31abe )
by Filipe
05:49
created

Template::checkEngine()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 1
cts 1
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
3
/**
4
 * This file is part of slick/template package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Template;
11
12
use Slick\Template\Engine\Twig;
13
use Slick\Template\Exception\InvalidArgumentException;
14
use Slick\Template\Extension\Text;
15
16
/**
17
 * Template
18
 *
19
 * @package Slick\Template
20
 */
21
class Template
22
{
23
24
    /** Known engines */
25
    const ENGINE_TWIG = Twig::class;
26
27
    /** Known engine extensions */
28
    const EXTENSION_TWIG_TEXT = Text::class;
29
30
    /**
31
     * @var array
32
     */
33
    private $defaultOptions = [];
34
35
    /**
36
     * @var array
37
     */
38
    private $options;
39
40
    /**
41
     * @var string
42
     */
43
    private $engine;
44
45
    /**
46
     * @var string[] a list of available paths
47
     */
48
    private static $paths = ['./'];
49
50
    /**
51
     * @var array Array containing template extensions
52
     */
53
    private static $extensions = [
54
        self::EXTENSION_TWIG_TEXT => null,
55
    ];
56
57
    /**
58
     * Creates a template factory
59
     *
60
     * @param array $options
61
     */
62
    public function __construct(array $options = [])
63
    {
64
        $this->engine = array_key_exists('engine', $options)
65
            ? $this->checkEngine($options['engine'])
66
            : self::ENGINE_TWIG;
67
68
        $this->options = array_key_exists('options', $options)
69
            ? array_merge($this->defaultOptions, $options['options'])
70
            : $this->defaultOptions;
71
    }
72
73 2
    /**
74
     * Prepends a searchable path to available paths list.
75 2
     *
76 2
     * @param string $path
77 2
     */
78 1 View Code Duplication
    public static function addPath($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
79 2
    {
80
        $path = str_replace('//', '/', rtrim($path, '/'));
81
        if (is_dir($path) && !in_array($path, self::$paths)) {
82
            array_unshift(self::$paths, $path);
83
        }
84
    }
85
86 2
    /**
87
     * Appends a searchable path to available paths list.
88 2
     *
89 2
     * @param string $path
90 2
     */
91 1 View Code Duplication
    public static function appendPath($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
92 2
    {
93
        $path = str_replace('//', '/', rtrim($path, '/'));
94
        if (is_dir($path) && !in_array($path, self::$paths)) {
95
            array_push(self::$paths, $path);
96
        }
97
    }
98
99 4
    /**
100
     * Adds an extension to the template engine
101 4
     *
102
     * @param string|object $className The class name or an instance
103
     *                                 of EngineExtensionInterface interface
104
     *
105
     * @return self|$this|Template
106
     */
107
    public function addExtension($className)
108
    {
109
        $object = is_object($className) ? $className : null;
110
        $className = is_object($className) ? get_class($object) : $className;
111 4
112
        $this->checkExtension($className);
113 4
114 2
        self::$extensions[$className] = $object;
115
        return $this;
116 2
    }
117 2
118 2
    /**
119
     * Initializes the engine
120
     *
121
     * @return TemplateEngineInterface
122
     */
123
    public function initialize()
124
    {
125
        /** @var TemplateEngineInterface $engine */
126
        $engine = new $this->engine($this->options);
127
        $engine->setLocations(self::$paths);
128
        $this->applyExtensions($engine);
129 6
        return $engine;
130
    }
131 6
132 6
    /**
133
     * Checks if provided class implements the TemplateEngineInterface
134 6
     *
135
     * @param string $engine
136 4
     *
137 4
     * @return string
138
     */
139
    private function checkEngine($engine)
140
    {
141
        if (! is_subclass_of($engine, TemplateEngineInterface::class)) {
142
            $name = TemplateEngineInterface::class;
143
            throw new InvalidArgumentException(
144
                "Class '{$engine}' does not implement '{$name}'."
145
            );
146
        }
147
        return $engine;
148 2
    }
149
150 2
    /**
151 2
     * Apply defined extensions to the provided template engine
152
     *
153
     * @param TemplateEngineInterface $engine
154
     *
155
     * @return TemplateEngineInterface
156
     */
157
    private function applyExtensions(TemplateEngineInterface $engine)
158
    {
159
        foreach (static::$extensions as $className => $extension) {
0 ignored issues
show
Bug introduced by
Since $extensions is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $extensions to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
160
            $ext = $this->getExtension($className, $extension);
161 2
            if ($ext->appliesTo($engine)) {
162
                $ext->update($engine);
163 2
            }
164 2
        }
165 2
        return $engine;
166 2
    }
167 1
168 1
    /**
169 2
     * Creates the extension
170
     *
171
     * @param string $class
172
     * @param EngineExtensionInterface $extension
173
     *
174
     * @return EngineExtensionInterface
175
     */
176
    private function getExtension($class, $extension)
177
    {
178 2
        if (is_object($extension)) {
179
            return $extension;
180 2
        }
181 2
        $this->checkExtension($class);
182
        return new $class();
183
    }
184 2
185 2
    /**
186
     * Checks if provided class implements the EngineExtensionInterface
187
     *
188
     * @param string $class
189
     *
190
     * @return string
191
     */
192
    private function checkExtension($class)
193
    {
194 10
        if (! is_subclass_of($class, EngineExtensionInterface::class)) {
195
            $name = TemplateEngineInterface::class;
196 10
            throw new InvalidArgumentException(
197 10
                "Engine extension '{$class}' does not implement '{$name}'."
198
            );
199 10
        }
200 2
        return $class;
201 2
    }
202
}
203