Completed
Pull Request — master (#2)
by
unknown
02:31
created

AbstractLoader::load()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 27
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 12
nc 10
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 $basePath
26
     */
27
    private $basePath = '';
28
29
    /**
30
     * @var string[] $paths
31
     */
32
    private $paths = [];
33
34
    /**
35
     * Default constructor, if none is provided by the concrete class implementations.
36
     *
37
     * ## Required dependencies
38
     * - `logger` A PSR-3 logger
39
     *
40
     * @param array $data The class dependencies map.
41
     */
42
    public function __construct(array $data = null)
43
    {
44
        $this->setLogger($data['logger']);
45
        $this->setBasePath($data['base_path']);
46
        $this->setPaths($data['paths']);
47
    }
48
49
    /**
50
     * Load a template content
51
     *
52
     * @param  string $ident The template ident to load and render.
53
     * @throws InvalidArgumentException If the dynamic template identifier is not a string.
54
     * @return string
55
     */
56
    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...
57
    {
58
        // Handle dynamic template hack.
59
        if ($ident === '$widget_template') {
60
            $ident = (isset($GLOBALS['widget_template']) ? $GLOBALS['widget_template'] : null);
61
            if (!is_string($ident)) {
62
                throw new InvalidArgumentException(
63
                    'Dynamic template ident (from "$widget_template") must be a string'
64
                );
65
            }
66
        }
67
68
        /**
69
         * Prevents the loader from passing a proper template through further
70
         * procedures meant for a template identifier.
71
         */
72
        if ($this->isTemplateMaybeLoaded($ident)) {
73
            return $ident;
74
        }
75
76
        $file = $this->findTemplateFile($ident);
77
        if ($file === null || $file === '') {
78
            return $ident;
79
        }
80
81
        return file_get_contents($file);
82
    }
83
84
    /**
85
     * @return string
86
     */
87
    protected function basePath()
88
    {
89
        return $this->basePath;
90
    }
91
92
    /**
93
     * @see FileLoader::path()
94
     * @return string[]
95
     */
96
    protected function paths()
97
    {
98
        return $this->paths;
99
    }
100
101
    /**
102
     * Determine if the template is loaded.
103
     *
104
     * @param  string $ident The template or identifier to check.
105
     * @return boolean Returns TRUE if the given value is most like the template contents instead
106
     *     of a template identifier (file path).
107
     */
108
    protected function isTemplateMaybeLoaded($ident)
109
    {
110
        return strpos($ident, PHP_EOL) !== false;
111
    }
112
113
    /**
114
     * Get the template file (full path + filename) to load from an ident.
115
     *
116
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
117
     *
118
     * @param string $ident The template identifier to load.
119
     * @throws InvalidArgumentException If the template ident is not a string.
120
     * @return string|null The full path + filename of the found template. Null if nothing was found.
121
     */
122
    protected function findTemplateFile($ident)
123
    {
124
        if (!is_string($ident)) {
125
            throw new InvalidArgumentException(
126
                'Template ident must be a string'
127
            );
128
        }
129
130
        $filename = $this->filenameFromIdent($ident);
131
        $searchPath = $this->paths();
132
        foreach ($searchPath as $path) {
133
            $f = realpath($path).'/'.strtolower($filename);
134
            if (file_exists($f)) {
135
                return $f;
136
            }
137
        }
138
139
        return null;
140
    }
141
142
    /**
143
     * @param string $ident The template identifier to convert to a filename.
144
     * @return string
145
     */
146
    abstract protected function filenameFromIdent($ident);
147
148
    /**
149
     * @param string[] $paths The list of path to add.
150
     * @return LoaderInterface Chainable
151
     */
152
    private function setPaths(array $paths)
153
    {
154
        $this->paths = [];
155
156
        foreach ($paths as $path) {
157
            $this->addPath($path);
158
        }
159
160
        return $this;
161
    }
162
163
    /**
164
     * @param string $basePath The base path to set.
165
     * @throws InvalidArgumentException If the base path parameter is not a string.
166
     * @return LoaderInterface Chainable
167
     */
168
    private function setBasePath($basePath)
169
    {
170
        if (!is_string($basePath)) {
171
            throw new InvalidArgumentException(
172
                'Base path must be a string'
173
            );
174
        }
175
        $basePath = realpath($basePath);
176
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
177
        return $this;
178
    }
179
180
    /**
181
     * @param string $path The path to add to the load.
182
     * @return LoaderInterface Chainable
183
     */
184
    private function addPath($path)
185
    {
186
        $this->paths[] = $this->resolvePath($path);
187
188
        return $this;
189
    }
190
191
    /**
192
     * @param string $path The path to resolve.
193
     * @throws InvalidArgumentException If the path argument is not a string.
194
     * @return string
195
     */
196
    private function resolvePath($path)
197
    {
198
        if (!is_string($path)) {
199
            throw new InvalidArgumentException(
200
                'Path needs to be a string'
201
            );
202
        }
203
204
        $basePath = $this->basePath();
205
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
206
        if ($basePath && strpos($path, $basePath) === false) {
207
            $path = $basePath.$path;
208
        }
209
210
        return $path;
211
    }
212
}
213