(no commit message)
[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  * ----------------------------------
6  * 1.1
7  *  - loadClasses rewritten to fix some notices
8  * 1.0
9  *  - Initial release
10  * ----------------------------------
11  *
12  * @author              Roland Haeder <webmaster@ship-simu.org>
13  * @version             1.1
14  * @copyright   Copyright(c) 2007, 2008 Roland Haeder, this is free software
15  * @license             GNU GPL 3.0 or any newer version
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
29  */
30 class ClassLoader {
31         /**
32          * Configuration array
33          */
34         private $cfg = array();
35
36         /**
37          * An ArrayObject for found classes
38          */
39         private $classes = null;
40
41         /**
42          * Suffix with extension for all class files
43          */
44         private $prefix = "class_";
45
46         /**
47          * Suffix with extension for all class files
48          */
49         private $suffix = ".php";
50
51         /**
52          * Length of the suffix. Will be overwritten later.
53          */
54         private $sufLen = 0;
55
56         /**
57          * Length of the prefix. Will be overwritten later.
58          */
59         private $preLen = 0;
60
61         /**
62          * A list for directory names (no leading/trailing slashes!) which not be scanned by the path scanner
63          * @see scanLocalPath
64          */
65         private $ignoreList = array();
66
67         /**
68          * An ArrayList object for include directories
69          */
70         private $dirList = null;
71
72         /**
73          * Debug this class loader? (true = yes, false = no)
74          */
75         private $debug = false;
76
77         /**
78          * Counter for scanned directories (debug output)
79          */
80         private $dirCnt = 0;
81
82         /**
83          * Counter for loaded classes (debug output)
84          */
85         private $classCnt = 0;
86
87         /**
88          * Instance of this class
89          */
90         private static $thisInstance = null;
91
92         /**
93          * The *public* constructor
94          *
95          * @param               $cfgInstance            Configuration class instance
96          * @return      void
97          */
98         public function __construct (FrameworkConfiguration $cfgInstance) {
99                 // Init the array list
100                 $this->dirList = new ArrayObject();
101
102                 // Set suffix and prefix from configuration
103                 $this->suffix = $cfgInstance->readConfig("class_suffix");
104                 $this->prefix = $cfgInstance->readConfig("class_prefix");
105
106                 // Estimate length of prefix and suffix for substr() function (cache)
107                 $this->sufLen = strlen($this->suffix);
108                 $this->preLen = strlen($this->prefix);
109
110                 // Set configuration instance
111                 $this->cfgInstance = $cfgInstance;
112
113                 // Initialize the classes list
114                 $this->classes = new ArrayObject();
115
116                 // Set own instance
117                 self::$thisInstance = $this;
118         }
119
120         /**
121          * Getter for an instance of this class
122          *
123          * @return      $thisInstance           An instance of this class
124          */
125         public final static function getInstance () {
126                 return self::$thisInstance;
127         }
128
129         /**
130          * Scans recursively a local path for class files which must have a prefix and a suffix as given by $this->suffix and $this->prefix
131          *
132          * @param               $basePath               The relative base path to PATH constant for all classes
133          * @param               $ignoreList     An optional list (array or string) of directory names which shall be ignored
134          * @return      void
135          */
136         public function loadClasses ($basePath, $ignoreList = array() ) {
137                 // Convert string to array
138                 if (!is_array($ignoreList)) $ignoreList = array($ignoreList);
139
140                 // Directories which our class loader ignores by default while
141                 // deep-scanning the directory structure. See scanLocalPath() for
142                 // details.
143                 $ignoreList[] = ".";
144                 $ignoreList[] = "..";
145                 $ignoreList[] = ".htaccess";
146
147                 // Keep it in class for later usage
148                 $this->ignoreList = $ignoreList;
149
150                 // Set base directory which holds all our classes, we should use an
151                 // absolute path here so is_dir(), is_file() and so on will always
152                 // find the correct files and dirs.
153                 $basePath2 = realpath($basePath);
154
155                 // If the basePath is false it is invalid
156                 if ($basePath2 === false) {
157                         // TODO: Do not die here.
158                         die("Cannot read {$basePath} !");
159                 } else {
160                         // Set base path
161                         $basePath = $basePath2;
162                 }
163
164                 // Load all super classes (backward, why ever this name... :-? )
165                 // We don't support sub directories here...
166                 $this->scanLocalPath($basePath);
167
168                 // While there are directories in our list scan them for classes
169                 $cnt = 0;
170                 while ($cnt != $this->dirList->count()) {
171                         for ($idx = $this->dirList->getIterator(); $idx->valid(); $idx->next()) {
172                                 // Get current path
173                                 $currPath = $idx->current();
174
175                                 // Remove the current entry or else this will lead into a infinite loop
176                                 $this->dirList->offsetSet($idx->key(), "");
177
178                                 // Scan the directory
179                                 $this->scanLocalPath($currPath);
180                         }
181
182                         // Check if we can leave
183                         $cnt = 0;
184                         for ($idx = $this->dirList->getIterator(); $idx->valid(); $idx->next()) {
185                                 if ($idx->current() == "") $cnt++;
186                         }
187                 }
188         }
189
190         /**
191         * The local path scanner. A found class will be loaded immediately
192         * @param                $localPath      The local path which shall be recursively scanned for include files
193         * @return               void
194         */
195         private function scanLocalPath ($localPath) {
196                 // Empty path names will be silently ignored
197                 if (empty($localPath)) return;
198
199                 // TODO: No dies here, mayybe this should be rewritten to throw an exception?
200                 $dirInstance = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($localPath);
201                 while ($dirClass = $dirInstance->readDirectoryExcept($this->ignoreList)) {
202                         // We need the relative dir name as an array index some lines below
203                         $dirClass2 = $dirClass;
204
205                         // A nice replacement for a simple dot ;)
206                         $dirClass = sprintf("%s/%s", $localPath, $dirClass);
207
208                         // Is a readable file with configured prefix and suffix? All other
209                         // files will silently be ignored!
210                         //* DEBUG: */ print "Prefix=".$this->prefix."(".substr($dirClass2, 0 , $this->preLen).")\n";
211                         //* DEBUG: */ print "Suffix=".$this->suffix."(".substr($dirClass2, -$this->sufLen, $this->sufLen).")\n";
212                         //* DEBUG: */ print "ENTRY={$dirClass}\n";
213                         if (
214                            (is_file($dirClass))
215                         && (is_readable($dirClass))
216                         && (substr($dirClass2, 0 , $this->preLen) == $this->prefix)
217                         && (substr($dirClass2, -$this->sufLen, $this->sufLen) == $this->suffix)
218                         ) {
219                                 // Class found so load it instantly
220                                 //* DEBUG: */ print "CLASS={$dirClass}\n";
221                                 $this->classes->append($dirClass);
222                                 $this->classCnt++;
223                         } elseif (is_dir($dirClass) && !in_array($dirClass2, $this->ignoreList)) {
224                                 // Directory found and added to list
225                                 //* DEBUG: */ print "DIR={$dirClass}\n";
226                                 if ($dirClass2 == "interfaces") {
227                                         $this->scanLocalPath($dirClass);
228                                 } else {
229                                         $this->dirList->append($dirClass);
230                                 }
231                                 $this->dirCnt++;
232                         }
233                         //* DEBUG: */ print "LOOP!\n";
234                 } // END - while
235
236                 // Close directory handler
237                 $dirInstance->closeDirectory();
238
239                 // Output counter in debug mode
240                 if (defined('DEBUG_MODE')) print(sprintf("[%s:] <strong>%d</strong> Klassendateien in <strong>%d</strong> Verzeichnissen gefunden und geladen.<br />\n",
241                         __CLASS__,
242                         $this->classCnt,
243                         $this->dirCnt
244                 ));
245         }
246
247         /**
248          * Includes all found classes
249          * @return      void
250          */
251         public function includeAllClasses () {
252                 if (is_object($this->classes)) {
253                         // Load all classes
254                         for ($idx = $this->classes->getIterator(); $idx->valid(); $idx->next()) {
255                                 // Load current class
256                                 //* DEBUG: */ print "Class=".$idx->current()."\n";
257                                 require_once($idx->current());
258                         }
259
260                         // Re-initialize the classes list
261                         $this->classes = new ArrayObject();
262                 }
263         }
264 }
265
266 // Initial load of core classes and the FrameworkDirectoryPointer class
267 require_once(sprintf("%sinc/classes/interfaces/class_FrameworkInterface%s", PATH, FrameworkConfiguration::getInstance()->readConfig("php_extension")));
268 require_once(sprintf("%sinc/classes/main/class_BaseFrameworkSystem%s", PATH, FrameworkConfiguration::getInstance()->readConfig("php_extension")));
269 require_once(sprintf("%sinc/classes/main/io/class_FrameworkDirectoryPointer%s", PATH, FrameworkConfiguration::getInstance()->readConfig("php_extension")));
270
271 // Initialize the class loader
272 $loader = new ClassLoader(FrameworkConfiguration::getInstance());
273
274 // [EOF]
275 ?>