|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Loader |
|
17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
18 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
19 * @version $Id: Loader.php 22019 2010-04-27 16:33:31Z matthew $ |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Static methods for loading classes and files. |
|
24 * |
|
25 * @category Zend |
|
26 * @package Zend_Loader |
|
27 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
28 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
29 */ |
|
30 class Zend_Loader |
|
31 { |
|
32 /** |
|
33 * Loads a class from a PHP file. The filename must be formatted |
|
34 * as "$class.php". |
|
35 * |
|
36 * If $dirs is a string or an array, it will search the directories |
|
37 * in the order supplied, and attempt to load the first matching file. |
|
38 * |
|
39 * If $dirs is null, it will split the class name at underscores to |
|
40 * generate a path hierarchy (e.g., "Zend_Example_Class" will map |
|
41 * to "Zend/Example/Class.php"). |
|
42 * |
|
43 * If the file was not found in the $dirs, or if no $dirs were specified, |
|
44 * it will attempt to load it from PHP's include_path. |
|
45 * |
|
46 * @param string $class - The full class name of a Zend component. |
|
47 * @param string|array $dirs - OPTIONAL Either a path or an array of paths |
|
48 * to search. |
|
49 * @return void |
|
50 * @throws Zend_Exception |
|
51 */ |
|
52 public static function loadClass($class, $dirs = null) |
|
53 { |
|
54 if (class_exists($class, false) || interface_exists($class, false)) { |
|
55 return; |
|
56 } |
|
57 |
|
58 if ((null !== $dirs) && !is_string($dirs) && !is_array($dirs)) { |
|
59 require_once 'Zend/Exception.php'; |
|
60 throw new Zend_Exception('Directory argument must be a string or an array'); |
|
61 } |
|
62 |
|
63 // Autodiscover the path from the class name |
|
64 // Implementation is PHP namespace-aware, and based on |
|
65 // Framework Interop Group reference implementation: |
|
66 // http://groups.google.com/group/php-standards/web/psr-0-final-proposal |
|
67 $className = ltrim($class, '\\'); |
|
68 $file = ''; |
|
69 $namespace = ''; |
|
70 if ($lastNsPos = strripos($className, '\\')) { |
|
71 $namespace = substr($className, 0, $lastNsPos); |
|
72 $className = substr($className, $lastNsPos + 1); |
|
73 $file = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; |
|
74 } |
|
75 $file .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; |
|
76 |
|
77 if (!empty($dirs)) { |
|
78 // use the autodiscovered path |
|
79 $dirPath = dirname($file); |
|
80 if (is_string($dirs)) { |
|
81 $dirs = explode(PATH_SEPARATOR, $dirs); |
|
82 } |
|
83 foreach ($dirs as $key => $dir) { |
|
84 if ($dir == '.') { |
|
85 $dirs[$key] = $dirPath; |
|
86 } else { |
|
87 $dir = rtrim($dir, '\\/'); |
|
88 $dirs[$key] = $dir . DIRECTORY_SEPARATOR . $dirPath; |
|
89 } |
|
90 } |
|
91 $file = basename($file); |
|
92 self::loadFile($file, $dirs, true); |
|
93 } else { |
|
94 self::loadFile($file, null, true); |
|
95 } |
|
96 |
|
97 if (!class_exists($class, false) && !interface_exists($class, false)) { |
|
98 require_once 'Zend/Exception.php'; |
|
99 throw new Zend_Exception("File \"$file\" does not exist or class \"$class\" was not found in the file"); |
|
100 } |
|
101 } |
|
102 |
|
103 /** |
|
104 * Loads a PHP file. This is a wrapper for PHP's include() function. |
|
105 * |
|
106 * $filename must be the complete filename, including any |
|
107 * extension such as ".php". Note that a security check is performed that |
|
108 * does not permit extended characters in the filename. This method is |
|
109 * intended for loading Zend Framework files. |
|
110 * |
|
111 * If $dirs is a string or an array, it will search the directories |
|
112 * in the order supplied, and attempt to load the first matching file. |
|
113 * |
|
114 * If the file was not found in the $dirs, or if no $dirs were specified, |
|
115 * it will attempt to load it from PHP's include_path. |
|
116 * |
|
117 * If $once is TRUE, it will use include_once() instead of include(). |
|
118 * |
|
119 * @param string $filename |
|
120 * @param string|array $dirs - OPTIONAL either a path or array of paths |
|
121 * to search. |
|
122 * @param boolean $once |
|
123 * @return boolean |
|
124 * @throws Zend_Exception |
|
125 */ |
|
126 public static function loadFile($filename, $dirs = null, $once = false) |
|
127 { |
|
128 self::_securityCheck($filename); |
|
129 |
|
130 /** |
|
131 * Search in provided directories, as well as include_path |
|
132 */ |
|
133 $incPath = false; |
|
134 if (!empty($dirs) && (is_array($dirs) || is_string($dirs))) { |
|
135 if (is_array($dirs)) { |
|
136 $dirs = implode(PATH_SEPARATOR, $dirs); |
|
137 } |
|
138 $incPath = get_include_path(); |
|
139 set_include_path($dirs . PATH_SEPARATOR . $incPath); |
|
140 } |
|
141 |
|
142 /** |
|
143 * Try finding for the plain filename in the include_path. |
|
144 */ |
|
145 if ($once) { |
|
146 include_once $filename; |
|
147 } else { |
|
148 include $filename; |
|
149 } |
|
150 |
|
151 /** |
|
152 * If searching in directories, reset include_path |
|
153 */ |
|
154 if ($incPath) { |
|
155 set_include_path($incPath); |
|
156 } |
|
157 |
|
158 return true; |
|
159 } |
|
160 |
|
161 /** |
|
162 * Returns TRUE if the $filename is readable, or FALSE otherwise. |
|
163 * This function uses the PHP include_path, where PHP's is_readable() |
|
164 * does not. |
|
165 * |
|
166 * Note from ZF-2900: |
|
167 * If you use custom error handler, please check whether return value |
|
168 * from error_reporting() is zero or not. |
|
169 * At mark of fopen() can not suppress warning if the handler is used. |
|
170 * |
|
171 * @param string $filename |
|
172 * @return boolean |
|
173 */ |
|
174 public static function isReadable($filename) |
|
175 { |
|
176 if (is_readable($filename)) { |
|
177 // Return early if the filename is readable without needing the |
|
178 // include_path |
|
179 return true; |
|
180 } |
|
181 |
|
182 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' |
|
183 && preg_match('/^[a-z]:/i', $filename) |
|
184 ) { |
|
185 // If on windows, and path provided is clearly an absolute path, |
|
186 // return false immediately |
|
187 return false; |
|
188 } |
|
189 |
|
190 foreach (self::explodeIncludePath() as $path) { |
|
191 if ($path == '.') { |
|
192 if (is_readable($filename)) { |
|
193 return true; |
|
194 } |
|
195 continue; |
|
196 } |
|
197 $file = $path . '/' . $filename; |
|
198 if (is_readable($file)) { |
|
199 return true; |
|
200 } |
|
201 } |
|
202 return false; |
|
203 } |
|
204 |
|
205 /** |
|
206 * Explode an include path into an array |
|
207 * |
|
208 * If no path provided, uses current include_path. Works around issues that |
|
209 * occur when the path includes stream schemas. |
|
210 * |
|
211 * @param string|null $path |
|
212 * @return array |
|
213 */ |
|
214 public static function explodeIncludePath($path = null) |
|
215 { |
|
216 if (null === $path) { |
|
217 $path = get_include_path(); |
|
218 } |
|
219 |
|
220 if (PATH_SEPARATOR == ':') { |
|
221 // On *nix systems, include_paths which include paths with a stream |
|
222 // schema cannot be safely explode'd, so we have to be a bit more |
|
223 // intelligent in the approach. |
|
224 $paths = preg_split('#:(?!//)#', $path); |
|
225 } else { |
|
226 $paths = explode(PATH_SEPARATOR, $path); |
|
227 } |
|
228 return $paths; |
|
229 } |
|
230 |
|
231 /** |
|
232 * spl_autoload() suitable implementation for supporting class autoloading. |
|
233 * |
|
234 * Attach to spl_autoload() using the following: |
|
235 * <code> |
|
236 * spl_autoload_register(array('Zend_Loader', 'autoload')); |
|
237 * </code> |
|
238 * |
|
239 * @deprecated Since 1.8.0 |
|
240 * @param string $class |
|
241 * @return string|false Class name on success; false on failure |
|
242 */ |
|
243 public static function autoload($class) |
|
244 { |
|
245 trigger_error(__CLASS__ . '::' . __METHOD__ . ' is deprecated as of 1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead', E_USER_NOTICE); |
|
246 try { |
|
247 @self::loadClass($class); |
|
248 return $class; |
|
249 } catch (Exception $e) { |
|
250 return false; |
|
251 } |
|
252 } |
|
253 |
|
254 /** |
|
255 * Register {@link autoload()} with spl_autoload() |
|
256 * |
|
257 * @deprecated Since 1.8.0 |
|
258 * @param string $class (optional) |
|
259 * @param boolean $enabled (optional) |
|
260 * @return void |
|
261 * @throws Zend_Exception if spl_autoload() is not found |
|
262 * or if the specified class does not have an autoload() method. |
|
263 */ |
|
264 public static function registerAutoload($class = 'Zend_Loader', $enabled = true) |
|
265 { |
|
266 trigger_error(__CLASS__ . '::' . __METHOD__ . ' is deprecated as of 1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead', E_USER_NOTICE); |
|
267 require_once 'Zend/Loader/Autoloader.php'; |
|
268 $autoloader = Zend_Loader_Autoloader::getInstance(); |
|
269 $autoloader->setFallbackAutoloader(true); |
|
270 |
|
271 if ('Zend_Loader' != $class) { |
|
272 self::loadClass($class); |
|
273 $methods = get_class_methods($class); |
|
274 if (!in_array('autoload', (array) $methods)) { |
|
275 require_once 'Zend/Exception.php'; |
|
276 throw new Zend_Exception("The class \"$class\" does not have an autoload() method"); |
|
277 } |
|
278 |
|
279 $callback = array($class, 'autoload'); |
|
280 |
|
281 if ($enabled) { |
|
282 $autoloader->pushAutoloader($callback); |
|
283 } else { |
|
284 $autoloader->removeAutoloader($callback); |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 /** |
|
290 * Ensure that filename does not contain exploits |
|
291 * |
|
292 * @param string $filename |
|
293 * @return void |
|
294 * @throws Zend_Exception |
|
295 */ |
|
296 protected static function _securityCheck($filename) |
|
297 { |
|
298 /** |
|
299 * Security check |
|
300 */ |
|
301 if (preg_match('/[^a-z0-9\\/\\\\_.:-]/i', $filename)) { |
|
302 require_once 'Zend/Exception.php'; |
|
303 throw new Zend_Exception('Security check: Illegal character in filename'); |
|
304 } |
|
305 } |
|
306 |
|
307 /** |
|
308 * Attempt to include() the file. |
|
309 * |
|
310 * include() is not prefixed with the @ operator because if |
|
311 * the file is loaded and contains a parse error, execution |
|
312 * will halt silently and this is difficult to debug. |
|
313 * |
|
314 * Always set display_errors = Off on production servers! |
|
315 * |
|
316 * @param string $filespec |
|
317 * @param boolean $once |
|
318 * @return boolean |
|
319 * @deprecated Since 1.5.0; use loadFile() instead |
|
320 */ |
|
321 protected static function _includeFile($filespec, $once = false) |
|
322 { |
|
323 if ($once) { |
|
324 return include_once $filespec; |
|
325 } else { |
|
326 return include $filespec ; |
|
327 } |
|
328 } |
|
329 } |