Project

General

Profile

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
}
(3-3/4)