x Sorry, these patches are not available anymore due to data migration. Please run a fresh inspection.
Passed
Push — master ( 7f0819...d3db1e )
by Chauncey
02:20
created

AbstractLoader::findTemplateFile()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 12
nc 4
nop 1
1
<?php
2
3
namespace Charcoal\View;
4
5
// PHP Dependencies
6
use InvalidArgumentException;
7
8
// PSR-3 (logger) dependencies
9
use Psr\Log\LoggerAwareInterface;
10
use Psr\Log\LoggerAwareTrait;
11
12
// Local namespace dependencies
13
use Charcoal\View\LoaderInterface;
14
15
/**
16
 * Base template loader.
17
 */
18
abstract class AbstractLoader implements
19
    LoggerAwareInterface,
20
    LoaderInterface
21
{
22
    use LoggerAwareTrait;
23
24
    /**
25
     * @var string
26
     */
27
    private $basePath = '';
28
29
    /**
30
     * @var string[]
31
     */
32
    private $paths = [];
33
34
    /**
35
     * @var array
36
     */
37
    private $dynamicTemplates = [];
38
39
    /**
40
     * Default constructor, if none is provided by the concrete class implementations.
41
     *
42
     * ## Required dependencies
43
     * - `logger` A PSR-3 logger
44
     *
45
     * @param array $data The class dependencies map.
46
     */
47
    public function __construct(array $data = null)
48
    {
49
        $this->setLogger($data['logger']);
50
        $this->setBasePath($data['base_path']);
51
        $this->setPaths($data['paths']);
52
    }
53
54
    /**
55
     * Load a template content
56
     *
57
     * @deprecated $GLOBALS['widget_template']
58
     * @param  string $ident The template ident to load and render.
59
     * @throws InvalidArgumentException If the dynamic template identifier is not a string.
60
     * @return string
61
     */
62
    public function load($ident)
0 ignored issues
show
Coding Style introduced by
load uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
63
    {
64
        // Handle dynamic template
65
        if (substr($ident, 0, 1) === '$') {
66
            $tryLegacy = ($ident === '$widget_template');
67
68
            $ident = $this->dynamicTemplate(substr($ident, 1));
69
70
            // Legacy dynamic template hack
71
            if ($tryLegacy) {
72
                $ident = empty($GLOBALS['widget_template']) ? $ident : $GLOBALS['widget_template'];
73
                $this->logger->warning(sprintf(
74
                    '%s is deprecated in favor of %s: %s',
75
                    '$GLOBALS[\'widget_template\']',
76
                    'setDynamicTemplate()',
77
                    $ident
78
                ));
79
                if (!is_string($ident)) {
80
                    throw new InvalidArgumentException(
81
                        'Dynamic template ident (from "$widget_template") must be a string'
82
                    );
83
                }
84
            }
85
        }
86
87
        $file = $this->findTemplateFile($ident);
88
        if ($file === null || $file === '') {
89
            return $ident;
90
        }
91
92
        return file_get_contents($file);
93
    }
94
95
    /**
96
     * @deprecated $GLOBALS['widget_template']
97
     * @param string      $varName       The name of the variable to set this template unto.
98
     * @param string|null $templateIdent The "dynamic template" to set or NULL to clear.
99
     * @throws InvalidArgumentException If var name is not a string
100
     *     or if the template is not a string (and not null).
101
     * @return void
102
     */
103
    public function setDynamicTemplate($varName, $templateIdent)
0 ignored issues
show
Coding Style introduced by
setDynamicTemplate uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
104
    {
105
        if (!is_string($varName)) {
106
            throw new InvalidArgumentException(
107
                'Can not set dynamic template: var name is not a string.'
108
            );
109
        }
110
111
        if ($templateIdent === null) {
112
            $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...
113
            return;
114
        }
115
116
        if (!is_string($templateIdent)) {
117
            throw new InvalidArgumentException(
118
                'Can not set dynamic template. Must be a a string, or null.'
119
            );
120
        }
121
122
        // Legacy dynamic template hack
123
        if ($varName === 'widget_template') {
124
            $GLOBALS['widget_template'] = $templateIdent;
125
        }
126
127
        $this->dynamicTemplates[$varName] = $templateIdent;
128
    }
129
130
    /**
131
     * @param string $varName The name of the variable to get template ident from.
132
     * @throws InvalidArgumentException If the var name is not a string.
133
     * @return string
134
     */
135
    public function dynamicTemplate($varName)
136
    {
137
        if (!is_string($varName)) {
138
            throw new InvalidArgumentException(
139
                'Can not get dynamic template: var name is not a string.'
140
            );
141
        }
142
143
        if (!isset($this->dynamicTemplates[$varName])) {
144
            return '';
145
        }
146
147
        return $this->dynamicTemplates[$varName];
148
    }
149
150
    /**
151
     * @deprecated $GLOBALS['widget_template']
152
     * @param string $varName The name of the variable to remove.
153
     * @throws InvalidArgumentException If var name is not a string.
154
     * @return void
155
     */
156
    public function removeDynamicTemplate($varName)
0 ignored issues
show
Coding Style introduced by
removeDynamicTemplate uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
157
    {
158
        if (!is_string($varName)) {
159
            throw new InvalidArgumentException(
160
                'Can not set dynamic template: var name is not a string.'
161
            );
162
        }
163
164
        // Legacy dynamic template hack
165
        if ($varName === 'widget_template') {
166
            $GLOBALS['widget_template'] = null;
167
        }
168
169
        unset($this->dynamicTemplates[$varName]);
170
    }
171
172
    /**
173
     * @deprecated $GLOBALS['widget_template']
174
     * @return void
175
     */
176
    public function clearDynamicTemplates()
0 ignored issues
show
Coding Style introduced by
clearDynamicTemplates uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
177
    {
178
        // Legacy dynamic template hack
179
        $GLOBALS['widget_template'] = null;
180
181
        $this->dynamicTemplates = [];
182
    }
183
184
    /**
185
     * @return string
186
     */
187
    protected function basePath()
188
    {
189
        return $this->basePath;
190
    }
191
192
    /**
193
     * @return string[]
194
     */
195
    protected function paths()
196
    {
197
        return $this->paths;
198
    }
199
200
    /**
201
     * Get the template file (full path + filename) to load from an ident.
202
     *
203
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
204
     *
205
     * @param string $ident The template identifier to load.
206
     * @throws InvalidArgumentException If the template ident is not a string.
207
     * @return string|null The full path + filename of the found template. Null if nothing was found.
208
     */
209
    protected function findTemplateFile($ident)
210
    {
211
        if (!is_string($ident)) {
212
            throw new InvalidArgumentException(sprintf(
213
                'Template ident must be a string, received %s',
214
                is_object($ident) ? get_class($ident) : gettype($ident)
215
            ));
216
        }
217
218
        $filename = $this->filenameFromIdent($ident);
219
        $searchPath = $this->paths();
220
        foreach ($searchPath as $path) {
221
            $f = realpath($path).'/'.strtolower($filename);
222
            if (file_exists($f)) {
223
                return $f;
224
            }
225
        }
226
227
        return null;
228
    }
229
230
    /**
231
     * @param string $ident The template identifier to convert to a filename.
232
     * @return string
233
     */
234
    abstract protected function filenameFromIdent($ident);
235
236
    /**
237
     * @param string[] $paths The list of path to add.
238
     * @return LoaderInterface Chainable
239
     */
240
    private function setPaths(array $paths)
241
    {
242
        $this->paths = [];
243
244
        foreach ($paths as $path) {
245
            $this->addPath($path);
246
        }
247
248
        return $this;
249
    }
250
251
    /**
252
     * @param string $basePath The base path to set.
253
     * @throws InvalidArgumentException If the base path parameter is not a string.
254
     * @return LoaderInterface Chainable
255
     */
256
    private function setBasePath($basePath)
257
    {
258
        if (!is_string($basePath)) {
259
            throw new InvalidArgumentException(
260
                'Base path must be a string'
261
            );
262
        }
263
        $basePath = realpath($basePath);
264
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
265
        return $this;
266
    }
267
268
    /**
269
     * @param string $path The path to add to the load.
270
     * @return LoaderInterface Chainable
271
     */
272
    private function addPath($path)
273
    {
274
        $this->paths[] = $this->resolvePath($path);
275
276
        return $this;
277
    }
278
279
    /**
280
     * @param string $path The path to resolve.
281
     * @throws InvalidArgumentException If the path argument is not a string.
282
     * @return string
283
     */
284
    private function resolvePath($path)
285
    {
286
        if (!is_string($path)) {
287
            throw new InvalidArgumentException(
288
                'Path needs to be a string'
289
            );
290
        }
291
292
        $basePath = $this->basePath();
293
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
294
        if ($basePath && strpos($path, $basePath) === false) {
295
            $path = $basePath.$path;
296
        }
297
298
        return $path;
299
    }
300
}
301