Passed
Pull Request — master (#2)
by Chauncey
02:01
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
abstract class AbstractLoader implements
18
    LoggerAwareInterface,
19
    LoaderInterface
20
{
21
    use LoggerAwareTrait;
22
23
    /**
24
     * @var string
25
     */
26
    private $basePath = '';
27
28
    /**
29
     * @var string[]
30
     */
31
    private $paths = [];
32
33
    /**
34
     * @var array
35
     */
36
    private $dynamicTemplates = [];
37
38
    /**
39
     * Default constructor, if none is provided by the concrete class implementations.
40
     *
41
     * ## Required dependencies
42
     * - `logger` A PSR-3 logger
43
     *
44
     * @param array $data The class dependencies map.
45
     */
46
    public function __construct(array $data = null)
47
    {
48
        $this->setLogger($data['logger']);
49
        $this->setBasePath($data['base_path']);
50
        $this->setPaths($data['paths']);
51
    }
52
53
    /**
54
     * Load a template content
55
     *
56
     * @deprecated $GLOBALS['widget_template']
57
     * @param  string $ident The template ident to load and render.
58
     * @throws InvalidArgumentException If the dynamic template identifier is not a string.
59
     * @return string
60
     */
61
    public function load($ident)
62
    {
63
        // Handle dynamic template
64
        if (substr($ident, 0, 1) === '$') {
65
            $tryLegacy = ($ident === '$widget_template');
66
67
            $ident = $this->dynamicTemplate(substr($ident, 1));
68
69
            // Legacy dynamic template hack
70
            if ($tryLegacy) {
71
                $ident = empty($GLOBALS['widget_template']) ? $ident : $GLOBALS['widget_template'];
72
                $this->logger->warning(sprintf(
73
                    '%s is deprecated in favor of %s: %s',
74
                    '$GLOBALS[\'widget_template\']',
75
                    'setDynamicTemplate()',
76
                    $ident
77
                ));
78
                if (!is_string($ident)) {
79
                    throw new InvalidArgumentException(
80
                        'Dynamic template ident (from "$widget_template") must be a string'
81
                    );
82
                }
83
            }
84
        }
85
86
        /**
87
         * Prevents the loader from passing a proper template through further
88
         * procedures meant for a template identifier.
89
         */
90
        if ($this->isTemplateString($ident)) {
91
            return $ident;
92
        }
93
94
        $file = $this->findTemplateFile($ident);
95
        if ($file === null || $file === '') {
96
            return $ident;
97
        }
98
99
        return file_get_contents($file);
100
    }
101
102
    /**
103
     * @deprecated $GLOBALS['widget_template']
104
     * @param string      $varName       The name of the variable to set this template unto.
105
     * @param string|null $templateIdent The "dynamic template" to set or NULL to clear.
106
     * @throws InvalidArgumentException If var name is not a string
107
     *     or if the template is not a string (and not null).
108
     * @return void
109
     */
110
    public function setDynamicTemplate($varName, $templateIdent)
111
    {
112
        if (!is_string($varName)) {
113
            throw new InvalidArgumentException(
114
                'Can not set dynamic template: var name is not a string.'
115
            );
116
        }
117
118
        if ($templateIdent === null) {
119
            $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...
120
            return;
121
        }
122
123
        if (!is_string($templateIdent)) {
124
            throw new InvalidArgumentException(
125
                'Can not set dynamic template. Must be a a string, or null.'
126
            );
127
        }
128
129
        // Legacy dynamic template hack
130
        if ($varName === 'widget_template') {
131
            $GLOBALS['widget_template'] = $templateIdent;
132
        }
133
134
        $this->dynamicTemplates[$varName] = $templateIdent;
135
    }
136
137
    /**
138
     * @param string $varName The name of the variable to get template ident from.
139
     * @throws InvalidArgumentException If the var name is not a string.
140
     * @return string
141
     */
142
    public function dynamicTemplate($varName)
143
    {
144
        if (!is_string($varName)) {
145
            throw new InvalidArgumentException(
146
                'Can not get dynamic template: var name is not a string.'
147
            );
148
        }
149
150
        if (!isset($this->dynamicTemplates[$varName])) {
151
            return '';
152
        }
153
154
        return $this->dynamicTemplates[$varName];
155
    }
156
157
    /**
158
     * @deprecated $GLOBALS['widget_template']
159
     * @param string $varName The name of the variable to remove.
160
     * @throws InvalidArgumentException If var name is not a string.
161
     * @return void
162
     */
163
    public function removeDynamicTemplate($varName)
164
    {
165
        if (!is_string($varName)) {
166
            throw new InvalidArgumentException(
167
                'Can not set dynamic template: var name is not a string.'
168
            );
169
        }
170
171
        // Legacy dynamic template hack
172
        if ($varName === 'widget_template') {
173
            $GLOBALS['widget_template'] = null;
174
        }
175
176
        unset($this->dynamicTemplates[$varName]);
177
    }
178
179
    /**
180
     * @deprecated $GLOBALS['widget_template']
181
     * @return void
182
     */
183
    public function clearDynamicTemplates()
184
    {
185
        // Legacy dynamic template hack
186
        $GLOBALS['widget_template'] = null;
187
188
        $this->dynamicTemplates = [];
189
    }
190
191
    /**
192
     * @return string
193
     */
194
    protected function basePath()
195
    {
196
        return $this->basePath;
197
    }
198
199
    /**
200
     * @return string[]
201
     */
202
    protected function paths()
203
    {
204
        return $this->paths;
205
    }
206
207
    /**
208
     * Determine if the variable is a template literal.
209
     *
210
     * This method looks for any line-breaks in the given string,
211
     * which a file path would not allow.
212
     *
213
     * @param  string $ident The template being evaluated.
214
     * @return boolean Returns TRUE if the given value is most likely the template contents
215
     *     as opposed to a template identifier (file path).
216
     */
217
    protected function isTemplateString($ident)
218
    {
219
        return strpos($ident, PHP_EOL) !== false;
220
    }
221
222
    /**
223
     * Get the template file (full path + filename) to load from an ident.
224
     *
225
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
226
     *
227
     * @param string $ident The template identifier to load.
228
     * @throws InvalidArgumentException If the template ident is not a string.
229
     * @return string|null The full path + filename of the found template. Null if nothing was found.
230
     */
231
    protected function findTemplateFile($ident)
232
    {
233
        if (!is_string($ident)) {
234
            throw new InvalidArgumentException(sprintf(
235
                'Template ident must be a string, received %s',
236
                is_object($ident) ? get_class($ident) : gettype($ident)
237
            ));
238
        }
239
240
        $filename = $this->filenameFromIdent($ident);
241
        $searchPath = $this->paths();
242
        foreach ($searchPath as $path) {
243
            $f = realpath($path).'/'.strtolower($filename);
244
            if (file_exists($f)) {
245
                return $f;
246
            }
247
        }
248
249
        return null;
250
    }
251
252
    /**
253
     * @param string $ident The template identifier to convert to a filename.
254
     * @return string
255
     */
256
    abstract protected function filenameFromIdent($ident);
257
258
    /**
259
     * @param string[] $paths The list of path to add.
260
     * @return LoaderInterface Chainable
261
     */
262
    private function setPaths(array $paths)
263
    {
264
        $this->paths = [];
265
266
        foreach ($paths as $path) {
267
            $this->addPath($path);
268
        }
269
270
        return $this;
271
    }
272
273
    /**
274
     * @param string $basePath The base path to set.
275
     * @throws InvalidArgumentException If the base path parameter is not a string.
276
     * @return LoaderInterface Chainable
277
     */
278
    private function setBasePath($basePath)
279
    {
280
        if (!is_string($basePath)) {
281
            throw new InvalidArgumentException(
282
                'Base path must be a string'
283
            );
284
        }
285
        $basePath = realpath($basePath);
286
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
287
        return $this;
288
    }
289
290
    /**
291
     * @param string $path The path to add to the load.
292
     * @return LoaderInterface Chainable
293
     */
294
    private function addPath($path)
295
    {
296
        $this->paths[] = $this->resolvePath($path);
297
298
        return $this;
299
    }
300
301
    /**
302
     * @param string $path The path to resolve.
303
     * @throws InvalidArgumentException If the path argument is not a string.
304
     * @return string
305
     */
306
    private function resolvePath($path)
307
    {
308
        if (!is_string($path)) {
309
            throw new InvalidArgumentException(
310
                'Path needs to be a string'
311
            );
312
        }
313
314
        $basePath = $this->basePath();
315
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
316
        if ($basePath && strpos($path, $basePath) === false) {
317
            $path = $basePath.$path;
318
        }
319
320
        return $path;
321
    }
322
}
323