Complex classes like Loader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Loader, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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() |
||
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) |
||
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() |
||
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) |
||
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) |
||
421 | |||
422 | /** |
||
423 | * Setter for the inclusions classmap. |
||
424 | * |
||
425 | * @param array inclusions classmap (classname => file) |
||
426 | */ |
||
427 | public static function setInclusionsClassMap(array $classmap) |
||
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() |
||
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.