Completed
Push — master ( 2c71ef...178e64 )
by Mathieu
04:04
created

AbstractLoader::setDynamicTemplate()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 11

Duplication

Lines 21
Ratio 100 %

Importance

Changes 0
Metric Value
dl 21
loc 21
rs 9.0534
c 0
b 0
f 0
cc 4
eloc 11
nc 4
nop 2
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
     * @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)
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...
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 (!$ident && $tryLegacy) {
71
                $this->logger->warning('The GLOBALS $widget_template hack will be obsolete soon. Use setDynamicTemplate() instead.');
72
                $ident = (isset($GLOBALS['widget_template']) ? $GLOBALS['widget_template'] : null);
73
                if (!is_string($ident)) {
74
                    throw new InvalidArgumentException(
75
                        'Dynamic template ident (from "$widget_template") must be a string'
76
                    );
77
                }
78
            }
79
        }
80
81
        $file = $this->findTemplateFile($ident);
82
        if ($file === null || $file === '') {
83
            return $ident;
84
        }
85
86
        return file_get_contents($file);
87
    }
88
89
    /**
90
     * @return string
91
     */
92
    protected function basePath()
93
    {
94
        return $this->basePath;
95
    }
96
97
    /**
98
     * @see FileLoader::path()
99
     * @return string[]
100
     */
101
    protected function paths()
102
    {
103
        return $this->paths;
104
    }
105
106
    /**
107
     * Get the template file (full path + filename) to load from an ident.
108
     *
109
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
110
     *
111
     * @param string $ident The template identifier to load.
112
     * @throws InvalidArgumentException If the template ident is not a string.
113
     * @return string|null The full path + filename of the found template. Null if nothing was found.
114
     */
115
    protected function findTemplateFile($ident)
116
    {
117
        if (!is_string($ident)) {
118
            throw new InvalidArgumentException(
119
                'Template ident must be a string'
120
            );
121
        }
122
123
        $filename = $this->filenameFromIdent($ident);
124
        $searchPath = $this->paths();
125
        foreach ($searchPath as $path) {
126
            $f = realpath($path).'/'.strtolower($filename);
127
            if (file_exists($f)) {
128
                return $f;
129
            }
130
        }
131
132
        return null;
133
    }
134
135
    /**
136
     * @param string $ident The template identifier to convert to a filename.
137
     * @return string
138
     */
139
    abstract protected function filenameFromIdent($ident);
140
141
    /**
142
     * @param string[] $paths The list of path to add.
143
     * @return LoaderInterface Chainable
144
     */
145
    private function setPaths(array $paths)
146
    {
147
        $this->paths = [];
148
149
        foreach ($paths as $path) {
150
            $this->addPath($path);
151
        }
152
153
        return $this;
154
    }
155
156
    /**
157
     * @param string $basePath The base path to set.
158
     * @throws InvalidArgumentException If the base path parameter is not a string.
159
     * @return LoaderInterface Chainable
160
     */
161
    private function setBasePath($basePath)
162
    {
163
        if (!is_string($basePath)) {
164
            throw new InvalidArgumentException(
165
                'Base path must be a string'
166
            );
167
        }
168
        $basePath = realpath($basePath);
169
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
170
        return $this;
171
    }
172
173
    /**
174
     * @param string $path The path to add to the load.
175
     * @return LoaderInterface Chainable
176
     */
177
    private function addPath($path)
178
    {
179
        $this->paths[] = $this->resolvePath($path);
180
181
        return $this;
182
    }
183
184
    /**
185
     * @param string $path The path to resolve.
186
     * @throws InvalidArgumentException If the path argument is not a string.
187
     * @return string
188
     */
189
    private function resolvePath($path)
190
    {
191
        if (!is_string($path)) {
192
            throw new InvalidArgumentException(
193
                'Path needs to be a string'
194
            );
195
        }
196
197
        $basePath = $this->basePath();
198
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
199
        if ($basePath && strpos($path, $basePath) === false) {
200
            $path = $basePath.$path;
201
        }
202
203
        return $path;
204
    }
205
206
    /**
207
     * @param string      $varName       The name of the variable to set this template unto.
208
     * @param string|null $templateIdent The "dynamic template" to set. null to clear.
209
     * @throws InvalidArgumentException If var name is not a string or if the template is not a string (and not null).
210
     * @return void
211
     */
212 View Code Duplication
    public function setDynamicTemplate($varName, $templateIdent)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
213
    {
214
        if (!is_string($varName)) {
215
            throw new InvalidArgumentException(
216
                'Can not set dynamic template: var name is not a string.'
217
            );
218
        }
219
220
        if ($templateIdent === null) {
221
            $this->dynamicTemplates[$varName] = null;
222
            return;
223
        }
224
225
        if (!is_string($templateIdent)) {
226
            throw new InvalidArgumentException(
227
                'Can not set dynamic template. Must be a a string, or null.'
228
            );
229
        }
230
231
        $this->dynamicTemplates[$varName] = $templateIdent;
232
    }
233
234
    /**
235
     * @param string $varName The name of the variable to get template ident from.
236
     * @throws InvalidArgumentException If the var name is not a string.
237
     * @return string
238
     */
239
    public function dynamicTemplate($varName)
240
    {
241
        if (!is_string($varName)) {
242
            throw new InvalidArgumentException(
243
                'Can not get dynamic template: var name is not a string.'
244
            );
245
        }
246
247
        if (!isset($this->dynamicTemplates[$varName])) {
248
            return '';
249
        }
250
251
        return $this->dynamicTemplates[$varName];
252
    }
253
}
254