Completed
Pull Request — master (#3)
by
unknown
03:51 queued 01:21
created

AbstractLoader::findTemplateFile()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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