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