]>
Commit | Line | Data |
---|---|---|
a4565e88 NL |
1 | <?php |
2 | ||
3 | /* | |
4 | * This file is part of Twig. | |
5 | * | |
6 | * (c) 2009 Fabien Potencier | |
7 | * | |
8 | * For the full copyright and license information, please view the LICENSE | |
9 | * file that was distributed with this source code. | |
10 | */ | |
11 | ||
12 | /** | |
13 | * Loads template from the filesystem. | |
14 | * | |
15 | * @author Fabien Potencier <fabien@symfony.com> | |
16 | */ | |
17 | class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface | |
18 | { | |
19 | protected $paths; | |
20 | protected $cache; | |
21 | ||
22 | /** | |
23 | * Constructor. | |
24 | * | |
25 | * @param string|array $paths A path or an array of paths where to look for templates | |
26 | */ | |
27 | public function __construct($paths = array()) | |
28 | { | |
29 | if ($paths) { | |
30 | $this->setPaths($paths); | |
31 | } | |
32 | } | |
33 | ||
34 | /** | |
35 | * Returns the paths to the templates. | |
36 | * | |
37 | * @param string $namespace A path namespace | |
38 | * | |
39 | * @return array The array of paths where to look for templates | |
40 | */ | |
41 | public function getPaths($namespace = '__main__') | |
42 | { | |
43 | return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array(); | |
44 | } | |
45 | ||
46 | /** | |
47 | * Returns the path namespaces. | |
48 | * | |
49 | * The "__main__" namespace is always defined. | |
50 | * | |
51 | * @return array The array of defined namespaces | |
52 | */ | |
53 | public function getNamespaces() | |
54 | { | |
55 | return array_keys($this->paths); | |
56 | } | |
57 | ||
58 | /** | |
59 | * Sets the paths where templates are stored. | |
60 | * | |
61 | * @param string|array $paths A path or an array of paths where to look for templates | |
62 | * @param string $namespace A path namespace | |
63 | */ | |
64 | public function setPaths($paths, $namespace = '__main__') | |
65 | { | |
66 | if (!is_array($paths)) { | |
67 | $paths = array($paths); | |
68 | } | |
69 | ||
70 | $this->paths[$namespace] = array(); | |
71 | foreach ($paths as $path) { | |
72 | $this->addPath($path, $namespace); | |
73 | } | |
74 | } | |
75 | ||
76 | /** | |
77 | * Adds a path where templates are stored. | |
78 | * | |
79 | * @param string $path A path where to look for templates | |
80 | * @param string $namespace A path name | |
81 | * | |
82 | * @throws Twig_Error_Loader | |
83 | */ | |
84 | public function addPath($path, $namespace = '__main__') | |
85 | { | |
86 | // invalidate the cache | |
87 | $this->cache = array(); | |
88 | ||
89 | if (!is_dir($path)) { | |
90 | throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); | |
91 | } | |
92 | ||
93 | $this->paths[$namespace][] = rtrim($path, '/\\'); | |
94 | } | |
95 | ||
96 | /** | |
97 | * Prepends a path where templates are stored. | |
98 | * | |
99 | * @param string $path A path where to look for templates | |
100 | * @param string $namespace A path name | |
101 | * | |
102 | * @throws Twig_Error_Loader | |
103 | */ | |
104 | public function prependPath($path, $namespace = '__main__') | |
105 | { | |
106 | // invalidate the cache | |
107 | $this->cache = array(); | |
108 | ||
109 | if (!is_dir($path)) { | |
110 | throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); | |
111 | } | |
112 | ||
113 | $path = rtrim($path, '/\\'); | |
114 | ||
115 | if (!isset($this->paths[$namespace])) { | |
116 | $this->paths[$namespace][] = $path; | |
117 | } else { | |
118 | array_unshift($this->paths[$namespace], $path); | |
119 | } | |
120 | } | |
121 | ||
122 | /** | |
123 | * {@inheritdoc} | |
124 | */ | |
125 | public function getSource($name) | |
126 | { | |
127 | return file_get_contents($this->findTemplate($name)); | |
128 | } | |
129 | ||
130 | /** | |
131 | * {@inheritdoc} | |
132 | */ | |
133 | public function getCacheKey($name) | |
134 | { | |
135 | return $this->findTemplate($name); | |
136 | } | |
137 | ||
138 | /** | |
139 | * {@inheritdoc} | |
140 | */ | |
141 | public function exists($name) | |
142 | { | |
143 | $name = (string) $name; | |
144 | if (isset($this->cache[$name])) { | |
145 | return true; | |
146 | } | |
147 | ||
148 | try { | |
149 | $this->findTemplate($name); | |
150 | ||
151 | return true; | |
152 | } catch (Twig_Error_Loader $exception) { | |
153 | return false; | |
154 | } | |
155 | } | |
156 | ||
157 | /** | |
158 | * {@inheritdoc} | |
159 | */ | |
160 | public function isFresh($name, $time) | |
161 | { | |
162 | return filemtime($this->findTemplate($name)) <= $time; | |
163 | } | |
164 | ||
165 | protected function findTemplate($name) | |
166 | { | |
167 | $name = (string) $name; | |
168 | ||
169 | // normalize name | |
170 | $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/')); | |
171 | ||
172 | if (isset($this->cache[$name])) { | |
173 | return $this->cache[$name]; | |
174 | } | |
175 | ||
176 | $this->validateName($name); | |
177 | ||
178 | $namespace = '__main__'; | |
179 | if (isset($name[0]) && '@' == $name[0]) { | |
180 | if (false === $pos = strpos($name, '/')) { | |
181 | throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); | |
182 | } | |
183 | ||
184 | $namespace = substr($name, 1, $pos - 1); | |
185 | ||
186 | $name = substr($name, $pos + 1); | |
187 | } | |
188 | ||
189 | if (!isset($this->paths[$namespace])) { | |
190 | throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); | |
191 | } | |
192 | ||
193 | foreach ($this->paths[$namespace] as $path) { | |
194 | if (is_file($path.'/'.$name)) { | |
195 | return $this->cache[$name] = $path.'/'.$name; | |
196 | } | |
197 | } | |
198 | ||
199 | throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]))); | |
200 | } | |
201 | ||
202 | protected function validateName($name) | |
203 | { | |
204 | if (false !== strpos($name, "\0")) { | |
205 | throw new Twig_Error_Loader('A template name cannot contain NUL bytes.'); | |
206 | } | |
207 | ||
208 | $name = ltrim($name, '/'); | |
209 | $parts = explode('/', $name); | |
210 | $level = 0; | |
211 | foreach ($parts as $part) { | |
212 | if ('..' === $part) { | |
213 | --$level; | |
214 | } elseif ('.' !== $part) { | |
215 | ++$level; | |
216 | } | |
217 | ||
218 | if ($level < 0) { | |
219 | throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); | |
220 | } | |
221 | } | |
222 | } | |
223 | } |