PHP的Yii框架中YiiBase入口类的扩展写法示例

5年以前  |  阅读数:228 次  |  编程语言:PHP 

通过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 mode
    defined('YII_DEBUG') or define('YII_DEBUG',true);
    // specify how many levels of call stack should be shown in each log message
    defined('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 mode
    defined('YII_DEBUG') or define('YII_DEBUG',true);
    // specify how many levels of call stack should be shown in each log message
    defined('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版本的命名空间格式的类(例如:\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 {          
          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() 语句。

 相关文章:
PHP分页显示制作详细讲解
SSH 登录失败:Host key verification failed
获取IMSI
将二进制数据转为16进制以便显示
获取IMEI
贪吃蛇
文件下载
双位运算符
发送邮件
PHP自定义函数获取搜索引擎来源关键字的方法
Java生成UUID
提取后缀名
年的日历图
在Zeus Web Server中安装PHP语言支持
让你成为最历害的git提交人
Yii2汉字转拼音类的实例代码
再谈PHP中单双引号的区别详解
指定应用ID以获取对应的应用名称
Python 2与Python 3版本和编码的对比
php封装的page分页类完整实例