1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Koch Framework |
5
|
|
|
* Jens-André Koch © 2005 - onwards. |
6
|
|
|
* |
7
|
|
|
* This file is part of "Koch Framework". |
8
|
|
|
* |
9
|
|
|
* License: GNU/GPL v2 or any later version, see LICENSE file. |
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 2 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
|
|
|
namespace Koch\Autoload; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Koch Framework - Class for Autoloading of Files by Classname. |
29
|
|
|
* |
30
|
|
|
* This Loader overwrites the Zend Engines _autoload() with our own user defined loading functions. |
31
|
|
|
* The main function of this class is autoload(). |
32
|
|
|
* It's registered via spl_autoload_register($load_function). |
33
|
|
|
* Autoload will run, if a file is not found. |
34
|
|
|
* There are several loader-functions, which are called in a chain by autoload(). |
35
|
|
|
* The procedure is (1) exclusions, (2) inclusions, (3) mapping (file or apc), (4) include path (psr-0). |
36
|
|
|
* |
37
|
|
|
* Usage: |
38
|
|
|
* 1) include this file |
39
|
|
|
* 2) spl_autoload_register('Koch\Autoload\Loader::autoload'); |
40
|
|
|
* |
41
|
|
|
* PHP Manual: __autoload |
42
|
|
|
* |
43
|
|
|
* @link http://www.php.net/manual/en/language.oop5.autoload.php |
44
|
|
|
* |
45
|
|
|
* PHP Manual: spl_autoload_register |
46
|
|
|
* @link http://www.php.net/manual/de/function.spl-autoload-register.php |
47
|
|
|
*/ |
48
|
|
|
class Loader |
49
|
|
|
{ |
50
|
|
|
/** |
51
|
|
|
* Generated Classmap from File or APC. |
52
|
|
|
* |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
private static $autoloaderMap = []; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* A manually defined classmap you might set from outside. |
59
|
|
|
* |
60
|
|
|
* @var array |
61
|
|
|
*/ |
62
|
|
|
public static $inclusionsClassmap = []; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Path to mapfile. |
66
|
|
|
* |
67
|
|
|
* @var string |
68
|
|
|
*/ |
69
|
|
|
public static $mapfile = ''; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Constructor. |
73
|
|
|
* |
74
|
|
|
* Registers the autoload() method in the SPL autoloader stack. |
75
|
|
|
*/ |
76
|
|
|
public function __construct() |
77
|
|
|
{ |
78
|
|
|
spl_autoload_register([$this, 'autoload'], true, true); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Autoloads a Class. |
83
|
|
|
* |
84
|
|
|
* @param string $classname The name of the class |
85
|
|
|
* |
86
|
|
|
* @return bool True on successful class loading, false otherwise. |
87
|
|
|
*/ |
88
|
|
|
public static function autoload($classname) |
|
|
|
|
89
|
|
|
{ |
90
|
|
|
// stop early, if class or interface or trait already loaded |
91
|
|
|
if (class_exists($classname, false) || interface_exists($classname, false)) { |
92
|
|
|
return false; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
// stop early, if trait already loaded (PHP 5.4) |
96
|
|
|
if (function_exists('trait_exists') && trait_exists($classname, false)) { |
97
|
|
|
return false; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/* |
101
|
|
|
* if the classname is to exclude, then |
102
|
|
|
* 1) stop autoloading immediately by |
103
|
|
|
* returning false, to save any pointless processing |
104
|
|
|
*/ |
105
|
|
|
if (self::autoloadExclusions($classname)) { |
106
|
|
|
return false; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/* |
110
|
|
|
* try to load the file by searching the |
111
|
|
|
* 2) hardcoded mapping table |
112
|
|
|
* |
113
|
|
|
* Note: If classname was included, autoloadInclusions returns true. |
114
|
|
|
*/ |
115
|
|
|
|
116
|
|
|
return true === self::autoloadInclusions($classname); |
117
|
|
|
|
118
|
|
|
/* |
119
|
|
|
* try to load the file by searching the |
120
|
|
|
* 3) automatically created mapping table. |
121
|
|
|
* |
122
|
|
|
* Note: the mapping table is loaded from APC or file. |
123
|
|
|
*/ |
124
|
|
|
|
125
|
|
|
return true === self::autoloadByApcOrFileMap($classname); |
|
|
|
|
126
|
|
|
|
127
|
|
|
/* |
128
|
|
|
* Try to load the file via include path lookup. |
129
|
|
|
* 5) psr-0 loader |
130
|
|
|
* |
131
|
|
|
* Note: If the file is found, it's added to the mapping file. |
132
|
|
|
* The next time the file is requested, it will be loaded |
133
|
|
|
* via the method above (3)! |
134
|
|
|
*/ |
135
|
|
|
|
136
|
|
|
return true === self::autoloadIncludePath($classname); |
|
|
|
|
137
|
|
|
|
138
|
|
|
/* |
139
|
|
|
* If classname was not found by any of the above methods, it's an |
140
|
|
|
* 6) Autoloading Fail |
141
|
|
|
*/ |
142
|
|
|
|
143
|
|
|
return false; |
|
|
|
|
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Excludes a certain classname from the autoloading. |
148
|
|
|
* |
149
|
|
|
* Some libraries have their own autoloaders, like e.g. Doctrine, Smarty. |
150
|
|
|
* In these cases Koch Framework has the first autoloader in the stack, |
151
|
|
|
* but is not responsible for loading. |
152
|
|
|
* |
153
|
|
|
* @param string $classname Classname to check for exclusion. |
154
|
|
|
* |
155
|
|
|
* @return bool true, if the class is to exclude. |
156
|
|
|
*/ |
157
|
|
|
public static function autoloadExclusions($classname) |
|
|
|
|
158
|
|
|
{ |
159
|
|
|
// define parts of classnames for exclusion |
160
|
|
|
foreach (['Smarty_Internal', 'Smarty_', 'PHPUnit', 'PHP_CodeCoverage'] as $classnameToExclude) { |
161
|
|
|
if (false !== strpos($classname, $classnameToExclude)) { |
162
|
|
|
return true; |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// exlude Doctrine |
167
|
|
|
if (substr($classname, 0, 8) === 'Doctrine') { |
|
|
|
|
168
|
|
|
return true; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return false; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Includes a certain classname by using a manually maintained autoloading map. |
176
|
|
|
* |
177
|
|
|
* @param string $classname Classname to check for inclusion. |
178
|
|
|
* |
179
|
|
|
* @return bool if classname was included |
180
|
|
|
*/ |
181
|
|
|
public static function autoloadInclusions($classname) |
|
|
|
|
182
|
|
|
{ |
183
|
|
|
// check if classname is in autoloading map |
184
|
|
|
if (isset(self::$inclusionsClassmap[$classname])) { |
185
|
|
|
include self::$inclusionsClassmap[$classname]; |
186
|
|
|
|
187
|
|
|
return true; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
return false; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Loads a file by classname using the autoloader mapping array from file or apc. |
195
|
|
|
* |
196
|
|
|
* @param string $classname The classname to look for in the autoloading map. |
197
|
|
|
* |
198
|
|
|
* @return bool True on file load, otherwise false. |
199
|
|
|
*/ |
200
|
|
|
public static function autoloadByApcOrFileMap($classname) |
|
|
|
|
201
|
|
|
{ |
202
|
|
|
if (empty(self::$autoloaderMap)) { |
203
|
|
|
if (defined('APC') and APC === true) { |
|
|
|
|
204
|
|
|
self::$autoloaderMap = self::readAutoloadingMapApc(); |
205
|
|
|
} else { // load the mapping from file |
206
|
|
|
self::$autoloaderMap = self::readAutoloadingMapFile(); |
|
|
|
|
207
|
|
|
} |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
if (isset(self::$autoloaderMap[$classname])) { |
211
|
|
|
include_once self::$autoloaderMap[$classname]; |
212
|
|
|
|
213
|
|
|
return true; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return false; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* PSR-0 Loader. |
221
|
|
|
* |
222
|
|
|
* - hardcoded namespaceSeparator |
223
|
|
|
* - hardcoded extension |
224
|
|
|
* |
225
|
|
|
* @link https://groups.google.com/group/php-standards/web/psr-0-final-proposal |
226
|
|
|
* @link http://gist.github.com/221634 |
227
|
|
|
* |
228
|
|
|
* @param string $classname |
229
|
|
|
* |
230
|
|
|
* @return bool True on success of require, false otherwise. |
231
|
|
|
*/ |
232
|
|
|
public static function autoloadIncludePath($classname) |
|
|
|
|
233
|
|
|
{ |
234
|
|
|
#echo "Class requested $classname <br>"; |
235
|
|
|
|
236
|
|
|
// trim opening namespace separator |
237
|
|
|
$classname = ltrim($classname, '\\'); |
238
|
|
|
|
239
|
|
|
$filename = ''; |
240
|
|
|
|
241
|
|
|
// determine position of last namespace separator |
242
|
|
|
if (false !== ($lastNsPos = strripos($classname, '\\'))) { |
243
|
|
|
// everything before it, is the namespace |
244
|
|
|
$namespace = substr($classname, 0, $lastNsPos + 1); |
245
|
|
|
// everything after it, is the classname |
246
|
|
|
$classname = substr($classname, $lastNsPos + 1); |
247
|
|
|
// replace every namespace separator with a directory separator |
248
|
|
|
$filename = str_replace('\\', DIRECTORY_SEPARATOR, $namespace); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
// convert underscore |
252
|
|
|
$filename .= str_replace('_', DIRECTORY_SEPARATOR, $classname) . '.php'; |
253
|
|
|
|
254
|
|
|
#echo "$classname => $filename <br>"; |
255
|
|
|
|
256
|
|
|
// searches on include path for the file and returns absolute path |
257
|
|
|
$filename = stream_resolve_include_path($filename); |
258
|
|
|
|
259
|
|
|
#echo "$classname => $filename => $namespace<br>"; |
260
|
|
|
|
261
|
|
|
if (is_string($filename)) { |
262
|
|
|
return self::includeFileAndMap($filename, $classname); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return false; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* Include File (and register it to the autoloading map file). |
270
|
|
|
* |
271
|
|
|
* This procedure ensures, that the autoload mapping array dataset |
272
|
|
|
* is increased stepwise resulting in a decreasing number of autoloading tries. |
273
|
|
|
* |
274
|
|
|
* @param string $filename The file to be required |
275
|
|
|
* @param string $classname |
276
|
|
|
* |
277
|
|
|
* @return bool True on success of require, false otherwise. |
278
|
|
|
*/ |
279
|
|
|
public static function includeFileAndMap($filename, $classname) |
|
|
|
|
280
|
|
|
{ |
281
|
|
|
$filename = realpath($filename); |
282
|
|
|
|
283
|
|
|
// conditional include |
284
|
|
|
include_once $filename; |
285
|
|
|
|
286
|
|
|
// add class and filename to the mapping array |
287
|
|
|
self::addMapping($classname, $filename); |
288
|
|
|
|
289
|
|
|
return true; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Includes a file, if found. |
294
|
|
|
* |
295
|
|
|
* @param string $filename The file to be included |
296
|
|
|
* @param string $classname (Optional) The classname expected inside this file. |
|
|
|
|
297
|
|
|
* |
298
|
|
|
* @return bool True on success of include, false otherwise. |
299
|
|
|
*/ |
300
|
|
|
public static function includeFile($filename, $classname = null) |
|
|
|
|
301
|
|
|
{ |
302
|
|
|
$filename = realpath($filename); |
303
|
|
|
|
304
|
|
|
if (is_file($filename)) { |
305
|
|
|
include $filename; |
306
|
|
|
|
307
|
|
|
if (null === $classname || class_exists($classname, false)) { |
308
|
|
|
return true; |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
return false; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Writes the autoload mapping array into a file. |
317
|
|
|
* The target file is ROOT.'configuration/'.self::$autoloader |
318
|
|
|
* The content to be written is an associative array $array, |
319
|
|
|
* consisting of the old mapping array appended by a new mapping. |
320
|
|
|
* |
321
|
|
|
* @param $array associative array with relation of a classname to a filename |
322
|
|
|
*/ |
323
|
|
|
public static function writeAutoloadingMapFile($array) |
|
|
|
|
324
|
|
|
{ |
325
|
|
|
if (is_writable(self::$mapfile) === false) { |
326
|
|
|
// create map file first |
327
|
|
|
self::readAutoloadingMapFile(); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
if (is_writable(self::$mapfile)) { |
331
|
|
|
return (bool) file_put_contents(self::$mapfile, serialize($array), LOCK_EX); |
332
|
|
|
} else { |
333
|
|
|
throw new \RuntimeException('Autoload cache file not writable: ' . self::$mapfile); |
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Reads the content of the autoloading map file and returns it unserialized. |
339
|
|
|
* |
340
|
|
|
* @return array<string> file content of autoload.config file |
|
|
|
|
341
|
|
|
*/ |
342
|
|
|
public static function readAutoloadingMapFile() |
343
|
|
|
{ |
344
|
|
|
// create file, if not existant |
345
|
|
|
if (is_file(self::$mapfile)) { |
346
|
|
|
$file_resource = fopen(self::$mapfile, 'a', false); |
|
|
|
|
347
|
|
|
fclose($file_resource); |
|
|
|
|
348
|
|
|
unset($file_resource); |
|
|
|
|
349
|
|
|
|
350
|
|
|
return []; |
351
|
|
|
} else { // load map from file |
352
|
|
|
try { |
353
|
|
|
return (array) unserialize(file_get_contents(self::$mapfile)); |
354
|
|
|
} catch (\Exception $e) { |
355
|
|
|
// delete mapfile, on unserialization error (error at offset xy) |
356
|
|
|
unlink(self::$mapfile); |
357
|
|
|
} |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Reads the autoload mapping array from APC. |
363
|
|
|
* |
364
|
|
|
* @return array automatically generated classmap |
365
|
|
|
*/ |
366
|
|
|
public static function readAutoloadingMapApc() |
367
|
|
|
{ |
368
|
|
|
return apc_fetch('KF_CLASSMAP'); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* Writes the autoload mapping array to APC. |
373
|
|
|
* |
374
|
|
|
* @return bool automatically generated classmap |
375
|
|
|
* @return bool True if stored. |
376
|
|
|
*/ |
377
|
|
|
public static function writeAutoloadingMapApc($array) |
|
|
|
|
378
|
|
|
{ |
379
|
|
|
return apc_store('KF_CLASSMAP', $array); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Adds a new $classname to $filename mapping to the map array. |
384
|
|
|
* The new map array is written to apc or file. |
385
|
|
|
* |
386
|
|
|
* @param string $class Classname is the lookup key for $filename. |
387
|
|
|
* @param string $file Filename is the file to load. |
388
|
|
|
* |
389
|
|
|
* @return bool True if added to map. |
390
|
|
|
*/ |
391
|
|
|
public static function addMapping($class, $file) |
|
|
|
|
392
|
|
|
{ |
393
|
|
|
self::$autoloaderMap = array_merge((array) self::$autoloaderMap, [$class => $file]); |
394
|
|
|
|
395
|
|
|
if (defined('APC') and APC === true) { |
|
|
|
|
396
|
|
|
return self::writeAutoloadingMapApc(self::$autoloaderMap); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
return self::writeAutoloadingMapFile(self::$autoloaderMap); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Getter for the autoloader classmap. |
404
|
|
|
* |
405
|
|
|
* @return array autoloader classmap. |
406
|
|
|
*/ |
407
|
|
|
public static function getAutoloaderClassMap() |
408
|
|
|
{ |
409
|
|
|
return self::$autoloaderMap; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Setter for the classmap file. |
414
|
|
|
* |
415
|
|
|
* @param string classmap filepath. |
416
|
|
|
*/ |
417
|
|
|
public static function setClassMapFile($mapfile) |
418
|
|
|
{ |
419
|
|
|
self::$mapfile = $mapfile; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Setter for the inclusions classmap. |
424
|
|
|
* |
425
|
|
|
* @param array inclusions classmap (classname => file) |
426
|
|
|
*/ |
427
|
|
|
public static function setInclusionsClassMap(array $classmap) |
428
|
|
|
{ |
429
|
|
|
self::$inclusionsClassmap = $classmap; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* Registers the autoloader. |
434
|
|
|
*/ |
435
|
|
|
public static function register($mapfile) |
436
|
|
|
{ |
437
|
|
|
self::setClassMapFile($mapfile); |
438
|
|
|
|
439
|
|
|
spl_autoload_register([__CLASS__, 'autoload'], true, true); |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* Unregisters the autoloader. |
444
|
|
|
*/ |
445
|
|
|
public static function unregister() |
446
|
|
|
{ |
447
|
|
|
spl_autoload_unregister([__CLASS__, 'autoload']); |
448
|
|
|
} |
449
|
|
|
} |
450
|
|
|
|
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.