Skip to content

Commit

Permalink
Changed to PSR-4 class loader.
Browse files Browse the repository at this point in the history
  • Loading branch information
ADmad committed Jan 9, 2014
1 parent fd27c25 commit 4a3a6a6
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 71 deletions.
141 changes: 77 additions & 64 deletions Cake/Core/ClassLoader.php
Expand Up @@ -22,100 +22,113 @@
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;
}

}
4 changes: 3 additions & 1 deletion Cake/Core/Plugin.php
Expand Up @@ -142,7 +142,9 @@ public static function load($plugin, $config = []) {
}

if ($config['autoload'] === true) {
(new ClassLoader($config['namespace'], dirname($config['path'])))->register();
$loader = new ClassLoader;
$loader->register();
$loader->addNamespace($config['namespace'], $config['path']);
}
}

Expand Down
2 changes: 1 addition & 1 deletion Test/TestCase/Core/PluginTest.php
Expand Up @@ -104,7 +104,7 @@ public function testLoadSingleWithAutoload() {
$this->assertFalse(class_exists('Company\TestPluginThree\Utility\Hello'));
Plugin::load('TestPluginThree', [
'namespace' => 'Company\TestPluginThree',
'path' => TEST_APP . 'Plugin/TestPluginThree',
'path' => TEST_APP . 'Plugin/Company/TestPluginThree',
'autoload' => true,
]);
$this->assertTrue(
Expand Down
14 changes: 9 additions & 5 deletions Test/init.php
Expand Up @@ -51,11 +51,15 @@

require CORE_PATH . 'Cake/Core/ClassLoader.php';

(new Cake\Core\ClassLoader('Cake', ROOT))->register();
(new Cake\Core\ClassLoader('TestApp', ROOT . '/Test'))->register();
(new Cake\Core\ClassLoader('TestPlugin', CORE_TESTS . 'TestApp/Plugin/'))->register();
(new Cake\Core\ClassLoader('TestPluginTwo', CORE_TESTS . 'TestApp/Plugin/'))->register();
(new Cake\Core\ClassLoader('PluginJs', CORE_TESTS . 'TestApp/Plugin/'))->register();
$loader = new Cake\Core\ClassLoader;
$loader->register();

$loader->addNamespace('Cake', ROOT . '/Cake');
$loader->addNamespace('Cake\Test', ROOT . '/Test');
$loader->addNamespace('TestApp', ROOT . '/Test/TestApp');
$loader->addNamespace('TestPlugin', CORE_TESTS . 'TestApp/Plugin/TestPlugin');
$loader->addNamespace('TestPluginTwo', CORE_TESTS . 'TestApp/Plugin/TestPluginTwo');
$loader->addNamespace('PluginJs', CORE_TESTS . 'TestApp/Plugin/PluginJs');

require CORE_PATH . 'Cake/bootstrap.php';

Expand Down

0 comments on commit 4a3a6a6

Please sign in to comment.