Commit f22dd82f by Qiang Xue

refactored autoloader.

parent bcc83320
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
use yii\base\Exception; use yii\base\Exception;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\base\InvalidParamException; use yii\base\InvalidParamException;
use yii\base\UnknownClassException;
use yii\logging\Logger; use yii\logging\Logger;
/** /**
...@@ -54,13 +55,10 @@ class YiiBase ...@@ -54,13 +55,10 @@ class YiiBase
*/ */
public static $classMap = array(); public static $classMap = array();
/** /**
* @var array list of directories where Yii will search for new classes to be included. * @var boolean whether to search PHP include_path when autoloading unknown classes.
* The first directory in the array will be searched first, and so on. * You may want to turn this off if you are also using autoloaders from other libraries.
* This property mainly affects how [[autoload]] works.
* @see import
* @see autoload
*/ */
public static $classPath = array(); public static $enableIncludePath = true;
/** /**
* @var yii\console\Application|yii\web\Application the application instance * @var yii\console\Application|yii\web\Application the application instance
*/ */
...@@ -214,8 +212,8 @@ class YiiBase ...@@ -214,8 +212,8 @@ class YiiBase
/** /**
* Class autoload loader. * Class autoload loader.
* This method is invoked automatically when the execution encounters an unknown class. * This method is invoked automatically when PHP sees an unknown class.
* The method will attempt to include the class file as follows: * The method will attempt to include the class file according to the following procedure:
* *
* 1. Search in [[classMap]]; * 1. Search in [[classMap]];
* 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt
...@@ -224,43 +222,64 @@ class YiiBase ...@@ -224,43 +222,64 @@ class YiiBase
* 3. If the class is named in PEAR style (e.g. `PHPUnit_Framework_TestCase`), * 3. If the class is named in PEAR style (e.g. `PHPUnit_Framework_TestCase`),
* it will attempt to include the file associated with the corresponding path alias * it will attempt to include the file associated with the corresponding path alias
* (e.g. `@PHPUnit/Framework/TestCase.php`); * (e.g. `@PHPUnit/Framework/TestCase.php`);
* 4. Search in [[classPath]]; * 4. Search PHP include_path for the actual class file if [[enableIncludePath]] is true;
* 5. Return false so that other autoloaders have chance to include the class file. * 5. Return false so that other autoloaders have chance to include the class file.
* *
* @param string $className class name * @param string $className class name
* @return boolean whether the class has been loaded successfully * @return boolean whether the class has been loaded successfully
* @throws Exception if the class file does not exist * @throws InvalidConfigException if the class file does not exist
* @throws UnknownClassException if the class does not exist in the class file
*/ */
public static function autoload($className) public static function autoload($className)
{ {
$className = ltrim($className, '\\'); $className = ltrim($className, '\\');
if (isset(self::$classMap[$className])) { if (isset(self::$classMap[$className])) {
$classFile = self::$classMap[$className]; $classFile = static::getAlias(self::$classMap[$className]);
if (!is_file($classFile)) {
throw new InvalidConfigException("Class file does not exist: $classFile");
}
} else { } else {
// follow PSR-0 to determine the class file
if (($pos = strrpos($className, '\\')) !== false) { if (($pos = strrpos($className, '\\')) !== false) {
// namespaced class, e.g. yii\base\Component // namespaced class, e.g. yii\base\Component
$classFile = str_replace('\\', '/', substr($className, 0, $pos + 1)) $path = str_replace('\\', '/', substr($className, 0, $pos + 1))
. str_replace('_', '/', substr($className, $pos + 1)) . '.php'; . str_replace('_', '/', substr($className, $pos + 1)) . '.php';
} else { } else {
$classFile = str_replace('_', '/', $className) . '.php'; $path = str_replace('_', '/', $className) . '.php';
}
// try via path alias first
if (strpos($path, '/') !== false) {
$fullPath = static::getAlias('@' . $path, false);
if ($fullPath !== false && is_file($fullPath)) {
$classFile = $fullPath;
}
}
// search include_path
if (!isset($classFile) && self::$enableIncludePath) {
foreach (array_unique(explode(PATH_SEPARATOR, get_include_path())) as $basePath) {
$fullPath = $basePath . '/' . $path;
if (is_file($fullPath)) {
$classFile = $fullPath;
break;
} }
if (strpos($classFile, '/') !== false) {
// make it into a path alias
$classFile = '@' . $classFile;
} }
} }
$classFile = static::getAlias($classFile); if (!isset($classFile)) {
if ($classFile !== false && is_file($classFile)) { // return false to let other autoloaders to try loading the class
return false;
}
}
include($classFile); include($classFile);
if (class_exists($className, false) || interface_exists($className, false)) { if (class_exists($className, false) || interface_exists($className, false)) {
return true; return true;
} else { } else {
throw new Exception("Unable to find '$className' in file: $classFile"); throw new UnknownClassException("Unable to find '$className' in file: $classFile");
}
} else {
return false;
} }
} }
...@@ -268,16 +287,16 @@ class YiiBase ...@@ -268,16 +287,16 @@ class YiiBase
* Creates a new object using the given configuration. * Creates a new object using the given configuration.
* *
* The configuration can be either a string or an array. * The configuration can be either a string or an array.
* If a string, it is treated as the *object type*; if an array, * If a string, it is treated as the *object class*; if an array,
* it must contain a `class` element specifying the *object type*, and * it must contain a `class` element specifying the *object class*, and
* the rest of the name-value pairs in the array will be used to initialize * the rest of the name-value pairs in the array will be used to initialize
* the corresponding object properties. * the corresponding object properties.
* *
* The object type can be either a class name or the [[getAlias|alias]] of * The object type can be either a class name or the [[getAlias()|alias]] of
* the class. For example, * the class. For example,
* *
* - `\app\components\GoogleMap`: fully-qualified namespaced class. * - `app\components\GoogleMap`: fully-qualified namespaced class.
* - `@app/components/GoogleMap`: an alias * - `@app/components/GoogleMap`: an alias, used for non-namespaced class.
* *
* Below are some usage examples: * Below are some usage examples:
* *
......
...@@ -593,16 +593,17 @@ abstract class Module extends Component ...@@ -593,16 +593,17 @@ abstract class Module extends Component
} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
$className = StringHelper::id2camel($id) . 'Controller'; $className = StringHelper::id2camel($id) . 'Controller';
$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php'; $classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
if (!is_file($classFile)) {
return false;
}
$className = ltrim($this->controllerNamespace . '\\' . $className, '\\'); $className = ltrim($this->controllerNamespace . '\\' . $className, '\\');
Yii::$classMap[$className] = $classFile; Yii::$classMap[$className] = $classFile;
if (class_exists($className)) {
if (is_subclass_of($className, 'yii\base\Controller')) { if (is_subclass_of($className, 'yii\base\Controller')) {
$controller = new $className($id, $this); $controller = new $className($id, $this);
} elseif (YII_DEBUG && !is_subclass_of($className, 'yii\base\Controller')) { } elseif (YII_DEBUG) {
throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller."); throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller.");
} }
} }
}
return isset($controller) ? array($controller, $route) : false; return isset($controller) ? array($controller, $route) : false;
} }
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* UnknownClassException represents an exception caused by accessing an unknown class.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class UnknownClassException extends Exception
{
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii|Unknown Class');
}
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
namespace yii\base; namespace yii\base;
/** /**
* UnknownMethodException represents an exception caused by accessing unknown object methods. * UnknownMethodException represents an exception caused by accessing an unknown object method.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
......
...@@ -32,7 +32,7 @@ class User extends Component ...@@ -32,7 +32,7 @@ class User extends Component
const EVENT_AFTER_LOGOUT = 'afterLogout'; const EVENT_AFTER_LOGOUT = 'afterLogout';
/** /**
* @var string the class name of the [[identity]] object. * @var string the class name or alias of the [[identity]] object.
*/ */
public $identityClass; public $identityClass;
/** /**
...@@ -131,7 +131,7 @@ class User extends Component ...@@ -131,7 +131,7 @@ class User extends Component
$this->_identity = null; $this->_identity = null;
} else { } else {
/** @var $class Identity */ /** @var $class Identity */
$class = $this->identityClass; $class = Yii::import($this->identityClass);
$this->_identity = $class::findIdentity($id); $this->_identity = $class::findIdentity($id);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment