轉載請注明: TheViper http://www.cnblogs.com/TheViper
在yii源碼分析1中說到spl_autoload_register注冊給定的函數作為 __autoload 的實現,在這裡是autoload().
public static function autoload($className) {
include self::$_coreClasses [$className];
}
實際上這個autoload()是沒有考慮非核心文件的引入的。比如,在app文件夾經常會有自定義的一些重要文件夾,比如'application.utils.*(工具類),'application.filters.*'(過濾類),'application.validators.*'(校驗類)等。
在實際用的時候,是不用一個一個include的,直接new就可以了,yii已經幫我們做了include的工作。而這個工作就是在autoload()裡面做的。
上面的代碼很顯然沒有考慮非核心文件的引入,這是我的疏忽。
那yii是怎麼幫我們引入非核心文件的?
這要從CApplication說起。
abstract class CApplication extends CModule {
public function __construct($config = null) {
if (is_string ( $config ))
$config = require ($config);
Yii::setApplication ( $this );//保存整個app實例
if (isset ( $config ['basePath'] )) {
$this->setBasePath ( $config ['basePath'] );
unset ( $config ['basePath'] );
} else
$this->setBasePath ( 'protected' );
//設置別名,後面就可以用application表示basePath了
Yii::setPathOfAlias ( 'application', $this->getBasePath () );
//鉤子,模塊 預 初始化時執行,子類實現。不過這時,配置還沒有寫入框架
$this->preinit ();
$this->registerCoreComponents ();
//父類實現
$this->configure ( $config );
//加載靜態應用組件
$this->preloadComponents ();
//這才開始初始化模塊
$this->init ();
}
注意到裡面的$this->configure ( $config );,$config是傳入的配置文件,是一個數組,非核心文件的定義就是在這裡面,比如引入工具類文件夾
<?php
return array (
'basePath' => dirname ( __FILE__ ) . DIRECTORY_SEPARATOR . '..',
'import' => array (
'application.utils.*'
)
);
?>
然後在父類CModule
public function configure($config) {
if (is_array ( $config )) {
foreach ( $config as $key => $value )
$this->$key = $value;
}
}
這裡yii很"狡猾",它在CModule的父類CComponent中重寫了__set()
public function __set($name,$value)
{
$setter='set'.$name;
if(method_exists($this,$setter))
return $this->$setter($value);
else....
}
可以看到,如果CModule中如果有設置yii指定參數(比如import)的方法,就會調用它,而我之前裁剪的時候,把CModule中的setImport()刪掉了。
另外可以看到basePath, params, modules, import, components 是yii保留的參數名。
public function setImport($aliases)
{
foreach($aliases as $alias)
Yii::import($alias);
}
然後是YiiBase裡面的import()
public static function import($alias, $forceInclude = false) {
if (isset ( self::$_imports [$alias] )) //是否已經存在路徑
return self::$_imports [$alias];
if (class_exists ( $alias, false ) || interface_exists ( $alias, false ))//類是否已經定義,針對如urlManager這樣的已定義於$_coreClasses[]的類
return self::$_imports [$alias] = $alias;
if (($pos = strrpos ( $alias, '.' )) === false) //直接是文件名
{
// try to autoload the class with an autoloader if $forceInclude is true
if ($forceInclude && (Yii::autoload ( $alias, true ) || class_exists ( $alias, true )))
self::$_imports [$alias] = $alias;
return $alias;
}
$className = ( string ) substr ( $alias, $pos + 1 );
$isClass = $className !== '*';
//是否為路徑+類名
if ($isClass && (class_exists ( $className, false ) || interface_exists ( $className, false )))
return self::$_imports [$alias] = $className;
//獲取真實路徑
if (($path = self::getPathOfAlias ( $alias )) !== false) {
//是否以*結尾,如application.utils.*
if ($isClass) {
if ($forceInclude) {
if (is_file ( $path . '.php' ))
require ($path . '.php');
else
throw new CException ( Yii::t ( 'yii', 'Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.', array (
'{alias}' => $alias
) ) );
self::$_imports [$alias] = $className;
} else
self::$classMap [$className] = $path . '.php';
return $className;
} else // a directory
{
if (self::$_includePaths === null) {
self::$_includePaths = array_unique ( explode ( PATH_SEPARATOR, get_include_path () ) );
if (($pos = array_search ( '.', self::$_includePaths, true )) !== false)
unset ( self::$_includePaths [$pos] );
}
array_unshift ( self::$_includePaths, $path );
if (self::$enableIncludePath && set_include_path ( '.' . PATH_SEPARATOR . implode ( PATH_SEPARATOR, self::$_includePaths ) ) === false)
self::$enableIncludePath = false;
return self::$_imports [$alias] = $path;
}
}
}
一系列的判斷,最後走到最後的else,將path寫入到$_imports,這時仍然沒有include.
include在autoload()
public static function autoload($className)
{
// use include so that the error PHP file may appear
if(isset(self::$classMap[$className]))
include(self::$classMap[$className]);
elseif(isset(self::$_coreClasses[$className]))
include(self::$_coreClasses[$className]);
else
{
// include class file relying on include_path
if(strpos($className,'\\')===false) // class without namespace
{
if(self::$enableIncludePath===false)
{
foreach(self::$_includePaths as $path)
{
$classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
if(is_file($classFile))
{
include($classFile);
break;
}
}
}
else
include($className.'.php');
}
return class_exists($className,false) || interface_exists($className,false);
}
return true;
}
如果需要include的是非核心文件,那這裡的$className只是一個alias,即文件名的前綴。
裁剪的yii http://files.cnblogs.com/TheViper/framework.zip
如果您覺得本文的內容對您有所幫助,您可以打賞我:
