|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
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 namespace Symfony\Component\ClassLoader; |
|
13 |
|
14 /** |
|
15 * UniversalClassLoader implements a "universal" autoloader for PHP 5.3. |
|
16 * |
|
17 * It is able to load classes that use either: |
|
18 * |
|
19 * * The technical interoperability standards for PHP 5.3 namespaces and |
|
20 * class names (http://groups.google.com/group/php-standards/web/psr-0-final-proposal); |
|
21 * |
|
22 * * The PEAR naming convention for classes (http://pear.php.net/). |
|
23 * |
|
24 * Classes from a sub-namespace or a sub-hierarchy of PEAR classes can be |
|
25 * looked for in a list of locations to ease the vendoring of a sub-set of |
|
26 * classes for large projects. |
|
27 * |
|
28 * Example usage: |
|
29 * |
|
30 * $loader = new UniversalClassLoader(); |
|
31 * |
|
32 * // register classes with namespaces |
|
33 * $loader->registerNamespaces(array( |
|
34 * 'Symfony\Component' => __DIR__.'/component', |
|
35 * 'Symfony' => __DIR__.'/framework', |
|
36 * 'Sensio' => array(__DIR__.'/src', __DIR__.'/vendor'), |
|
37 * )); |
|
38 * |
|
39 * // register a library using the PEAR naming convention |
|
40 * $loader->registerPrefixes(array( |
|
41 * 'Swift_' => __DIR__.'/Swift', |
|
42 * )); |
|
43 * |
|
44 * // activate the autoloader |
|
45 * $loader->register(); |
|
46 * |
|
47 * In this example, if you try to use a class in the Symfony\Component |
|
48 * namespace or one of its children (Symfony\Component\Console for instance), |
|
49 * the autoloader will first look for the class under the component/ |
|
50 * directory, and it will then fallback to the framework/ directory if not |
|
51 * found before giving up. |
|
52 * |
|
53 * @author Fabien Potencier <fabien@symfony.com> |
|
54 * |
|
55 * @api |
|
56 */ |
|
57 class UniversalClassLoader |
|
58 { |
|
59 private $namespaces = array(); |
|
60 private $prefixes = array(); |
|
61 private $namespaceFallbacks = array(); |
|
62 private $prefixFallbacks = array(); |
|
63 |
|
64 /** |
|
65 * Gets the configured namespaces. |
|
66 * |
|
67 * @return array A hash with namespaces as keys and directories as values |
|
68 */ |
|
69 public function getNamespaces() |
|
70 { |
|
71 return $this->namespaces; |
|
72 } |
|
73 |
|
74 /** |
|
75 * Gets the configured class prefixes. |
|
76 * |
|
77 * @return array A hash with class prefixes as keys and directories as values |
|
78 */ |
|
79 public function getPrefixes() |
|
80 { |
|
81 return $this->prefixes; |
|
82 } |
|
83 |
|
84 /** |
|
85 * Gets the directory(ies) to use as a fallback for namespaces. |
|
86 * |
|
87 * @return array An array of directories |
|
88 */ |
|
89 public function getNamespaceFallbacks() |
|
90 { |
|
91 return $this->namespaceFallbacks; |
|
92 } |
|
93 |
|
94 /** |
|
95 * Gets the directory(ies) to use as a fallback for class prefixes. |
|
96 * |
|
97 * @return array An array of directories |
|
98 */ |
|
99 public function getPrefixFallbacks() |
|
100 { |
|
101 return $this->prefixFallbacks; |
|
102 } |
|
103 |
|
104 /** |
|
105 * Registers the directory to use as a fallback for namespaces. |
|
106 * |
|
107 * @param array $dirs An array of directories |
|
108 * |
|
109 * @api |
|
110 */ |
|
111 public function registerNamespaceFallbacks(array $dirs) |
|
112 { |
|
113 $this->namespaceFallbacks = $dirs; |
|
114 } |
|
115 |
|
116 /** |
|
117 * Registers the directory to use as a fallback for class prefixes. |
|
118 * |
|
119 * @param array $dirs An array of directories |
|
120 * |
|
121 * @api |
|
122 */ |
|
123 public function registerPrefixFallbacks(array $dirs) |
|
124 { |
|
125 $this->prefixFallbacks = $dirs; |
|
126 } |
|
127 |
|
128 /** |
|
129 * Registers an array of namespaces |
|
130 * |
|
131 * @param array $namespaces An array of namespaces (namespaces as keys and locations as values) |
|
132 * |
|
133 * @api |
|
134 */ |
|
135 public function registerNamespaces(array $namespaces) |
|
136 { |
|
137 foreach ($namespaces as $namespace => $locations) { |
|
138 $this->namespaces[$namespace] = (array) $locations; |
|
139 } |
|
140 } |
|
141 |
|
142 /** |
|
143 * Registers a namespace. |
|
144 * |
|
145 * @param string $namespace The namespace |
|
146 * @param array|string $paths The location(s) of the namespace |
|
147 * |
|
148 * @api |
|
149 */ |
|
150 public function registerNamespace($namespace, $paths) |
|
151 { |
|
152 $this->namespaces[$namespace] = (array) $paths; |
|
153 } |
|
154 |
|
155 /** |
|
156 * Registers an array of classes using the PEAR naming convention. |
|
157 * |
|
158 * @param array $classes An array of classes (prefixes as keys and locations as values) |
|
159 * |
|
160 * @api |
|
161 */ |
|
162 public function registerPrefixes(array $classes) |
|
163 { |
|
164 foreach ($classes as $prefix => $locations) { |
|
165 $this->prefixes[$prefix] = (array) $locations; |
|
166 } |
|
167 } |
|
168 |
|
169 /** |
|
170 * Registers a set of classes using the PEAR naming convention. |
|
171 * |
|
172 * @param string $prefix The classes prefix |
|
173 * @param array|string $paths The location(s) of the classes |
|
174 * |
|
175 * @api |
|
176 */ |
|
177 public function registerPrefix($prefix, $paths) |
|
178 { |
|
179 $this->prefixes[$prefix] = (array) $paths; |
|
180 } |
|
181 |
|
182 /** |
|
183 * Registers this instance as an autoloader. |
|
184 * |
|
185 * @param Boolean $prepend Whether to prepend the autoloader or not |
|
186 * |
|
187 * @api |
|
188 */ |
|
189 public function register($prepend = false) |
|
190 { |
|
191 spl_autoload_register(array($this, 'loadClass'), true, $prepend); |
|
192 } |
|
193 |
|
194 /** |
|
195 * Loads the given class or interface. |
|
196 * |
|
197 * @param string $class The name of the class |
|
198 */ |
|
199 public function loadClass($class) |
|
200 { |
|
201 if ($file = $this->findFile($class)) { |
|
202 require $file; |
|
203 } |
|
204 } |
|
205 |
|
206 /** |
|
207 * Finds the path to the file where the class is defined. |
|
208 * |
|
209 * @param string $class The name of the class |
|
210 * |
|
211 * @return string|null The path, if found |
|
212 */ |
|
213 public function findFile($class) |
|
214 { |
|
215 if ('\\' == $class[0]) { |
|
216 $class = substr($class, 1); |
|
217 } |
|
218 |
|
219 if (false !== $pos = strrpos($class, '\\')) { |
|
220 // namespaced class name |
|
221 $namespace = substr($class, 0, $pos); |
|
222 foreach ($this->namespaces as $ns => $dirs) { |
|
223 if (0 !== strpos($namespace, $ns)) { |
|
224 continue; |
|
225 } |
|
226 |
|
227 foreach ($dirs as $dir) { |
|
228 $className = substr($class, $pos + 1); |
|
229 $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $className).'.php'; |
|
230 if (file_exists($file)) { |
|
231 return $file; |
|
232 } |
|
233 } |
|
234 } |
|
235 |
|
236 foreach ($this->namespaceFallbacks as $dir) { |
|
237 $file = $dir.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php'; |
|
238 if (file_exists($file)) { |
|
239 return $file; |
|
240 } |
|
241 } |
|
242 } else { |
|
243 // PEAR-like class name |
|
244 foreach ($this->prefixes as $prefix => $dirs) { |
|
245 if (0 !== strpos($class, $prefix)) { |
|
246 continue; |
|
247 } |
|
248 |
|
249 foreach ($dirs as $dir) { |
|
250 $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; |
|
251 if (file_exists($file)) { |
|
252 return $file; |
|
253 } |
|
254 } |
|
255 } |
|
256 |
|
257 foreach ($this->prefixFallbacks as $dir) { |
|
258 $file = $dir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php'; |
|
259 if (file_exists($file)) { |
|
260 return $file; |
|
261 } |
|
262 } |
|
263 } |
|
264 } |
|
265 } |