diff --git a/framework/YiiBase.php b/framework/YiiBase.php index 7b3acee..ceaeda1 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -7,6 +7,7 @@ use yii\base\Exception; use yii\base\InvalidConfigException; use yii\base\InvalidParamException; +use yii\base\UnknownClassException; use yii\logging\Logger; /** @@ -54,13 +55,10 @@ class YiiBase */ public static $classMap = array(); /** - * @var array list of directories where Yii will search for new classes to be included. - * The first directory in the array will be searched first, and so on. - * This property mainly affects how [[autoload]] works. - * @see import - * @see autoload + * @var boolean whether to search PHP include_path when autoloading unknown classes. + * You may want to turn this off if you are also using autoloaders from other libraries. */ - public static $classPath = array(); + public static $enableIncludePath = true; /** * @var yii\console\Application|yii\web\Application the application instance */ @@ -214,8 +212,8 @@ class YiiBase /** * Class autoload loader. - * This method is invoked automatically when the execution encounters an unknown class. - * The method will attempt to include the class file as follows: + * This method is invoked automatically when PHP sees an unknown class. + * The method will attempt to include the class file according to the following procedure: * * 1. Search in [[classMap]]; * 2. If the class is namespaced (e.g. `yii\base\Component`), it will attempt @@ -224,43 +222,64 @@ class YiiBase * 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 * (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. * * @param string $className class name * @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) { $className = ltrim($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 { + // follow PSR-0 to determine the class file if (($pos = strrpos($className, '\\')) !== false) { // 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'; } else { - $classFile = str_replace('_', '/', $className) . '.php'; + $path = str_replace('_', '/', $className) . '.php'; } - if (strpos($classFile, '/') !== false) { - // make it into a path alias - $classFile = '@' . $classFile; + + // try via path alias first + if (strpos($path, '/') !== false) { + $fullPath = static::getAlias('@' . $path, false); + if ($fullPath !== false && is_file($fullPath)) { + $classFile = $fullPath; + } } - } - $classFile = static::getAlias($classFile); - if ($classFile !== false && is_file($classFile)) { - include($classFile); - if (class_exists($className, false) || interface_exists($className, false)) { - return true; - } else { - throw new Exception("Unable to find '$className' in file: $classFile"); + // 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 (!isset($classFile)) { + // return false to let other autoloaders to try loading the class + return false; } + } + + include($classFile); + + if (class_exists($className, false) || interface_exists($className, false)) { + return true; } else { - return false; + throw new UnknownClassException("Unable to find '$className' in file: $classFile"); } } @@ -268,16 +287,16 @@ class YiiBase * Creates a new object using the given configuration. * * The configuration can be either a string or an array. - * If a string, it is treated as the *object type*; if an array, - * it must contain a `class` element specifying the *object type*, and + * If a string, it is treated as the *object class*; if an array, + * 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 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, * - * - `\app\components\GoogleMap`: fully-qualified namespaced class. - * - `@app/components/GoogleMap`: an alias + * - `app\components\GoogleMap`: fully-qualified namespaced class. + * - `@app/components/GoogleMap`: an alias, used for non-namespaced class. * * Below are some usage examples: * diff --git a/framework/base/Module.php b/framework/base/Module.php index 74c848b..0b2bd16 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -593,14 +593,15 @@ abstract class Module extends Component } elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { $className = StringHelper::id2camel($id) . 'Controller'; $classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php'; + if (!is_file($classFile)) { + return false; + } $className = ltrim($this->controllerNamespace . '\\' . $className, '\\'); Yii::$classMap[$className] = $classFile; - if (class_exists($className)) { - if (is_subclass_of($className, 'yii\base\Controller')) { - $controller = new $className($id, $this); - } elseif (YII_DEBUG && !is_subclass_of($className, 'yii\base\Controller')) { - throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller."); - } + if (is_subclass_of($className, 'yii\base\Controller')) { + $controller = new $className($id, $this); + } elseif (YII_DEBUG) { + throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller."); } } diff --git a/framework/base/UnknownClassException.php b/framework/base/UnknownClassException.php new file mode 100644 index 0000000..ac44746 --- /dev/null +++ b/framework/base/UnknownClassException.php @@ -0,0 +1,26 @@ + + * @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'); + } +} + diff --git a/framework/base/UnknownMethodException.php b/framework/base/UnknownMethodException.php index 29bedca..440e76e 100644 --- a/framework/base/UnknownMethodException.php +++ b/framework/base/UnknownMethodException.php @@ -8,7 +8,7 @@ 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 * @since 2.0 diff --git a/framework/web/User.php b/framework/web/User.php index 4dc2607..435b606 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -32,7 +32,7 @@ class User extends Component 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; /** @@ -131,7 +131,7 @@ class User extends Component $this->_identity = null; } else { /** @var $class Identity */ - $class = $this->identityClass; + $class = Yii::import($this->identityClass); $this->_identity = $class::findIdentity($id); } }