|
|
@@ -22,100 +22,113 @@ namespace Cake\Core;
|
|
|
class ClassLoader {
|
|
|
|
|
|
/**
|
|
|
- * File extension
|
|
|
+ * An associative array where the key is a namespace prefix and the value
|
|
|
+ * is an array of base directories for classes in that namespace.
|
|
|
*
|
|
|
- * @var string
|
|
|
+ * @var array
|
|
|
*/
|
|
|
- protected $_fileExtension;
|
|
|
+ protected $_prefixes = [];
|
|
|
|
|
|
/**
|
|
|
- * The path a given namespace maps to.
|
|
|
+ * Register loader with SPL autoloader stack.
|
|
|
*
|
|
|
- * @var string
|
|
|
+ * @return void
|
|
|
*/
|
|
|
- protected $_path;
|
|
|
+ public function register() {
|
|
|
+ spl_autoload_register([$this, 'loadClass']);
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- * Registered namespace
|
|
|
+ * Adds a base directory for a namespace prefix.
|
|
|
*
|
|
|
- * @var string
|
|
|
+ * @param string $prefix The namespace prefix.
|
|
|
+ * @param string $baseDir A base directory for class files in the
|
|
|
+ * namespace.
|
|
|
+ * @param bool $prepend If true, prepend the base directory to the stack
|
|
|
+ * instead of appending it; this causes it to be searched first rather
|
|
|
+ * than last.
|
|
|
+ * @return void
|
|
|
*/
|
|
|
- protected $_namespace;
|
|
|
+ public function addNamespace($prefix, $baseDir, $prepend = false) {
|
|
|
+ $prefix = trim($prefix, '\\') . '\\';
|
|
|
|
|
|
-/**
|
|
|
- * Store the namespace length for performance
|
|
|
- *
|
|
|
- * @var integer
|
|
|
- */
|
|
|
- protected $_namespaceLength;
|
|
|
+ $baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
|
|
|
+ $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
|
|
|
|
|
|
-/**
|
|
|
- * Constructor
|
|
|
- *
|
|
|
- * @param string $ns The _namespace to use.
|
|
|
- */
|
|
|
- public function __construct($ns = null, $path = null, $fileExtension = '.php') {
|
|
|
- $this->_namespace = rtrim($ns, '\\') . '\\';
|
|
|
- $this->_namespaceLength = strlen($this->_namespace);
|
|
|
- $this->_path = $path;
|
|
|
- $this->_fileExtension = '.php';
|
|
|
- }
|
|
|
+ if (!isset($this->_prefixes[$prefix])) {
|
|
|
+ $this->_prefixes[$prefix] = [];
|
|
|
+ }
|
|
|
|
|
|
-/**
|
|
|
- * Gets the base include path for all class files in the _namespace of this class loader.
|
|
|
- *
|
|
|
- * @return string
|
|
|
- */
|
|
|
- public function getIncludePath() {
|
|
|
- return $this->_includePath;
|
|
|
+ if ($prepend) {
|
|
|
+ array_unshift($this->_prefixes[$prefix], $baseDir);
|
|
|
+ } else {
|
|
|
+ array_push($this->_prefixes[$prefix], $baseDir);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Gets the file extension of class files in the _namespace of this class loader.
|
|
|
+ * Loads the class file for a given class name.
|
|
|
*
|
|
|
- * @return string
|
|
|
+ * @param string $class The fully-qualified class name.
|
|
|
+ * @return mixed The mapped file name on success, or boolean false on
|
|
|
+ * failure.
|
|
|
*/
|
|
|
- public function getFileExtension() {
|
|
|
- return $this->_fileExtension;
|
|
|
- }
|
|
|
+ public function loadClass($class) {
|
|
|
+ $prefix = $class;
|
|
|
|
|
|
-/**
|
|
|
- * Installs this class loader on the SPL autoload stack.
|
|
|
- *
|
|
|
- * @return void
|
|
|
- */
|
|
|
- public function register() {
|
|
|
- spl_autoload_register([$this, 'loadClass']);
|
|
|
+ while (($pos = strrpos($prefix, '\\')) !== false) {
|
|
|
+ $prefix = substr($class, 0, $pos + 1);
|
|
|
+ $relativeClass = substr($class, $pos + 1);
|
|
|
+
|
|
|
+ $mappedFile = $this->_loadMappedFile($prefix, $relativeClass);
|
|
|
+ if ($mappedFile) {
|
|
|
+ return $mappedFile;
|
|
|
+ }
|
|
|
+
|
|
|
+ $prefix = rtrim($prefix, '\\');
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Uninstalls this class loader from the SPL autoloader stack.
|
|
|
+ * Load the mapped file for a namespace prefix and relative class.
|
|
|
*
|
|
|
- * @return void
|
|
|
+ * @param string $prefix The namespace prefix.
|
|
|
+ * @param string $relativeClass The relative class name.
|
|
|
+ * @return mixed Boolean false if no mapped file can be loaded, or the
|
|
|
+ * name of the mapped file that was loaded.
|
|
|
*/
|
|
|
- public function unregister() {
|
|
|
- spl_autoload_unregister([$this, 'loadClass']);
|
|
|
+ protected function _loadMappedFile($prefix, $relativeClass) {
|
|
|
+ if (!isset($this->_prefixes[$prefix])) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($this->_prefixes[$prefix] as $baseDir) {
|
|
|
+ $file = $baseDir .
|
|
|
+ str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';
|
|
|
+ $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
|
|
|
+
|
|
|
+ if ($this->_requireFile($file)) {
|
|
|
+ return $file;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Loads the given class or interface.
|
|
|
+ * If a file exists, require it from the file system.
|
|
|
*
|
|
|
- * @param string $className The name of the class to load.
|
|
|
- * @return boolean
|
|
|
+ * @param string $file The file to require.
|
|
|
+ * @return bool True if the file exists, false if not.
|
|
|
*/
|
|
|
- public function loadClass($className) {
|
|
|
- if (strpos($className, 'Cake\\Test\\') === 0) {
|
|
|
- $className = substr($className, 5);
|
|
|
- } elseif (strpos($className, 'App\\Test\\') === 0) {
|
|
|
- $className = substr($className, 4);
|
|
|
- } elseif (substr($className, 0, $this->_namespaceLength) !== $this->_namespace) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- $path = $this->_path . DS . str_replace('\\', DS, $className) . $this->_fileExtension;
|
|
|
- if (!file_exists($path)) {
|
|
|
- return false;
|
|
|
+ protected function _requireFile($file) {
|
|
|
+ if (file_exists($file)) {
|
|
|
+ require $file;
|
|
|
+ return true;
|
|
|
}
|
|
|
- return require $path;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
}
|