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 23484 2010-12-10 03:57:59Z mjh_ca $
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: }
330: