PHP的Yii框架中YiiBase入口类的扩展写法示例
author:一佰互联 2019-04-27   click:156

通过yiic.php自动创建一个应用后,入口文件初始代码如下:

<?php// change the following paths if necessary$yii=dirname(__FILE__)."/../yii/framework/yii.php";$config=dirname(__FILE__)."/protected/config/main.php";// remove the following lines when in production modedefined("YII_DEBUG") or define("YII_DEBUG",true);// specify how many levels of call stack should be shown in each log messagedefined("YII_TRACE_LEVEL") or define("YII_TRACE_LEVEL",3);require_once($yii);Yii::createWebApplication($config)->run();

其中第三行引入了一个yii.php的文件,这个可以在yii核心目录里的framework/下找到,这个文件中定义了一个Yii类,并且继承了YiiBase类。

代码如下

require(dirname(__FILE__)."/YiiBase.php"); /** * Yii is a helper class serving common framework functionalities. * * It encapsulates {@link YiiBase} which provides the actual implementation. * By writing your own Yii class, you can customize some functionalities of YiiBase. * * @author Qiang Xue <qiang.xue@gmail.com> * @package system * @since 1.0 */class Yii extends YiiBase{}

Yii::createWebApplication

这个方法实际上是在YiiBase父类中定义的,所以,Yii为我们预留了扩展的可能。我们只需要在yii.php中添加我们想要扩展的方法即可,在项目中直接使用 Yii::方法名() 调用。 为了将项目代码和核心目录完全分离,我个人觉得在项目目录下使用另外一个yii.php来替代从核心目录中包含yii.php更加好。

这里我用了更加极端的方法,我直接将yii这个类定义在了入口文件,并扩展了一个全局工厂函数 instance()方法,请看代码:

<?php// change the following paths if necessary$yii=dirname(__FILE__)."/../yii/framework/YiiBase.php";$config=dirname(__FILE__)."/protected/config/main.php";// remove the following lines when in production modedefined("YII_DEBUG") or define("YII_DEBUG",true);// specify how many levels of call stack should be shown in each log messagedefined("YII_TRACE_LEVEL") or define("YII_TRACE_LEVEL",3);require_once($yii);//扩展基类class Yii extends YiiBase{  /**   * 全局扩展方法:工厂函数   * @param type $alias 类库别名   */  static function instance($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: 要导入的类名或路径* $forceInclude false:只导入不include类文件,true则导入并include类文件*/ public static function import($alias, $forceInclude = false){   //Yii把所有的依赖放入到这个全局的$_imports数组中,名字不能重复 //如果当前依赖已经被引入过了,那么直接返回 if (isset(self::$_imports[$alias])) {        return self::$_imports[$alias];    }   //class_exists和interface_exists方法的第二个参数的值为false表示不autoload  if (class_exists($alias, false) || interface_exists($alias, false)) {       return self::$_imports[$alias] = $alias;   }   //如果传进来的是一个php5.3版本的命名空间格式的类(例如:ac.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 {                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] = $alias;           } else {             self::$classMap[$alias] = $classFile;          }          return $alias;      } else {      // try to autoload the class with an autoloader        if (class_exists($alias, true)) {            return self::$_imports[$alias] = $alias;        } else {            throw new CException(Yii::t("yii", "Alias "{alias}" is invalid. Make sure it points to an existing directory or file.",          array("{alias}" => $namespace)));        }      }   }  if (($pos = strrpos($alias, ".")) === false) // a simple class name  {      // 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) {       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;        }     // $alias是"system.web.*"这样的已*结尾的路径,将路径加到include_path中    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 {        throw new CException(Yii::t("yii", "Alias "{alias}" is invalid. Make sure it points to an existing directory or file.",      array("{alias}" => $alias)));      } }

是的,上面这个方法最后就把要加载的东西都放到$_imports,$_includePaths中去了。这就是Yii的import方法,好的,接下来我们看看autoload方法:

public static function autoload($className, $classMapOnly = false){  // 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(YII_PATH . self::$_coreClasses[$className]);  } elseif ($classMapOnly) {      return false;  } 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);                          if (YII_DEBUG && basename(realpath($classFile)) !== $className . ".php") {                              throw new CException(Yii::t("yii", "Class name "{class}" does not match class file "{file}".", array(                "{class}" => $className,                "{file}" => $classFile,              )));                          }                          break;                     }               }         } else {              include($className . ".php");             }     } else // class name with namespace in PHP 5.3       {           $namespace = str_replace("\", ".", ltrim($className, "\"));         if (($path = self::getPathOfAlias($namespace)) !== false) {        include($path . ".php");           } else {              return false;           }       }    

   return class_exists($className, false) || interface_exists($className, false);    }    return true;}config文件中的 import 项里的类或路径在脚本启动中会被自动导入。用户应用里个别类需要引入的类可以在类定义前加入 Yii::import() 语句。