Passed
Pull Request — master (#7)
by Chauncey
02:26
created

AbstractLoader::hasDynamicTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Charcoal\View;
4
5
use InvalidArgumentException;
6
7
// From PSR-3
8
use Psr\Log\LoggerAwareInterface;
9
use Psr\Log\LoggerAwareTrait;
10
11
// From 'charcoal-view'
12
use Charcoal\View\GenericTemplateRegistry;
13
use Charcoal\View\LoaderInterface;
14
use Charcoal\View\LoaderRegistryInterface;
15
16
/**
17
 * Base Template Loader
18
 *
19
 * Finds a template file in a collection of directory paths.
20
 */
21
abstract class AbstractLoader implements
22
    LoggerAwareInterface,
23
    LoaderInterface
24
{
25
    use LoggerAwareTrait;
26
27
    /**
28
     * @var string
29
     */
30
    private $basePath = '';
31
32
    /**
33
     * @var string[]
34
     */
35
    private $paths = [];
36
37
    /**
38
     * @var boolean
39
     */
40
    private $silent = true;
41
42
    /**
43
     * @var LoaderRegistryInterface
44
     */
45
    private $templateRegistry;
46
47
    /**
48
     * Default constructor, if none is provided by the concrete class implementations.
49
     *
50
     * ## Required dependencies
51
     * - `logger` A PSR-3 logger
52
     *
53
     * @param array $data The class dependencies map.
54
     */
55
    public function __construct(array $data = null)
56
    {
57
        $this->setLogger($data['logger']);
58
        $this->setBasePath($data['base_path']);
59
        $this->setPaths($data['paths']);
60
61
        if (!isset($data['registry'])) {
62
            $data['registry'] = new GenericTemplateRegistry();
63
        }
64
65
        $this->setTemplateRegistry($data['registry']);
66
67
        if (isset($data['silent'])) {
68
            $this->setSilent($data['silent']);
69
        }
70
    }
71
72
    /**
73
     * Load a template content
74
     *
75
     * @param  string $ident The template ident to load and render.
76
     * @return string
77
     */
78
    public function load($ident)
79
    {
80
        // Bail early
81
        if ($ident === null || $ident === '') {
82
            return (string)$ident;
83
        }
84
85
        // Handle dynamic templates
86
        $resolved = $this->resolve($ident);
87
        if ($resolved === null || $resolved === '') {
88
            return $ident;
89
        }
90
91
        $file = $this->findTemplateFile($resolved);
92
        if ($file === null || $file === '') {
93
            return $ident;
94
        }
95
96
        return file_get_contents($file);
97
    }
98
99
    /**
100
     * Converts a dynamic template identifer into a template path.
101
     *
102
     * @param  string $ident The template key.
103
     * @throws InvalidArgumentException If the legacy dynamic template is empty.
104
     * @return string|null The template path or the template key.
105
     */
106
    public function resolve($ident)
107
    {
108
        if ($this->templateRegistry === null) {
109
            return $ident;
110
        }
111
112
        if (substr($ident, 0, 1) !== '$') {
113
            return $ident;
114
        }
115
116
        /** @deprecated 0.3 */
117
        if ($ident === '$widget_template') {
118
            if (empty($GLOBALS['widget_template'])) {
119
                if ($this->silent() === false) {
120
                    throw new InvalidArgumentException(
121
                        sprintf('Dynamic template "%s" does not contain a view.', $ident)
122
                    );
123
                }
124
125
                $this->logger->warning(sprintf(
126
                    '%s is deprecated in favor of %s::setDynamicTemplate()',
127
                    '$GLOBALS[\'widget_template\']',
128
                    get_called_class()
129
                ));
130
131
                return '';
132
            }
133
134
            $ident = $GLOBALS['widget_template'];
135
136
            $this->logger->warning(sprintf(
137
                '%s ("%s") is deprecated in favor of %s::setDynamicTemplate()',
138
                '$GLOBALS[\'widget_template\']',
139
                $ident,
140
                get_called_class()
141
            ));
142
143
            return $ident;
144
        }
145
146
        return $this->templateRegistry()->get(substr($ident, 1));
147
    }
148
149
    /**
150
     * @param  string $varName The name of the variable to get template ident from.
151
     * @return string|null Returns the template ident or NULL if no template found.
152
     */
153
    public function dynamicTemplate($varName)
154
    {
155
        return $this->templateRegistry()->get($varName);
156
    }
157
158
    /**
159
     * @param  string $varName The name of the variable to get template ident from.
160
     * @return boolean
161
     */
162
    public function hasDynamicTemplate($varName)
163
    {
164
        return $this->templateRegistry()->has($varName);
165
    }
166
167
    /**
168
     * @param  string      $varName       The name of the variable to set this template unto.
169
     * @param  string|null $templateIdent The "dynamic template" to set or NULL to clear.
170
     * @return void
171
     */
172
    public function setDynamicTemplate($varName, $templateIdent)
173
    {
174
        if ($templateIdent === null) {
175
            $this->removeDynamicTemplate($varName);
176
            return;
177
        }
178
179
        /** @deprecated 0.3 */
180
        if ($varName === 'widget_template') {
181
            $GLOBALS['widget_template'] = $templateIdent;
182
        }
183
184
        $this->templateRegistry()->set($varName, $templateIdent);
185
    }
186
187
    /**
188
     * @param  string $varName The name of the variable to remove.
189
     * @return void
190
     */
191
    public function removeDynamicTemplate($varName)
192
    {
193
        /** @deprecated 0.3 */
194
        if ($varName === 'widget_template') {
195
            $GLOBALS['widget_template'] = null;
196
        }
197
198
        $this->templateRegistry()->remove($varName);
199
    }
200
201
    /**
202
     * @return void
203
     */
204
    public function clearDynamicTemplates()
205
    {
206
        /** @deprecated 0.3 */
207
        $GLOBALS['widget_template'] = null;
208
209
        $this->templateRegistry()->clear();
210
    }
211
212
    /**
213
     * @return string
214
     */
215
    protected function basePath()
216
    {
217
        return $this->basePath;
218
    }
219
220
    /**
221
     * @param  string $basePath The base path to set.
222
     * @throws InvalidArgumentException If the base path parameter is not a string.
223
     * @return LoaderInterface Chainable
224
     */
225
    private function setBasePath($basePath)
226
    {
227
        if (!is_string($basePath)) {
228
            throw new InvalidArgumentException(
229
                'Base path must be a string'
230
            );
231
        }
232
        $basePath = realpath($basePath);
233
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
234
        return $this;
235
    }
236
237
    /**
238
     * @return string[]
239
     */
240
    protected function paths()
241
    {
242
        return $this->paths;
243
    }
244
245
    /**
246
     * @param  string[] $paths The list of path to add.
247
     * @return LoaderInterface Chainable
248
     */
249
    private function setPaths(array $paths)
250
    {
251
        $this->paths = [];
252
253
        foreach ($paths as $path) {
254
            $this->addPath($path);
255
        }
256
257
        return $this;
258
    }
259
260
    /**
261
     * @param  string $path The path to add to the load.
262
     * @return LoaderInterface Chainable
263
     */
264
    private function addPath($path)
265
    {
266
        $this->paths[] = $this->resolvePath($path);
267
268
        return $this;
269
    }
270
271
    /**
272
     * @param  string $path The path to resolve.
273
     * @throws InvalidArgumentException If the path argument is not a string.
274
     * @return string
275
     */
276
    private function resolvePath($path)
277
    {
278
        if (!is_string($path)) {
279
            throw new InvalidArgumentException(
280
                'Path needs to be a string'
281
            );
282
        }
283
284
        $basePath = $this->basePath();
285
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
286
        if ($basePath && strpos($path, $basePath) === false) {
287
            $path = $basePath.$path;
288
        }
289
290
        return $path;
291
    }
292
293
    /**
294
     * Get error reporting.
295
     *
296
     * @return boolean Whether to be quiet when an error occurs.
297
     */
298
    public function silent()
299
    {
300
        return $this->silent;
301
    }
302
303
    /**
304
     * Set error reporting.
305
     *
306
     * @param  boolean $silent Whether to be quiet when an error occurs.
307
     * @return void
308
     */
309
    private function setSilent($silent)
310
    {
311
        $this->silent = (bool)$silent;
312
    }
313
314
    /**
315
     * @return LoaderRegistryInterface
316
     */
317
    public function templateRegistry()
318
    {
319
        return $this->templateRegistry;
320
    }
321
322
    /**
323
     * @param  LoaderRegistryInterface $registry A loader registry instance.
324
     * @return void
325
     */
326
    private function setTemplateRegistry(LoaderRegistryInterface $registry)
327
    {
328
        $this->templateRegistry = $registry;
329
    }
330
331
    /**
332
     * Get the template file (full path + filename) to load from an ident.
333
     *
334
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
335
     *
336
     * @param  string $ident The template identifier to load.
337
     * @throws InvalidArgumentException If the template ident is not a string.
338
     * @return string|null The full path + filename of the found template. Null if nothing was found.
339
     */
340
    protected function findTemplateFile($ident)
341
    {
342
        if (!is_string($ident)) {
343
            throw new InvalidArgumentException(sprintf(
344
                'Template ident must be a string, received %s',
345
                is_object($ident) ? get_class($ident) : gettype($ident)
346
            ));
347
        }
348
349
        $filename = $this->filenameFromIdent($ident);
350
        $searchPath = $this->paths();
351
        foreach ($searchPath as $path) {
352
            $f = realpath($path).'/'.strtolower($filename);
353
            if (file_exists($f)) {
354
                return $f;
355
            }
356
        }
357
358
        return null;
359
    }
360
361
    /**
362
     * @param  string $ident The template identifier to convert to a filename.
363
     * @return string
364
     */
365
    abstract protected function filenameFromIdent($ident);
366
367
    /**
368
     * Handle when a template is not found.
369
     *
370
     * @param  string $ident The template ident.
371
     * @return string
372
     */
373
    abstract protected function handleNotFound($ident);
374
}
375