a1b2e8230e126e36d0af3dbde6aa0e02e7dd69a3
[shipsimu.git] / inc / loader / class_ClassLoader.php
1 <?php
2 /**
3  * This class loads class include files with a specific prefix and suffix
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright(c) 2007, 2008 Roland Haeder, this is free software
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  *
24  * ----------------------------------
25  * 1.2
26  *  - ClassLoader rewritten to PHP SPL's own RecursiveIteratorIterator class
27  * 1.1
28  *  - loadClasses rewritten to fix some notices
29  * 1.0
30  *  - Initial release
31  * ----------------------------------
32  */
33 class ClassLoader {
34         /**
35          * Configuration array
36          */
37         private $cfg = array();
38
39         /**
40          * Array with all classes
41          */
42         private $classes = array();
43
44         /**
45          * Suffix with extension for all class files
46          */
47         private $prefix = "class_";
48
49         /**
50          * Suffix with extension for all class files
51          */
52         private $suffix = ".php";
53
54         /**
55          * Length of the suffix. Will be overwritten later.
56          */
57         private $suffixLen = 0;
58
59         /**
60          * Length of the prefix. Will be overwritten later.
61          */
62         private $prefixLen = 0;
63
64         /**
65          * A list for directory names (no leading/trailing slashes!) which not be scanned by the path scanner
66          * @see scanLocalPath
67          */
68         private $ignoreList = array();
69
70         /**
71          * Debug this class loader? (true = yes, false = no)
72          */
73         private $debug = false;
74
75         /**
76          * Instance of this class
77          */
78         private static $selfInstance = null;
79
80         /**
81          * The *public* constructor
82          *
83          * @param               $cfgInstance            Configuration class instance
84          * @return      void
85          */
86         public function __construct (FrameworkConfiguration $cfgInstance) {
87                 // Set suffix and prefix from configuration
88                 $this->suffix = $cfgInstance->readConfig('class_suffix');
89                 $this->prefix = $cfgInstance->readConfig('class_prefix');
90
91                 // Estimate length of prefix and suffix for substr() function (cache)
92                 $this->suffixLen = strlen($this->suffix);
93                 $this->prefixLen = strlen($this->prefix);
94
95                 // Set configuration instance
96                 $this->cfgInstance = $cfgInstance;
97
98                 // Set own instance
99                 self::$selfInstance = $this;
100         }
101
102         /**
103          * Getter for an instance of this class
104          *
105          * @return      $selfInstance           An instance of this class
106          */
107         public final static function getInstance () {
108                 // Is the instance there?
109                 if (is_null(self::$selfInstance)) {
110                         // Get a new one
111                         self::$selfInstance = new ClassLoader(FrameworkConfiguration::getInstance());
112                 } // END - if
113
114                 // Return the instance
115                 return self::$selfInstance;
116         }
117
118         /**
119          * Scans recursively a local path for class files which must have a prefix and a suffix as given by $this->suffix and $this->prefix
120          *
121          * @param               $basePath               The relative base path to PATH constant for all classes
122          * @param               $ignoreList     An optional list (array or string) of directory names which shall be ignored
123          * @return      void
124          */
125         public function loadClasses ($basePath, $ignoreList = array() ) {
126                 // Convert string to array
127                 if (!is_array($ignoreList)) $ignoreList = array($ignoreList);
128
129                 // Directories which our class loader ignores by default while
130                 // deep-scanning the directory structure. See scanLocalPath() for
131                 // details.
132                 $ignoreList[] = ".";
133                 $ignoreList[] = "..";
134                 $ignoreList[] = ".htaccess";
135                 $ignoreList[] = ".svn";
136
137                 // Keep it in class for later usage
138                 $this->ignoreList = $ignoreList;
139
140                 // Set base directory which holds all our classes, we should use an
141                 // absolute path here so is_dir(), is_file() and so on will always
142                 // find the correct files and dirs.
143                 $basePath2 = realpath($basePath);
144
145                 // If the basePath is false it is invalid
146                 if ($basePath2 === false) {
147                         // TODO: Do not die here.
148                         die("Cannot read {$basePath} !");
149                 } else {
150                         // Set base path
151                         $basePath = $basePath2;
152                 }
153
154                 // Get a new iterator
155                 //* DEBUG: */ echo "<strong>Base path: {$basePath}</strong><br />\n";
156                 $iterator = new RecursiveDirectoryIterator($basePath);
157                 $recursive = new RecursiveIteratorIterator($iterator);
158                 foreach ($recursive as $entry) {
159                         // Get filename from iterator
160                         $fileName = $entry->getFileName();
161
162                         // Is this file wanted?
163                         //* DEBUG: */ echo "FOUND:{$fileName}<br />\n";
164                         if ((!in_array($fileName, $this->ignoreList)) && (substr($fileName, 0, $this->prefixLen) == $this->prefix) && (substr($fileName, -$this->suffixLen, $this->suffixLen) == $this->suffix)) {
165                                 // Get the FQFN and add it to our class list
166                                 $fqfn = $entry->getRealPath();
167                                 //* DEBUG: */ echo "ADD: {$fileName}<br />\n";
168                                 $this->classes[$fileName] = $fqfn;
169                         }
170                 }
171         }
172
173         /**
174          * Load extra config files
175          *
176          * @return      void
177          */
178         public function loadExtraConfigs () {
179                 // Backup old prefix
180                 $oldPrefix = $this->prefix;
181
182                 // Set new prefix (temporary!)
183                 $this->prefix = "config-";
184                 $this->prefixLen = strlen($this->prefix);
185
186                 // Set base directory
187                 $basePath = sprintf("%sinc/config/", PATH);
188
189                 // Load all classes from the config directory
190                 $this->loadClasses($basePath);
191
192                 // Set the prefix back
193                 $this->prefix = $oldPrefix;
194                 $this->prefixLen = strlen($this->prefix);
195         }
196
197         /**
198          * Tries to find the given class in our list. This method ignores silently
199          * missing classes or interfaces. So if you use class_exists() this method
200          * does not interrupt your program.
201          *
202          * @param       $className      The class we shall load
203          * @return      void
204          */
205         public function includeClass ($className) {
206                 // Create a name with prefix and suffix
207                 $fileName = $this->prefix . $className . $this->suffix;
208
209                 // Now look it up in our index
210                 if (isset($this->classes[$fileName])) {
211                         if ($this->classes[$fileName] != "loaded") {
212                                 // File is found so load it only once
213                                 require($this->classes[$fileName]);
214
215                                 // Mark it as loaded
216                                 $this->classes[$fileName] = "loaded";
217                         } // END - if
218                 } // END - if
219         }
220 }
221
222 // [EOF]
223 ?>