Completed
Push — develop ( 3f22a1...840d5f )
by Jens
04:24
created

FilesystemLoader::addPath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 2
1
<?php
2
/**
3
 * @author @jayS-de <[email protected]>
4
 */
5
6
7
namespace JaySDe\HandlebarsBundle\Loader;
8
9
10
use JaySDe\HandlebarsBundle\Error\LoaderException;
11
use Symfony\Component\Config\FileLocatorInterface;
12
use Symfony\Component\Templating\TemplateNameParserInterface;
13
14
class FilesystemLoader
15
{
16
    /** Identifier of the main namespace. */
17
    const MAIN_NAMESPACE = '__main__';
18
19
    protected $locator;
20
    protected $parser;
21
22
    protected $paths = [];
23
    protected $cache = [];
24
    protected $errorCache = [];
25
26
    /**
27
     * FilesystemLoader constructor.
28
     * @param FileLocatorInterface $locator
29
     * @param TemplateNameParserInterface $parser
30
     */
31 23
    public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser)
32
    {
33 23
        $this->locator = $locator;
34 23
        $this->parser = $parser;
35 23
        $this->setPaths([]);
36 23
    }
37
38
    /**
39
     * Returns the paths to the templates.
40
     *
41
     * @param string $namespace A path namespace
42
     *
43
     * @return array The array of paths where to look for templates
44
     */
45 7
    public function getPaths($namespace = self::MAIN_NAMESPACE)
46
    {
47 7
        return isset($this->paths[$namespace]) ? $this->paths[$namespace] : [];
48
    }
49
50
    /**
51
     * Returns the path namespaces.
52
     *
53
     * The main namespace is always defined.
54
     *
55
     * @return array The array of defined namespaces
56
     */
57 1
    public function getNamespaces()
58
    {
59 1
        return array_keys($this->paths);
60
    }
61
62
    /**
63
     * Sets the paths where templates are stored.
64
     *
65
     * @param string|array $paths     A path or an array of paths where to look for templates
66
     * @param string       $namespace A path namespace
67
     */
68 23
    public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
69
    {
70 23
        if (!is_array($paths)) {
71 1
            $paths = [$paths];
72
        }
73
74 23
        $this->paths[$namespace] = [];
75 23
        foreach ($paths as $path) {
76 4
            $this->addPath($path, $namespace);
77
        }
78 23
    }
79
80
    /**
81
     * Adds a path where templates are stored.
82
     *
83
     * @param string $path      A path where to look for templates
84
     * @param string $namespace A path name
85
     *
86
     * @throws LoaderException
87
     */
88 18
    public function addPath($path, $namespace = self::MAIN_NAMESPACE)
89
    {
90
        // invalidate the cache
91 18
        $this->cache = $this->errorCache = [];
92
93 18
        if (!is_dir($path)) {
94 2
            throw new LoaderException(sprintf('The "%s" directory does not exist.', $path));
95
        }
96
97 16
        $this->paths[$namespace][] = rtrim($path, '/\\');
98 16
    }
99
100
    /**
101
     * Prepends a path where templates are stored.
102
     *
103
     * @param string $path      A path where to look for templates
104
     * @param string $namespace A path name
105
     *
106
     * @throws LoaderException
107
     */
108 3
    public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
109
    {
110
        // invalidate the cache
111 3
        $this->cache = $this->errorCache = [];
112
113 3
        if (!is_dir($path)) {
114 1
            throw new LoaderException(sprintf('The "%s" directory does not exist.', $path));
115
        }
116
117 2
        $path = rtrim($path, '/\\');
118
119 2
        if (!isset($this->paths[$namespace])) {
120 1
            $this->paths[$namespace][] = $path;
121
        } else {
122 1
            array_unshift($this->paths[$namespace], $path);
123
        }
124 2
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 5
    public function getSource($name)
130
    {
131 5
        return file_get_contents($this->findTemplate($name));
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 2
    public function getCacheKey($name)
138
    {
139 2
        return $this->findTemplate($name);
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 6
    public function exists($template)
146
    {
147 6
        $name = $this->normalizeName($template);
148
149 6
        if (isset($this->cache[$name])) {
150 1
            return true;
151
        }
152
153
        try {
154 6
            return false !== $this->findTemplate($name, false);
155 1
        } catch (LoaderException $exception) {
156 1
            return false;
157
        }
158
    }
159
160 12
    protected function parseName($name, $default = self::MAIN_NAMESPACE)
161
    {
162 12
        if (isset($name[0]) && '@' == $name[0]) {
163 5
            if (false === $pos = strpos($name, '/')) {
164 2
                throw new LoaderException(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
165
            }
166
167 3
            $namespace = substr($name, 1, $pos - 1);
168 3
            $shortname = substr($name, $pos + 1);
169
170 3
            return array($namespace, $shortname);
171
        }
172
173 8
        return array($default, $name);
174
    }
175
176 12
    protected function normalizeName($name)
177
    {
178 12
        return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
179
    }
180
181 12
    protected function findTemplate($template, $throw = true)
182
    {
183 12
        $normalizedName = $this->normalizeName($template);
184
185 12
        if (isset($this->cache[$normalizedName])) {
186 1
            return $this->cache[$normalizedName];
187
        }
188
189 12
        list($namespace, $shortName) = $this->parseName($normalizedName);
190
191 10
        if (!$this->validateTemplate($normalizedName, $namespace)) {
192 3
            if ($throw) { throw new LoaderException($this->errorCache[$normalizedName]); }
193 2
            return false;
194
        }
195
196 8
        return $this->searchTemplate($template, $normalizedName, $shortName, $namespace, $throw);
197
    }
198
199 8
    private function searchTemplate($template, $name, $shortName, $namespace, $throw) {
200 8
        foreach ($this->paths[$namespace] as $path) {
201 5
            if (is_file($path.'/'.$shortName)) {
202 1
                if (false !== $realpath = realpath($path.'/'.$shortName)) {
203 1
                    return $this->cache[$name] = $realpath;
204
                }
205
206 4
                return $this->cache[$name] = $path.'/'.$shortName;
207
            }
208
        }
209
210 8
        if (!$template = $this->locateTemplate($template, $name, $namespace)) {
211 4
            if ($throw) { throw new LoaderException($this->errorCache[$name]); }
212 1
            return false;
213
        }
214 4
        return $template;
215
    }
216
217 10
    private function validateTemplate($name, $namespace)
218
    {
219 10
        if (isset($this->errorCache[$name])) {
220 1
            return false;
221
        }
222
223 10
        if (!isset($this->paths[$namespace])) {
224 2
            $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
225
226 2
            return false;
227
        }
228
229 8
        return true;
230
    }
231
232 8
    private function locateTemplate($template, $name, $namespace)
233
    {
234
        try {
235 8
            $template = $this->parser->parse($template);
236 8
            $realpath = $this->locator->locate($template);
237 7
            if (false !== $realpath && null !== $realpath) {
238 7
                return $this->cache[$name] = $realpath;
239
            }
240 1
        } catch (\Exception $e) {
241
            // catch locator not found exceptions
242
        }
243
244 4
        $this->errorCache[$name] = sprintf(
245 4
            'Unable to find template "%s" (looked into: %s).',
246
            $name,
247 4
            implode(', ', $this->paths[$namespace])
248
        );
249 4
        return false;
250
    }
251
}
252