Passed
Push — master ( 11e596...ed648f )
by Gabriel
13:25
created

ThemeFolderResolveTemplatePath   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 245
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 29
eloc 57
c 0
b 0
f 0
dl 0
loc 245
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A prependPath() 0 8 2
A find() 0 12 4
A parseNamespacedName() 0 10 4
A findNamespacedView() 0 3 1
A __construct() 0 3 1
A addPath() 0 8 2
A getViewFilename() 0 3 1
A findInPaths() 0 13 3
A findRelativePathView() 0 4 1
A parseName() 0 7 2
A __invoke() 0 3 1
A hasNamespaceInformation() 0 3 1
A getPaths() 0 3 1
A setPaths() 0 8 3
A isRelativeView() 0 6 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Nip\View\ResolveTemplatePath;
5
6
use InvalidArgumentException;
7
use League\Plates\Engine;
8
use League\Plates\Exception\TemplateNotFound;
9
use League\Plates\Template\Name;
10
use League\Plates\Template\ResolveTemplatePath;
11
use Nip\View\Utilities\Backtrace;
12
13
/**
14
 * Class ViewFinder
15
 * @package Nip\View\ViewFinder
16
 */
17
class ThemeFolderResolveTemplatePath implements ResolveTemplatePath
18
{
19
    /**
20
     * Hint path delimiter value.
21
     *
22
     * @var string
23
     */
24
    public const HINT_PATH_DELIMITER = '::';
25
26
    /**
27
     * Identifier of the main namespace.
28
     *
29
     * @var string
30
     */
31
    public const MAIN_NAMESPACE = '__main__';
32
33
    /**
34
     * The array of active view paths.
35
     *
36
     * @var array
37
     */
38
    protected $paths = [];
39
40
    /**
41
     * @var string
42
     */
43
    protected $rootPath;
44
45
    /**
46
     * @var Engine
47
     */
48
    protected $engine;
49
50
    /**
51
     * ThemeFolderResolveTemplatePath constructor.
52
     * @param Engine $engine
53
     */
54
    public function __construct(Engine $engine)
55
    {
56
        $this->engine = $engine;
57
    }
58
59
    public function __invoke(Name $name): string
60
    {
61
        return $this->find($name->getName());
62
    }
63
64
    /**
65
     * Get the fully qualified location of the view.
66
     *
67
     * @param string $name
68
     * @return string
69
     */
70
    public function find($name)
71
    {
72
        if (is_file($name)) {
73
            return $name;
74
        }
75
        list($namespace, $view) = $this->parseName($name);
76
77
        if ($namespace == self::MAIN_NAMESPACE && $this->isRelativeView($view)) {
78
            return $this->findRelativePathView($view);
79
        }
80
81
        return $this->findNamespacedView($view, $namespace);
82
    }
83
84
    /**
85
     * Adds a path where templates are stored.
86
     *
87
     * @param string $path A path where to look for templates
88
     * @param string $namespace A path namespace
89
     *
90
     * @return void
91
     */
92
    public function addPath($path, $namespace = self::MAIN_NAMESPACE)
93
    {
94
        $this->paths[$namespace][] = rtrim($path, '/\\');
95
        if ($this->engine->getFolders()->exists($namespace)) {
96
            $this->engine->getFolders()->get($namespace)->setPath($path);
97
            return;
98
        }
99
        $this->engine->addFolder($namespace, $path);
100
    }
101
102
    /**
103
     * Prepends a path where templates are stored.
104
     *
105
     * @param string $path A path where to look for templates
106
     * @param string $namespace A path namespace
107
     * @return void
108
     */
109
    public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
110
    {
111
        $path = rtrim($path, '/\\');
112
113
        if (!isset($this->paths[$namespace])) {
114
            $this->paths[$namespace][] = $path;
115
        } else {
116
            array_unshift($this->paths[$namespace], $path);
117
        }
118
    }
119
120
    /**
121
     * Sets the paths where templates are stored.
122
     *
123
     * @param string|array $paths A path or an array of paths where to look for templates
124
     * @param string $namespace A path namespace
125
     */
126
    public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
127
    {
128
        if (!is_array($paths)) {
129
            $paths = [$paths];
130
        }
131
        $this->paths[$namespace] = [];
132
        foreach ($paths as $path) {
133
            $this->addPath($path, $namespace);
134
        }
135
    }
136
137
    /**
138
     * Get the path to a template with a relative path.
139
     *
140
     * @param string $name
141
     * @return string
142
     */
143
    protected function findRelativePathView($name)
144
    {
145
        $caller = Backtrace::getViewOrigin();
146
        return $this->findInPaths($name, [dirname($caller)]);
147
    }
148
149
    /**
150
     * Get the path to a template with a named path.
151
     *
152
     * @param string $name
153
     * @param $namespace
154
     * @return string
155
     */
156
    protected function findNamespacedView($name, $namespace)
157
    {
158
        return $this->findInPaths($name, $this->paths[$namespace]);
159
    }
160
161
    /**
162
     * @param $name
163
     * @param string $namespace
164
     * @return array
165
     */
166
    public function parseName($name, $namespace = self::MAIN_NAMESPACE)
167
    {
168
        $name = trim($name);
169
        if ($this->hasNamespaceInformation($name)) {
170
            return $this->parseNamespacedName($name);
171
        }
172
        return [$namespace, $name];
173
    }
174
175
    /**
176
     * Get the segments of a template with a named path.
177
     *
178
     * @param string $name
179
     * @return array
180
     *
181
     * @throws InvalidArgumentException
182
     */
183
    protected function parseNamespacedName($name)
184
    {
185
        $segments = explode(static::HINT_PATH_DELIMITER, $name);
186
        if (count($segments) != 2) {
187
            throw new InvalidArgumentException("View [$name] has an invalid name.");
188
        }
189
        if (!isset($this->paths[$segments[0]]) || count($this->paths[$segments[0]]) < 1) {
190
            throw new InvalidArgumentException("No path defined for namespace [{$segments[0]}].");
191
        }
192
        return $segments;
193
    }
194
195
    /**
196
     * @param $name
197
     * @return bool
198
     */
199
    public function isRelativeView($name): bool
200
    {
201
        if (!is_string($name)) {
202
            return false;
203
        }
204
        return substr($name, 0, 1) !== '/';
205
    }
206
207
    /**
208
     * Find the given view in the list of paths.
209
     *
210
     * @param string $name
211
     * @param array $paths
212
     * @return string
213
     *
214
     * @throws InvalidArgumentException
215
     */
216
    protected function findInPaths($name, $paths)
217
    {
218
        foreach ((array)$paths as $path) {
219
            $file = $this->getViewFilename($name);
220
            $viewPath = $path . '/' . $file;
221
            if (file_exists($viewPath)) {
222
                return $viewPath;
223
            }
224
        }
225
        throw new TemplateNotFound(
226
            $name,
227
            $paths,
228
            'View [' . $name . '] not found in paths [' . implode(', ', $paths) . '].'
229
        );
230
    }
231
232
    /**
233
     * Get an view file name with extension.
234
     *
235
     * @param string $name
236
     * @return string
237
     */
238
    protected function getViewFilename($name)
239
    {
240
        return $name . '.php';
241
    }
242
243
    /**
244
     * Returns whether or not the view name has any hint information.
245
     *
246
     * @param string $name
247
     * @return bool
248
     */
249
    public function hasNamespaceInformation($name)
250
    {
251
        return strpos($name, static::HINT_PATH_DELIMITER) > 0;
252
    }
253
254
    /**
255
     * Get the active view paths.
256
     *
257
     * @return array
258
     */
259
    public function getPaths()
260
    {
261
        return $this->paths;
262
    }
263
}
264