1: <?php
2:
3: /**
4: * Picon Framework
5: * http://code.google.com/p/picon-framework/
6: *
7: * Copyright (C) 2011-2012 Martin Cassidy <martin.cassidy@webquub.com>
8:
9: * Picon Framework is free software: you can redistribute it and/or modify
10: * it under the terms of the GNU General Public License as published by
11: * the Free Software Foundation, either version 3 of the License, or
12: * (at your option) any later version.
13:
14: * Picon Framework is distributed in the hope that it will be useful,
15: * but WITHOUT ANY WARRANTY; without even the implied warranty of
16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: * General Public License for more details.
18:
19: * You should have received a copy of the GNU General Public License
20: * along with Picon Framework. If not, see <http://www.gnu.org/licenses/>.
21: * */
22:
23: namespace picon;
24:
25: /**
26: * Auto loads classes via the PHP autoload handle
27: * Works through the cache manager to prevent directory scanning for the
28: * same thing time and time again
29: *
30: * @author Martin Cassidy
31: * @package core
32: */
33: class AutoLoader
34: {
35: const AUTO_LOAD_RESOURCE_NAME = 'auto_loader';
36:
37: private $scannedDirectories = array();
38: private $cachedPaths = null;
39:
40: public function __construct()
41: {
42: $this->cachedPaths = CacheManager::loadResource(self::AUTO_LOAD_RESOURCE_NAME, CacheManager::APPLICATION_SCOPE);
43:
44: if($this->cachedPaths==null)
45: {
46: $this->cachedPaths = array();
47: }
48:
49: spl_autoload_register(array($this, "autoLoad"));
50: }
51:
52: /**
53: * Add a directory to the class auto load scanner for a particular namespace
54: * @param String $directory The directory to scan
55: * @param String $namespace The namespace (optional, if your class is in
56: * the default namespace leave this blank)
57: */
58: public function addScannedDirectory($directory, $namespace = 'default')
59: {
60: if(!array_key_exists($namespace, $this->scannedDirectories))
61: {
62: $this->scannedDirectories[$namespace] = array();
63: }
64: array_push($this->scannedDirectories[$namespace], $directory);
65: }
66:
67: /**
68: * Internal method for loading classes, used by spl_autoload_register
69: *
70: * This method will trigger php error on failure instead of throwing exceptions
71: * as exceptions are caught internally by the php auto loader
72: *
73: * @param String $className the name of the class to load, including the
74: * namespace e.g. picon\RequestProcessor
75: */
76: protected function autoLoad($className)
77: {
78: if(array_key_exists($className, $this->cachedPaths))
79: {
80: require_once($this->cachedPaths[$className]);
81: return;
82: }
83:
84: $success = false;
85: $path = explode("\\", $className);
86:
87: $class = $path[count($path)-1];
88: unset($path[count($path)-1]);
89: $namespace = implode("\\", $path);
90:
91: if(empty($namespace))
92: {
93: $namespace = "default";
94: }
95:
96: if(substr($namespace, 0, 1)=='\\')
97: {
98: $namespace = substr($namespace, 1, strlen($namespace)-1);
99: }
100:
101: if(!array_key_exists($namespace, $this->scannedDirectories))
102: {
103: $this->onFail($namespace, $class);
104: }
105:
106: foreach($this->scannedDirectories[$namespace] as $dir)
107: {
108: $success = $this->loadClass($dir, $class, $className);
109: if($success)
110: {
111: break;
112: }
113: }
114: if(!$success)
115: {
116: $this->onFail($namespace, $class);
117: }
118: }
119:
120: /**
121: * Called when the auto loader fails to load the requested class
122: * @param type $namespace
123: * @param type $class
124: */
125: protected function onFail($namespace, $class)
126: {
127: trigger_error(sprintf("Unable to load class %s in namespace %s, please check that the name of file matches tha name of the class and that the file is in a directory that is used by the class scanner", $class, $namespace),E_USER_ERROR);
128: }
129:
130: /**
131: * Load a class from a directory, this is recursive and will also search
132: * sub directories.
133: * @param string $directory The root directory for the class
134: * @param string $className The simple name of the class
135: * @param string $fullName The fully qualified name of the class
136: * @return boolean true if the class file was found, false if not
137: */
138: private function loadClass($directory, $className, $fullName)
139: {
140: $d = dir($directory);
141: $success = false;
142: while ((false !== ($entry = $d->read()))&&!$success)
143: {
144: if($entry==$className.'.php')
145: {
146: require_once($directory."/".$entry);
147: $this->cachedPaths[$fullName] = $directory."/".$entry;
148: $success = true;
149: }
150: if(is_dir($directory."/".$entry) && !preg_match("/^\\.{1}\\.?$/", $entry))
151: {
152: $success = $this->loadClass($directory."/".$entry,$className, $fullName);
153: }
154: }
155: $d->close();
156: return $success;
157: }
158:
159: public function __destruct()
160: {
161: CacheManager::saveResource(self::AUTO_LOAD_RESOURCE_NAME, $this->cachedPaths, CacheManager::APPLICATION_SCOPE);
162: }
163: }
164:
165: ?>
166: