Passed
Push — master ( b912aa...9d745d )
by Mathieu
01:50
created

AbstractLoader::isTemplateString()   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\LoaderInterface;
13
14
/**
15
 * Base Template Loader
16
 *
17
 * Finds a template file in a collection of directory paths.
18
 */
19
abstract class AbstractLoader implements
20
    LoggerAwareInterface,
21
    LoaderInterface
22
{
23
    use LoggerAwareTrait;
24
25
    /**
26
     * @var string
27
     */
28
    private $basePath = '';
29
30
    /**
31
     * @var string[]
32
     */
33
    private $paths = [];
34
35
    /**
36
     * @var array
37
     */
38
    private $dynamicTemplates = [];
39
40
    /**
41
     * Default constructor, if none is provided by the concrete class implementations.
42
     *
43
     * ## Required dependencies
44
     * - `logger` A PSR-3 logger
45
     *
46
     * @param array $data The class dependencies map.
47
     */
48
    public function __construct(array $data = null)
49
    {
50
        $this->setLogger($data['logger']);
51
        $this->setBasePath($data['base_path']);
52
        $this->setPaths($data['paths']);
53
    }
54
55
    /**
56
     * Load a template content
57
     *
58
     * @deprecated $GLOBALS['widget_template']
59
     *
60
     * @param  string $ident The template ident to load and render.
61
     * @throws InvalidArgumentException If the dynamic template identifier is not a string.
62
     * @return string
63
     */
64
    public function load($ident)
65
    {
66
        // Handle dynamic template
67
        if (substr($ident, 0, 1) === '$') {
68
            $tryLegacy = ($ident === '$widget_template');
69
70
            $ident = $this->dynamicTemplate(substr($ident, 1));
71
72
            // Legacy dynamic template hack
73
            if ($tryLegacy) {
74
                $ident = empty($GLOBALS['widget_template']) ? $ident : $GLOBALS['widget_template'];
75
                $this->logger->warning(sprintf(
76
                    '%s is deprecated in favor of %s: %s',
77
                    '$GLOBALS[\'widget_template\']',
78
                    'setDynamicTemplate()',
79
                    $ident
80
                ));
81
                if (!is_string($ident)) {
82
                    throw new InvalidArgumentException(
83
                        'Dynamic template ident (from "$widget_template") must be a string'
84
                    );
85
                }
86
            }
87
        }
88
89
        /**
90
         * Prevents the loader from passing a proper template through further
91
         * procedures meant for a template identifier.
92
         */
93
        if ($this->isTemplateString($ident)) {
94
            return $ident;
95
        }
96
97
        $file = $this->findTemplateFile($ident);
98
        if ($file === null || $file === '') {
99
            return $ident;
100
        }
101
102
        return file_get_contents($file);
103
    }
104
105
    /**
106
     * @param  string $varName The name of the variable to get template ident from.
107
     * @throws InvalidArgumentException If the var name is not a string.
108
     * @return string
109
     */
110
    public function dynamicTemplate($varName)
111
    {
112
        if (!is_string($varName)) {
113
            throw new InvalidArgumentException(
114
                'Can not get dynamic template: var name is not a string.'
115
            );
116
        }
117
118
        if (!isset($this->dynamicTemplates[$varName])) {
119
            return '';
120
        }
121
122
        return $this->dynamicTemplates[$varName];
123
    }
124
125
    /**
126
     * @deprecated $GLOBALS['widget_template']
127
     *
128
     * @param  string      $varName       The name of the variable to set this template unto.
129
     * @param  string|null $templateIdent The "dynamic template" to set or NULL to clear.
130
     * @throws InvalidArgumentException If var name is not a string
131
     *     or if the template is not a string (and not null).
132
     * @return void
133
     */
134
    public function setDynamicTemplate($varName, $templateIdent)
135
    {
136
        if (!is_string($varName)) {
137
            throw new InvalidArgumentException(
138
                'Can not set dynamic template: var name is not a string.'
139
            );
140
        }
141
142
        if ($templateIdent === null) {
143
            $this->removeDynamicTemplate($varName);
0 ignored issues
show
Deprecated Code introduced by
The method Charcoal\View\AbstractLo...removeDynamicTemplate() has been deprecated with message: $GLOBALS['widget_template']

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
144
            return;
145
        }
146
147
        if (!is_string($templateIdent)) {
148
            throw new InvalidArgumentException(
149
                'Can not set dynamic template. Must be a a string, or null.'
150
            );
151
        }
152
153
        // Legacy dynamic template hack
154
        if ($varName === 'widget_template') {
155
            $GLOBALS['widget_template'] = $templateIdent;
156
        }
157
158
        $this->dynamicTemplates[$varName] = $templateIdent;
159
    }
160
161
    /**
162
     * @deprecated $GLOBALS['widget_template']
163
     *
164
     * @param  string $varName The name of the variable to remove.
165
     * @throws InvalidArgumentException If var name is not a string.
166
     * @return void
167
     */
168
    public function removeDynamicTemplate($varName)
169
    {
170
        if (!is_string($varName)) {
171
            throw new InvalidArgumentException(
172
                'Can not set dynamic template: var name is not a string.'
173
            );
174
        }
175
176
        // Legacy dynamic template hack
177
        if ($varName === 'widget_template') {
178
            $GLOBALS['widget_template'] = null;
179
        }
180
181
        unset($this->dynamicTemplates[$varName]);
182
    }
183
184
    /**
185
     * @deprecated $GLOBALS['widget_template']
186
     *
187
     * @return void
188
     */
189
    public function clearDynamicTemplates()
190
    {
191
        // Legacy dynamic template hack
192
        $GLOBALS['widget_template'] = null;
193
194
        $this->dynamicTemplates = [];
195
    }
196
197
    /**
198
     * @return string
199
     */
200
    protected function basePath()
201
    {
202
        return $this->basePath;
203
    }
204
205
    /**
206
     * @param  string $basePath The base path to set.
207
     * @throws InvalidArgumentException If the base path parameter is not a string.
208
     * @return LoaderInterface Chainable
209
     */
210
    private function setBasePath($basePath)
211
    {
212
        if (!is_string($basePath)) {
213
            throw new InvalidArgumentException(
214
                'Base path must be a string'
215
            );
216
        }
217
        $basePath = realpath($basePath);
218
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
219
        return $this;
220
    }
221
222
    /**
223
     * @return string[]
224
     */
225
    protected function paths()
226
    {
227
        return $this->paths;
228
    }
229
230
    /**
231
     * @param  string[] $paths The list of path to add.
232
     * @return LoaderInterface Chainable
233
     */
234
    private function setPaths(array $paths)
235
    {
236
        $this->paths = [];
237
238
        foreach ($paths as $path) {
239
            $this->addPath($path);
240
        }
241
242
        return $this;
243
    }
244
245
    /**
246
     * @param  string $path The path to add to the load.
247
     * @return LoaderInterface Chainable
248
     */
249
    private function addPath($path)
250
    {
251
        $this->paths[] = $this->resolvePath($path);
252
253
        return $this;
254
    }
255
256
    /**
257
     * @param  string $path The path to resolve.
258
     * @throws InvalidArgumentException If the path argument is not a string.
259
     * @return string
260
     */
261
    private function resolvePath($path)
262
    {
263
        if (!is_string($path)) {
264
            throw new InvalidArgumentException(
265
                'Path needs to be a string'
266
            );
267
        }
268
269
        $basePath = $this->basePath();
270
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
271
        if ($basePath && strpos($path, $basePath) === false) {
272
            $path = $basePath.$path;
273
        }
274
275
        return $path;
276
    }
277
278
    /**
279
     * Determine if the variable is a template literal.
280
     *
281
     * This method looks for any line-breaks in the given string,
282
     * which a file path would not allow.
283
     *
284
     * @param  string $ident The template being evaluated.
285
     * @return boolean Returns TRUE if the given value is most likely the template contents
286
     *     as opposed to a template identifier (file path).
287
     */
288
    protected function isTemplateString($ident)
289
    {
290
        return strpos($ident, PHP_EOL) !== false;
291
    }
292
293
    /**
294
     * Get the template file (full path + filename) to load from an ident.
295
     *
296
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
297
     *
298
     * @param  string $ident The template identifier to load.
299
     * @throws InvalidArgumentException If the template ident is not a string.
300
     * @return string|null The full path + filename of the found template. Null if nothing was found.
301
     */
302
    protected function findTemplateFile($ident)
303
    {
304
        if (!is_string($ident)) {
305
            throw new InvalidArgumentException(sprintf(
306
                'Template ident must be a string, received %s',
307
                is_object($ident) ? get_class($ident) : gettype($ident)
308
            ));
309
        }
310
311
        $filename = $this->filenameFromIdent($ident);
312
        $searchPath = $this->paths();
313
        foreach ($searchPath as $path) {
314
            $f = realpath($path).'/'.strtolower($filename);
315
            if (file_exists($f)) {
316
                return $f;
317
            }
318
        }
319
320
        return null;
321
    }
322
323
    /**
324
     * @param  string $ident The template identifier to convert to a filename.
325
     * @return string
326
     */
327
    abstract protected function filenameFromIdent($ident);
328
}
329