轉載請注明: 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
如果您覺得本文的內容對您有所幫助,您可以打賞我: