]> git.mxchange.org Git - core.git/blob - inc/loader/class_ClassLoader.php
cc7bb8152237b18ce4b9aae3d8253178455d27bb
[core.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@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2013 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.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.4
26  *  - Some comments improved, other minor improvements
27  * 1.3
28  *  - Constructor is now empty and factory method 'createClassLoader' is created
29  *  - renamed loadClasses to scanClassPath
30  *  - Added initLoader()
31  * 1.2
32  *  - ClassLoader rewritten to PHP SPL's own RecursiveIteratorIterator class
33  * 1.1
34  *  - loadClasses rewritten to fix some notices
35  * 1.0
36  *  - Initial release
37  * ----------------------------------
38  */
39 class ClassLoader {
40         /**
41          * Instance of this class
42          */
43         private static $selfInstance = NULL;
44
45         /**
46          * Array with all classes
47          */
48         private $classes = array();
49
50         /**
51          * List of loaded classes
52          */
53         private $loadedClasses = array();
54
55         /**
56          * Suffix with extension for all class files
57          */
58         private $prefix = 'class_';
59
60         /**
61          * Suffix with extension for all class files
62          */
63         private $suffix = '.php';
64
65         /**
66          * A list for directory names (no leading/trailing slashes!) which not be scanned by the path scanner
67          * @see scanLocalPath
68          */
69         private $ignoreList = array();
70
71         /**
72          * Debug this class loader? (TRUE = yes, FALSE = no)
73          */
74         private $debug = FALSE;
75
76         /**
77          * Whether the file list is cached
78          */
79         private $listCached = FALSE;
80
81         /**
82          * Wethe class content has been cached
83          */
84         private $classesCached = FALSE;
85
86         /**
87          * Filename for the list cache
88          */
89         private $listCacheFQFN = '';
90
91         /**
92          * Cache for class content
93          */
94         private $classCacheFQFN = '';
95
96         /**
97          * Counter for loaded include files
98          */
99         private $total = 0;
100
101         /**
102          * Framework/application paths for classes, etc.
103          */
104         private static $frameworkPaths = array(
105                 'exceptions', // Exceptions
106                 'interfaces', // Interfaces
107                 'main',       // General main classes
108                 'middleware'  // The middleware
109         );
110
111
112         /**
113          * The protected constructor. Please use the factory method below, or use
114          * getSelfInstance() for singleton
115          *
116          * @return      void
117          */
118         protected function __construct () {
119                 // Is currently empty
120         }
121
122         /**
123          * The destructor makes it sure all caches got flushed
124          *
125          * @return      void
126          */
127         public function __destruct () {
128                 // Skip here if dev-mode
129                 if (defined('DEVELOPER')) {
130                         return;
131                 } // END - if
132
133                 // Skip here if already cached
134                 if ($this->listCached === FALSE) {
135                         // Writes the cache file of our list away
136                         $cacheContent = serialize($this->classes);
137                         file_put_contents($this->listCacheFQFN, $cacheContent);
138                 } // END - if
139
140                 // Skip here if already cached
141                 if ($this->classesCached === FALSE) {
142                         // Generate a full-cache of all classes
143                         $cacheContent = '';
144                         foreach ($this->loadedClasses as $fqfn) {
145                                 // Load the file
146                                 $cacheContent .= file_get_contents($fqfn);
147                         } // END - foreach
148
149                         // And write it away
150                         file_put_contents($this->classCacheFQFN, $cacheContent);
151                 } // END - if
152         }
153
154         /**
155          * Creates an instance of this class loader for given configuration instance
156          *
157          * @param       $configInstance         Configuration class instance
158          * @return      void
159          */
160         public static final function createClassLoader (FrameworkConfiguration $configInstance) {
161                 // Get a new instance
162                 $loaderInstance = new ClassLoader();
163
164                 // Init the instance
165                 $loaderInstance->initLoader($configInstance);
166
167                 // Return the prepared instance
168                 return $loaderInstance;
169         }
170
171         /**
172          * Scans for all framework classes, exceptions and interfaces.
173          *
174          * @return      void
175          */
176         public static function scanFrameworkClasses () {
177                 // Cache loader instance
178                 $loaderInstance = self::getSelfInstance();
179
180                 // Load all classes
181                 foreach (self::$frameworkPaths as $className) {
182                         // Try to load the framework classes
183                         $loaderInstance->scanClassPath('inc/classes/' . $className . '/');
184                 } // END - foreach
185         }
186
187         /**
188          * Scans for application's classes, etc.
189          *
190          * @return      void
191          */
192         public static function scanApplicationClasses () {
193                 // Get config instance
194                 $cfg = FrameworkConfiguration::getSelfInstance();
195
196                 // Load all classes for the application
197                 foreach (self::$frameworkPaths as $class) {
198                         // Try to load the application classes
199                         ClassLoader::getSelfInstance()->scanClassPath(sprintf('%s/%s/%s', $cfg->getConfigEntry('application_path'), $cfg->getConfigEntry('app_name'), $class));
200                 } // END - foreach
201         }
202
203         /**
204          * Initializes our loader class
205          *
206          * @param       $configInstance Configuration class instance
207          * @return      void
208          */
209         protected function initLoader (FrameworkConfiguration $configInstance) {
210                 // Set configuration instance
211                 $this->configInstance = $configInstance;
212
213                 // Construct the FQFN for the cache
214                 if (!defined('DEVELOPER')) {
215                         $this->listCacheFQFN  = $this->configInstance->getConfigEntry('local_db_path') . 'list-' . $this->configInstance->getConfigEntry('app_name') . '.cache';
216                         $this->classCacheFQFN = $this->configInstance->getConfigEntry('local_db_path') . 'class-' . $this->configInstance->getConfigEntry('app_name') . '.cache';
217                 } // END - if
218
219                 // Set suffix and prefix from configuration
220                 $this->suffix = $configInstance->getConfigEntry('class_suffix');
221                 $this->prefix = $configInstance->getConfigEntry('class_prefix');
222
223                 // Set own instance
224                 self::$selfInstance = $this;
225
226                 // Skip here if no dev-mode
227                 if (defined('DEVELOPER')) {
228                         return;
229                 } // END - if
230
231                 // IS the cache there?
232                 if (file_exists($this->listCacheFQFN)) {
233                         // Get content
234                         $cacheContent = file_get_contents($this->listCacheFQFN);
235
236                         // And convert it
237                         $this->classes = unserialize($cacheContent);
238
239                         // List has been restored from cache!
240                         $this->listCached = TRUE;
241                 } // END - if
242
243                 // Does the class cache exist?
244                 if (file_exists($this->classCacheFQFN)) {
245                         // Then include it
246                         require($this->classCacheFQFN);
247
248                         // Mark the class cache as loaded
249                         $this->classesCached = TRUE;
250                 } // END - if
251         }
252
253         /**
254          * Autoload-function
255          *
256          * @param       $className      Name of the class to load
257          * @return      void
258          */
259         public static function autoLoad ($className) {
260                 // Try to include this class
261                 self::getSelfInstance()->includeClass($className);
262         }
263
264         /**
265          * Singleton getter for an instance of this class
266          *
267          * @return      $selfInstance   A singleton instance of this class
268          */
269         public static final function getSelfInstance () {
270                 // Is the instance there?
271                 if (is_null(self::$selfInstance)) {
272                         // Get a new one
273                         self::$selfInstance = ClassLoader::createClassLoader(FrameworkConfiguration::getSelfInstance());
274                 } // END - if
275
276                 // Return the instance
277                 return self::$selfInstance;
278         }
279
280         /**
281          * Scans recursively a local path for class files which must have a prefix and a suffix as given by $this->suffix and $this->prefix
282          *
283          * @param       $basePath               The relative base path to 'base_path' constant for all classes
284          * @param       $ignoreList             An optional list (array forced) of directory and file names which shall be ignored
285          * @return      void
286          */
287         public function scanClassPath ($basePath, array $ignoreList = array() ) {
288                 // Is a list has been restored from cache, don't read it again
289                 if ($this->listCached === TRUE) {
290                         // Abort here
291                         return;
292                 } // END - if
293
294                 /*
295                  * Directories which this class loader ignores by default while
296                  * scanning the whole directory structure starting from given base
297                  * path.
298                  */
299                 array_push($ignoreList, '.');
300                 array_push($ignoreList, '..');
301                 array_push($ignoreList, '.htaccess');
302                 array_push($ignoreList, '.svn');
303
304                 // Keep it in class for later usage
305                 $this->ignoreList = $ignoreList;
306
307                 /*
308                  * Set base directory which holds all our classes, we should use an
309                  * absolute path here so is_dir(), is_file() and so on will always
310                  * find the correct files and dirs.
311                  */
312                 $basePath2 = realpath($basePath);
313
314                 // If the basePath is FALSE it is invalid
315                 if ($basePath2 === FALSE) {
316                         /* @todo: Do not die here. */
317                         exit(__METHOD__ . ':Cannot read ' . $basePath . ' !');
318                 } else {
319                         // Set base path
320                         $basePath = $basePath2;
321                 }
322
323                 // Get a new iterator
324                 //* DEBUG: */ echo "<strong>Base path: {$basePath}</strong><br />\n";
325                 $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basePath));
326
327                 foreach ($iterator as $entry) {
328                         // Get filename from iterator
329                         $fileName = $entry->getFileName();
330
331                         // Get the FQFN and add it to our class list
332                         $fqfn = $entry->getRealPath();
333
334                         // Is this file wanted?
335                         //* DEBUG: */ echo "FOUND:{$fileName}<br />\n";
336                         if ((!in_array($fileName, $this->ignoreList)) && (filesize($fqfn) > 100) && (substr($fileName, 0, strlen($this->prefix)) == $this->prefix) && (substr($fileName, -strlen($this->suffix), strlen($this->suffix)) == $this->suffix)) {
337                                 //* DEBUG: */ echo "ADD: {$fileName}<br />\n";
338                                 // Add it to the list
339                                 $this->classes[$fileName] = $fqfn;
340                         } // END - if
341                 } // END - foreach
342         }
343
344         /**
345          * Load extra config files
346          *
347          * @return      void
348          */
349         public function loadExtraConfigs () {
350                 // Backup old prefix
351                 $oldPrefix = $this->prefix;
352
353                 // Set new prefix (temporary!)
354                 $this->prefix = 'config-';
355
356                 // Set base directory
357                 $basePath = $this->configInstance->getConfigEntry('base_path') . 'inc/config/';
358
359                 // Load all classes from the config directory
360                 $this->scanClassPath($basePath);
361
362                 // Include these extra configs now
363                 $this->includeExtraConfigs();
364
365                 // Set back the old prefix
366                 $this->prefix = $oldPrefix;
367         }
368
369         /**
370          * Tries to find the given class in our list. This method ignores silently
371          * missing classes or interfaces. So if you use class_exists() this method
372          * does not interrupt your program.
373          *
374          * @param       $className      The class we shall load
375          * @return      void
376          */
377         public function includeClass ($className) {
378                 // Create a name with prefix and suffix
379                 $fileName = $this->prefix . $className . $this->suffix;
380
381                 // Now look it up in our index
382                 if ((isset($this->classes[$fileName])) && (!in_array($this->classes[$fileName], $this->loadedClasses))) {
383                         // File is found and not loaded so load it only once
384                         //* DEBUG: */ echo "LOAD: ".$fileName." - Start<br />\n";
385                         require($this->classes[$fileName]);
386                         //* DEBUG: */ echo "LOAD: ".$fileName." - End<br />\n";
387
388                         // Count this include
389                         $this->total++;
390
391                         // Mark this class as loaded
392                         array_push($this->loadedClasses, $this->classes[$fileName]);
393
394                         // Remove it from classes list
395                         unset($this->classes[$fileName]);
396
397                         // Developer mode excludes caching (better debugging)
398                         if (!defined('DEVELOPER')) {
399                                 // Reset cache
400                                 $this->classesCached = FALSE;
401                         } // END - if
402                 } // END - if
403         }
404
405         /**
406          * Includes all extra config files
407          *
408          * @return      void
409          */
410         private function includeExtraConfigs () {
411                 // Run through all class names (should not be much)
412                 foreach ($this->classes as $fileName => $fqfn) {
413                         // Is this a config?
414                         if (substr($fileName, 0, strlen($this->prefix)) == $this->prefix) {
415                                 // Then include it
416                                 require($fqfn);
417
418                                 // Remove it from the list
419                                 unset($this->classes[$fileName]);
420                         } // END - if
421                 } // END - foreach
422         }
423
424         /**
425          * Getter for total include counter
426          *
427          * @return      $total  Total loaded include files
428          */
429         public final function getTotal () {
430                 return $this->total;
431         }
432
433         /**
434          * Getter for a printable list of included classes/interfaces/exceptions
435          *
436          * @param       $includeList    A printable include list
437          */
438         public function getPrintableIncludeList () {
439                 // Prepare the list
440                 $includeList = '';
441                 foreach ($this->loadedClasses as $classFile) {
442                         $includeList .= basename($classFile) . '<br />' . PHP_EOL;
443                 } // END - foreach
444
445                 // And return it
446                 return $includeList;
447         }
448 }
449
450 // [EOF]
451 ?>