PHP的Yii框架中YiiBase入口类的扩展写法示例
通过yiic.php自动创建一个应用后,入口文件初始代码如下:
<?php
//changethefollowingpathsifnecessary
$yii=dirname(__FILE__).'/../yii/framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';
//removethefollowinglineswheninproductionmode
defined('YII_DEBUG')ordefine('YII_DEBUG',true);
//specifyhowmanylevelsofcallstackshouldbeshownineachlogmessage
defined('YII_TRACE_LEVEL')ordefine('YII_TRACE_LEVEL',3);
require_once($yii);
Yii::createWebApplication($config)->run();
其中第三行引入了一个yii.php的文件,这个可以在yii核心目录里的framework/下找到,这个文件中定义了一个Yii类,并且继承了YiiBase类。
代码如下
require(dirname(__FILE__).'/YiiBase.php');
/**
*Yiiisahelperclassservingcommonframeworkfunctionalities.
*
*Itencapsulates{@linkYiiBase}whichprovidestheactualimplementation.
*BywritingyourownYiiclass,youcancustomizesomefunctionalitiesofYiiBase.
*
*@authorQiangXue<qiang.xue@gmail.com>
*@packagesystem
*@since1.0
*/
classYiiextendsYiiBase
{
}
而
Yii::createWebApplication
这个方法实际上是在YiiBase父类中定义的,所以,Yii为我们预留了扩展的可能。我们只需要在yii.php中添加我们想要扩展的方法即可,在项目中直接使用Yii::方法名()调用。
为了将项目代码和核心目录完全分离,我个人觉得在项目目录下使用另外一个yii.php来替代从核心目录中包含yii.php更加好。
这里我用了更加极端的方法,我直接将yii这个类定义在了入口文件,并扩展了一个全局工厂函数instance()方法,请看代码:
<?php
//changethefollowingpathsifnecessary
$yii=dirname(__FILE__).'/../yii/framework/YiiBase.php';
$config=dirname(__FILE__).'/protected/config/main.php';
//removethefollowinglineswheninproductionmode
defined('YII_DEBUG')ordefine('YII_DEBUG',true);
//specifyhowmanylevelsofcallstackshouldbeshownineachlogmessage
defined('YII_TRACE_LEVEL')ordefine('YII_TRACE_LEVEL',3);
require_once($yii);
//扩展基类
classYiiextendsYiiBase{
/**
*全局扩展方法:工厂函数
*@paramtype$alias类库别名
*/
staticfunctioninstance($alias){
static$_class_=array();
$key=md5($alias);
if(!isset($_class_[$key])){
$_class_[$key]=self::createComponent($alias);
}
return$_class_[$key];
}
}
Yii::createWebApplication($config)->run();
这个类是在最后一行Yii::createWebApplication()之前定义的,以保证Yii类能正常使用(不要把这个类放在文件末尾,会出错。)
在项目中任何地方,使用$obj=Yii::instance($alias);去实例化一个类,并且是单例模式。
YiiBase中的两个比较重要的方法(import,autoload)
然后看看YiiBase中的import方法就知道这些静态变量是干嘛用的了:
/*Yii::import()
*$alias:要导入的类名或路径
*$forceIncludefalse:只导入不include类文件,true则导入并include类文件
*/
publicstaticfunctionimport($alias,$forceInclude=false){
//Yii把所有的依赖放入到这个全局的$_imports数组中,名字不能重复
//如果当前依赖已经被引入过了,那么直接返回
if(isset(self::$_imports[$alias])){
returnself::$_imports[$alias];
}
//class_exists和interface_exists方法的第二个参数的值为false表示不autoload
if(class_exists($alias,false)||interface_exists($alias,false)){
returnself::$_imports[$alias]=$alias;
}
//如果传进来的是一个php5.3版本的命名空间格式的类(例如:\a\b\c.php)
if(($pos=strrpos($alias,'\\'))!==false){
//$namespace=a.b
$namespace=str_replace('\\','.',ltrim(substr($alias,0,$pos),'\\'));
//判断a.b这个路径是否存在,或者a.b只是alias里面的一个键,调用该方法返回这个键对应的值,比如'email'=>realpath(__DIR__.'/../vendor/cornernote/yii-email-module/email')
if(($path=self::getPathOfAlias($namespace))!==false){
$classFile=$path.DIRECTORY_SEPARATOR.substr($alias,$pos+1).'.php';
if($forceInclude){
if(is_file($classFile)){
require($classFile);
}else{
thrownewCException(Yii::t('yii','Alias"{alias}"isinvalid.MakesureitpointstoanexistingPHPfileandthefileisreadable.',array('{alias}'=>$alias)));
}
self::$_imports[$alias]=$alias;
}else{
self::$classMap[$alias]=$classFile;
}
return$alias;
}else{
//trytoautoloadtheclasswithanautoloader
if(class_exists($alias,true)){
returnself::$_imports[$alias]=$alias;
}else{
thrownewCException(Yii::t('yii','Alias"{alias}"isinvalid.Makesureitpointstoanexistingdirectoryorfile.',array('{alias}'=>$namespace)));
}
}
}
if(($pos=strrpos($alias,'.'))===false)//asimpleclassname
{
//trytoautoloadtheclasswithanautoloaderif$forceIncludeistrue
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))){
returnself::$_imports[$alias]=$className;
}
if(($path=self::getPathOfAlias($alias))!==false){
if($isClass){
if($forceInclude){
if(is_file($path.'.php')){
require($path.'.php');
}else{
thrownewCException(Yii::t('yii','Alias"{alias}"isinvalid.MakesureitpointstoanexistingPHPfileandthefileisreadable.',array('{alias}'=>$alias)));
}
self::$_imports[$alias]=$className;
}else{
self::$classMap[$className]=$path.'.php';
}
return$className;
}
//$alias是'system.web.*'这样的已*结尾的路径,将路径加到include_path中
else//adirectory
{
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;
}
returnself::$_imports[$alias]=$path;
}
}else{
thrownewCException(Yii::t('yii','Alias"{alias}"isinvalid.Makesureitpointstoanexistingdirectoryorfile.',array('{alias}'=>$alias)));
}
}
是的,上面这个方法最后就把要加载的东西都放到$_imports,$_includePaths中去了。这就是Yii的import方法,好的,接下来我们看看autoload方法:
publicstaticfunctionautoload($className,$classMapOnly=false){//useincludesothattheerrorPHPfilemayappear
if(isset(self::$classMap[$className])){
include(self::$classMap[$className]);
}elseif(isset(self::$_coreClasses[$className])){
include(YII_PATH.self::$_coreClasses[$className]);
}elseif($classMapOnly){
returnfalse;
}else{
//includeclassfilerelyingoninclude_path
if(strpos($className,'\\')===false)
//classwithoutnamespace
{
if(self::$enableIncludePath===false){
foreach(self::$_includePathsas$path){
$classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
if(is_file($classFile)){
include($classFile);
if(YII_DEBUG&&basename(realpath($classFile))!==$className.'.php'){
thrownewCException(Yii::t('yii','Classname"{class}"doesnotmatchclassfile"{file}".',array('{class}'=>$className,'{file}'=>$classFile,)));
}
break;
}
}
}else{
include($className.'.php');
}
}else//classnamewithnamespaceinPHP5.3
{
$namespace=str_replace('\\','.',ltrim($className,'\\'));
if(($path=self::getPathOfAlias($namespace))!==false){
include($path.'.php');
}else{
returnfalse;
}
}
returnclass_exists($className,false)||interface_exists($className,false); } returntrue;}
config文件中的import项里的类或路径在脚本启动中会被自动导入。用户应用里个别类需要引入的类可以在类定义前加入Yii::import()语句。