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