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()语句。