1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 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
12namespace Twig\Loader;
13
14use Twig\Error\LoaderError;
15use Twig\Source;
16
17/**
18 * Loads templates from other loaders.
19 *
20 * @final
21 *
22 * @author Fabien Potencier <fabien@symfony.com>
23 */
24class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
25{
26    private $hasSourceCache = [];
27    protected $loaders = [];
28
29    /**
30     * @param LoaderInterface[] $loaders
31     */
32    public function __construct(array $loaders = [])
33    {
34        foreach ($loaders as $loader) {
35            $this->addLoader($loader);
36        }
37    }
38
39    public function addLoader(LoaderInterface $loader)
40    {
41        $this->loaders[] = $loader;
42        $this->hasSourceCache = [];
43    }
44
45    /**
46     * @return LoaderInterface[]
47     */
48    public function getLoaders()
49    {
50        return $this->loaders;
51    }
52
53    public function getSource($name)
54    {
55        @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
56
57        $exceptions = [];
58        foreach ($this->loaders as $loader) {
59            if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
60                continue;
61            }
62
63            try {
64                return $loader->getSource($name);
65            } catch (LoaderError $e) {
66                $exceptions[] = $e->getMessage();
67            }
68        }
69
70        throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
71    }
72
73    public function getSourceContext($name)
74    {
75        $exceptions = [];
76        foreach ($this->loaders as $loader) {
77            if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
78                continue;
79            }
80
81            try {
82                if ($loader instanceof SourceContextLoaderInterface) {
83                    return $loader->getSourceContext($name);
84                }
85
86                return new Source($loader->getSource($name), $name);
87            } catch (LoaderError $e) {
88                $exceptions[] = $e->getMessage();
89            }
90        }
91
92        throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
93    }
94
95    public function exists($name)
96    {
97        $name = (string) $name;
98
99        if (isset($this->hasSourceCache[$name])) {
100            return $this->hasSourceCache[$name];
101        }
102
103        foreach ($this->loaders as $loader) {
104            if ($loader instanceof ExistsLoaderInterface) {
105                if ($loader->exists($name)) {
106                    return $this->hasSourceCache[$name] = true;
107                }
108
109                continue;
110            }
111
112            try {
113                if ($loader instanceof SourceContextLoaderInterface) {
114                    $loader->getSourceContext($name);
115                } else {
116                    $loader->getSource($name);
117                }
118
119                return $this->hasSourceCache[$name] = true;
120            } catch (LoaderError $e) {
121            }
122        }
123
124        return $this->hasSourceCache[$name] = false;
125    }
126
127    public function getCacheKey($name)
128    {
129        $exceptions = [];
130        foreach ($this->loaders as $loader) {
131            if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
132                continue;
133            }
134
135            try {
136                return $loader->getCacheKey($name);
137            } catch (LoaderError $e) {
138                $exceptions[] = \get_class($loader).': '.$e->getMessage();
139            }
140        }
141
142        throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
143    }
144
145    public function isFresh($name, $time)
146    {
147        $exceptions = [];
148        foreach ($this->loaders as $loader) {
149            if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
150                continue;
151            }
152
153            try {
154                return $loader->isFresh($name, $time);
155            } catch (LoaderError $e) {
156                $exceptions[] = \get_class($loader).': '.$e->getMessage();
157            }
158        }
159
160        throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
161    }
162}
163
164class_alias('Twig\Loader\ChainLoader', 'Twig_Loader_Chain');
165