1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * Redistributions of files must retain the above copyright notice.
8: *
9: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
10: * @link https://cakephp.org CakePHP(tm) Project
11: * @since 3.0.0
12: * @license https://opensource.org/licenses/mit-license.php MIT License
13: */
14: namespace Cake\Core;
15:
16: /**
17: * ClassLoader
18: */
19: class ClassLoader
20: {
21:
22: /**
23: * An associative array where the key is a namespace prefix and the value
24: * is an array of base directories for classes in that namespace.
25: *
26: * @var array
27: */
28: protected $_prefixes = [];
29:
30: /**
31: * Register loader with SPL autoloader stack.
32: *
33: * @return void
34: */
35: public function register()
36: {
37: spl_autoload_register([$this, 'loadClass']);
38: }
39:
40: /**
41: * Adds a base directory for a namespace prefix.
42: *
43: * @param string $prefix The namespace prefix.
44: * @param string $baseDir A base directory for class files in the
45: * namespace.
46: * @param bool $prepend If true, prepend the base directory to the stack
47: * instead of appending it; this causes it to be searched first rather
48: * than last.
49: * @return void
50: */
51: public function addNamespace($prefix, $baseDir, $prepend = false)
52: {
53: $prefix = trim($prefix, '\\') . '\\';
54:
55: $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
56: $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
57:
58: if (!isset($this->_prefixes[$prefix])) {
59: $this->_prefixes[$prefix] = [];
60: }
61:
62: if ($prepend) {
63: array_unshift($this->_prefixes[$prefix], $baseDir);
64: } else {
65: $this->_prefixes[$prefix][] = $baseDir;
66: }
67: }
68:
69: /**
70: * Loads the class file for a given class name.
71: *
72: * @param string $class The fully-qualified class name.
73: * @return string|false The mapped file name on success, or boolean false on
74: * failure.
75: */
76: public function loadClass($class)
77: {
78: $prefix = $class;
79:
80: while (($pos = strrpos($prefix, '\\')) !== false) {
81: $prefix = substr($class, 0, $pos + 1);
82: $relativeClass = substr($class, $pos + 1);
83:
84: $mappedFile = $this->_loadMappedFile($prefix, $relativeClass);
85: if ($mappedFile) {
86: return $mappedFile;
87: }
88:
89: $prefix = rtrim($prefix, '\\');
90: }
91:
92: return false;
93: }
94:
95: /**
96: * Load the mapped file for a namespace prefix and relative class.
97: *
98: * @param string $prefix The namespace prefix.
99: * @param string $relativeClass The relative class name.
100: * @return mixed Boolean false if no mapped file can be loaded, or the
101: * name of the mapped file that was loaded.
102: */
103: protected function _loadMappedFile($prefix, $relativeClass)
104: {
105: if (!isset($this->_prefixes[$prefix])) {
106: return false;
107: }
108:
109: foreach ($this->_prefixes[$prefix] as $baseDir) {
110: $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';
111:
112: if ($this->_requireFile($file)) {
113: return $file;
114: }
115: }
116:
117: return false;
118: }
119:
120: /**
121: * If a file exists, require it from the file system.
122: *
123: * @param string $file The file to require.
124: * @return bool True if the file exists, false if not.
125: */
126: protected function _requireFile($file)
127: {
128: if (file_exists($file)) {
129: require $file;
130:
131: return true;
132: }
133:
134: return false;
135: }
136: }
137: