Passed
Push — master ( d714b8...6e64d6 )
by Chauncey
02:15
created

AbstractLoader::clearDynamicTemplates()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
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
                $this->logger->warning(
73
                    'The $GLOBALS[\'widget_template\'] hack will be obsolete soon. Use setDynamicTemplate() instead.'
74
                );
75
                $ident = empty($GLOBALS['widget_template']) ? $ident : $GLOBALS['widget_template'];
76
                if (!is_string($ident)) {
77
                    throw new InvalidArgumentException(
78
                        'Dynamic template ident (from "$widget_template") must be a string'
79
                    );
80
                }
81
            }
82
        }
83
84
        $file = $this->findTemplateFile($ident);
85
        if ($file === null || $file === '') {
86
            return $ident;
87
        }
88
89
        return file_get_contents($file);
90
    }
91
92
    /**
93
     * @return string
94
     */
95
    protected function basePath()
96
    {
97
        return $this->basePath;
98
    }
99
100
    /**
101
     * @see FileLoader::path()
102
     * @return string[]
103
     */
104
    protected function paths()
105
    {
106
        return $this->paths;
107
    }
108
109
    /**
110
     * Get the template file (full path + filename) to load from an ident.
111
     *
112
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
113
     *
114
     * @param string $ident The template identifier to load.
115
     * @throws InvalidArgumentException If the template ident is not a string.
116
     * @return string|null The full path + filename of the found template. Null if nothing was found.
117
     */
118
    protected function findTemplateFile($ident)
119
    {
120
        if (!is_string($ident)) {
121
            throw new InvalidArgumentException(
122
                'Template ident must be a string'
123
            );
124
        }
125
126
        $filename = $this->filenameFromIdent($ident);
127
        $searchPath = $this->paths();
128
        foreach ($searchPath as $path) {
129
            $f = realpath($path).'/'.strtolower($filename);
130
            if (file_exists($f)) {
131
                return $f;
132
            }
133
        }
134
135
        return null;
136
    }
137
138
    /**
139
     * @param string $ident The template identifier to convert to a filename.
140
     * @return string
141
     */
142
    abstract protected function filenameFromIdent($ident);
143
144
    /**
145
     * @param string[] $paths The list of path to add.
146
     * @return LoaderInterface Chainable
147
     */
148
    private function setPaths(array $paths)
149
    {
150
        $this->paths = [];
151
152
        foreach ($paths as $path) {
153
            $this->addPath($path);
154
        }
155
156
        return $this;
157
    }
158
159
    /**
160
     * @param string $basePath The base path to set.
161
     * @throws InvalidArgumentException If the base path parameter is not a string.
162
     * @return LoaderInterface Chainable
163
     */
164
    private function setBasePath($basePath)
165
    {
166
        if (!is_string($basePath)) {
167
            throw new InvalidArgumentException(
168
                'Base path must be a string'
169
            );
170
        }
171
        $basePath = realpath($basePath);
172
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
173
        return $this;
174
    }
175
176
    /**
177
     * @param string $path The path to add to the load.
178
     * @return LoaderInterface Chainable
179
     */
180
    private function addPath($path)
181
    {
182
        $this->paths[] = $this->resolvePath($path);
183
184
        return $this;
185
    }
186
187
    /**
188
     * @param string $path The path to resolve.
189
     * @throws InvalidArgumentException If the path argument is not a string.
190
     * @return string
191
     */
192
    private function resolvePath($path)
193
    {
194
        if (!is_string($path)) {
195
            throw new InvalidArgumentException(
196
                'Path needs to be a string'
197
            );
198
        }
199
200
        $basePath = $this->basePath();
201
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
202
        if ($basePath && strpos($path, $basePath) === false) {
203
            $path = $basePath.$path;
204
        }
205
206
        return $path;
207
    }
208
209
    /**
210
     * @deprecated $GLOBALS['widget_template']
211
     * @param string      $varName       The name of the variable to set this template unto.
212
     * @param string|null $templateIdent The "dynamic template" to set or NULL to clear.
213
     * @throws InvalidArgumentException If var name is not a string
214
     *     or if the template is not a string (and not null).
215
     * @return void
216
     */
217
    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...
218
    {
219
        if (!is_string($varName)) {
220
            throw new InvalidArgumentException(
221
                'Can not set dynamic template: var name is not a string.'
222
            );
223
        }
224
225
        if ($templateIdent === null) {
226
            $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...
227
            return;
228
        }
229
230
        if (!is_string($templateIdent)) {
231
            throw new InvalidArgumentException(
232
                'Can not set dynamic template. Must be a a string, or null.'
233
            );
234
        }
235
236
        // Legacy dynamic template hack
237
        if ($varName === 'widget_template') {
238
            $GLOBALS['widget_template'] = $templateIdent;
239
        }
240
241
        $this->dynamicTemplates[$varName] = $templateIdent;
242
    }
243
244
    /**
245
     * @param string $varName The name of the variable to get template ident from.
246
     * @throws InvalidArgumentException If the var name is not a string.
247
     * @return string
248
     */
249
    public function dynamicTemplate($varName)
250
    {
251
        if (!is_string($varName)) {
252
            throw new InvalidArgumentException(
253
                'Can not get dynamic template: var name is not a string.'
254
            );
255
        }
256
257
        if (!isset($this->dynamicTemplates[$varName])) {
258
            return '';
259
        }
260
261
        return $this->dynamicTemplates[$varName];
262
    }
263
264
    /**
265
     * @deprecated $GLOBALS['widget_template']
266
     * @param string $varName The name of the variable to remove.
267
     * @throws InvalidArgumentException If var name is not a string.
268
     * @return void
269
     */
270
    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...
271
    {
272
        if (!is_string($varName)) {
273
            throw new InvalidArgumentException(
274
                'Can not set dynamic template: var name is not a string.'
275
            );
276
        }
277
278
        // Legacy dynamic template hack
279
        if ($varName === 'widget_template') {
280
            $GLOBALS['widget_template'] = null;
281
        }
282
283
        unset($this->dynamicTemplates[$varName]);
284
    }
285
286
    /**
287
     * @deprecated $GLOBALS['widget_template']
288
     * @return void
289
     */
290
    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...
291
    {
292
        // Legacy dynamic template hack
293
        $GLOBALS['widget_template'] = null;
294
295
        $this->dynamicTemplates = [];
296
    }
297
}
298