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: