Completed
Push — develop ( ce93b3...ca1b30 )
by Jens
03:12
created

FilesystemLoader::getSource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
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 View Code Duplication
        if (!isset($this->paths[$namespace])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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
        $name = $this->normalizeName($template);
184
185 12
        if (isset($this->cache[$name])) {
186 1
            return $this->cache[$name];
187
        }
188
189 12
        list($namespace, $shortname) = $this->parseName($name);
190
191
        try {
192 10
            $this->validateTemplate($name, $namespace);
193 3
        } catch (LoaderException $e) {
194 3
            if ($throw) {
195 2
                throw $e;
196
            }
197 2
            return false;
198
        }
199
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
        try {
211 8
            return $this->locateTemplate($template, $name, $namespace);
212 4
        } catch (LoaderException $e) {
213 4
            if ($throw) {
214 3
                throw $e;
215
            }
216 1
            return false;
217
        }
218
    }
219
220 10
    private function validateTemplate($name, $namespace)
221
    {
222 10
        if (isset($this->errorCache[$name])) {
223 1
            throw new LoaderException($this->errorCache[$name]);
224
        }
225
226 10 View Code Duplication
        if (!isset($this->paths[$namespace])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
227 2
            $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
228
229 2
            throw new LoaderException($this->errorCache[$name]);
230
        }
231
232 8
        return true;
233
    }
234
235 8
    private function locateTemplate($template, $name, $namespace)
236
    {
237
        try {
238 8
            $template = $this->parser->parse($template);
239 8
            $realpath = $this->locator->locate($template);
240 7
            if (false !== $realpath && null !== $realpath) {
241 7
                return $this->cache[$name] = $realpath;
242
            }
243 1
        } catch (\Exception $e) {
244
            // catch locator not found exceptions
245
        }
246
247 4
        $this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
248
249 4
        throw new LoaderException($this->errorCache[$name]);
250
    }
251
}
252