ThemeFolderResolveTemplatePath   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 29
eloc 54
c 0
b 0
f 0
dl 0
loc 249
rs 10

15 Methods

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