Completed
Pull Request — master (#1)
by Mathieu
04:21
created

AbstractLoader::findTemplateFile()   C

Complexity

Conditions 7
Paths 11

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 11
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
     * @return string
54
     */
55
    public function load($ident)
56
    {
57
        $file = $this->findTemplateFile($ident);
58
        if (!$file) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $file of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
59
            return $ident;
60
        }
61
62
        return file_get_contents($file);
63
    }
64
65
    /**
66
     * @return string
67
     */
68
    protected function basePath()
69
    {
70
        return $this->basePath;
71
    }
72
73
    /**
74
     * @see FileLoader::path()
75
     * @return string[]
76
     */
77
    protected function paths()
78
    {
79
        return $this->paths;
80
    }
81
82
    /**
83
     * Get the template file (full path + filename) to load from an ident.
84
     *
85
     * This method first generates the filename for an identifier and search for it in all of the loader's paths.
86
     *
87
     * @param string $ident The template identifier to load.
88
     * @throws InvalidArgumentException If the template ident is not a string.
89
     * @return string|null The full path + filename of the found template. Null if nothing was found.
90
     */
91
    protected function findTemplateFile($ident)
0 ignored issues
show
Coding Style introduced by
findTemplateFile 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...
92
    {
93
        if (!is_string($ident)) {
94
            throw new InvalidArgumentException(
95
                'Template ident must be a string'
96
            );
97
        }
98
99
        // Handle dynamic template hack.
100
        if ($ident === '$widget_template') {
101
            $ident = (isset($GLOBALS['widget_template']) ? $GLOBALS['widget_template'] : null);
102
            if (!is_string($ident)) {
103
                throw new InvalidArgumentException(
104
                    'Template ident (dynamic, from \$widget_templatee) must be a string'
105
                );
106
            }
107
        }
108
109
        $filename = $this->filenameFromIdent($ident);
110
        $searchPath = $this->paths();
111
        foreach ($searchPath as $path) {
112
            $f = realpath($path).'/'.strtolower($filename);
113
            if (file_exists($f)) {
114
                return $f;
115
            }
116
        }
117
118
        return null;
119
    }
120
121
    /**
122
     * @param string $ident The template identifier to convert to a filename.
123
     * @return string
124
     */
125
    abstract protected function filenameFromIdent($ident);
126
127
    /**
128
     * @param string[] $paths The list of path to add.
129
     * @return LoaderInterface Chainable
130
     */
131
    private function setPaths(array $paths)
132
    {
133
        $this->paths = [];
134
135
        foreach ($paths as $path) {
136
            $this->addPath($path);
137
        }
138
139
        return $this;
140
    }
141
142
    /**
143
     * @param string $basePath The base path to set.
144
     * @throws InvalidArgumentException If the base path parameter is not a string.
145
     * @return LoaderInterface Chainable
146
     */
147
    private function setBasePath($basePath)
148
    {
149
        if (!is_string($basePath)) {
150
            throw new InvalidArgumentException(
151
                'Base path must be a string'
152
            );
153
        }
154
        $basePath = realpath($basePath);
155
        $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
156
        return $this;
157
    }
158
159
    /**
160
     * @param string $path The path to add to the load.
161
     * @return LoaderInterface Chainable
162
     */
163
    private function addPath($path)
164
    {
165
        $this->paths[] = $this->resolvePath($path);
166
167
        return $this;
168
    }
169
170
    /**
171
     * @param string $path The path to resolve.
172
     * @throws InvalidArgumentException If the path argument is not a string.
173
     * @return string
174
     */
175
    private function resolvePath($path)
176
    {
177
        if (!is_string($path)) {
178
            throw new InvalidArgumentException(
179
                'Path needs to be a string'
180
            );
181
        }
182
183
        $basePath = $this->basePath();
184
        $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
185
        if ($basePath && strpos($path, $basePath) === false) {
186
            $path = $basePath.$path;
187
        }
188
189
        return $path;
190
    }
191
}
192