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


Posted in PHP onMarch 17, 2016

通过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 相关文章推荐
PHP 配置文件中open_basedir选项作用
Jul 19 PHP
php 验证码制作(网树注释思想)
Jul 20 PHP
一些php项目中比较通用的php自建函数的详解
Jun 06 PHP
浅析PHP中Collection 类的设计
Jun 21 PHP
PHP之sprintf函数用法详解
Nov 12 PHP
php生成唯一的订单函数分享
Feb 02 PHP
php实现的mongodb操作类实例
Apr 03 PHP
PHP各种异常和错误的拦截方法及发生致命错误时进行报警
Jan 19 PHP
php遍历、读取文件夹中图片并分页显示图片的方法
Nov 15 PHP
PHP获取当前执行php文件名的代码
Mar 02 PHP
ThinkPHP整合datatables实现服务端分页的示例代码
Feb 10 PHP
PHP实现小程序批量通知推送
Nov 27 PHP
Symfony控制层深入详解
Mar 17 #PHP
详解PHP的Yii框架的运行机制及其路由功能
Mar 17 #PHP
深入解析PHP的Yii框架中的event事件机制
Mar 17 #PHP
全面解读PHP的Yii框架中的日志功能
Mar 17 #PHP
Symfony核心类概述
Mar 17 #PHP
使用symfony命令创建项目的方法
Mar 17 #PHP
Symfony实现行为和模板中取得request参数的方法
Mar 17 #PHP
You might like
php 代码优化的42条建议 推荐
2009/09/25 PHP
thinkphp3.2中Lite文件替换框架入口文件或应用入口文件的方法
2015/05/21 PHP
PHP实现mysqli批量执行多条语句的方法示例
2017/07/22 PHP
用javascript实现的激活输入框后隐藏初始内容
2007/06/29 Javascript
Javascript中定义方法的另类写法(批量定义js对象的方法)
2011/02/25 Javascript
javascript权威指南 学习笔记之null和undefined
2011/09/25 Javascript
js+xml生成级联下拉框代码
2012/07/24 Javascript
jquery利用ajax调用后台方法实例
2013/08/23 Javascript
理解JavaScript中worker事件api
2015/12/25 Javascript
AngularJS基础 ng-if 指令用法
2016/08/01 Javascript
详解使用vue实现tab 切换操作
2017/07/03 Javascript
layui table 表格模板按钮的实例代码
2019/09/21 Javascript
Vue实现图片与文字混输效果
2019/12/04 Javascript
如何在vue中使用kindeditor富文本编辑器
2020/12/19 Vue.js
[49:20]VG vs TNC Supermajor小组赛B组败者组决赛 BO3 第二场 6.2
2018/06/03 DOTA
Python描述器descriptor详解
2015/02/03 Python
Python实现的tcp端口检测操作示例
2018/07/24 Python
对python的输出和输出格式详解
2018/12/08 Python
pycharm中使用anaconda部署python环境的方法步骤
2018/12/19 Python
Python函数装饰器原理与用法详解
2019/08/16 Python
Python搭建代理IP池实现检测IP的方法
2019/10/27 Python
Tensorflow训练模型越来越慢的2种解决方案
2020/02/07 Python
python使用docx模块读写docx文件的方法与docx模块常用方法详解
2020/02/17 Python
浅谈Pytorch中的自动求导函数backward()所需参数的含义
2020/02/29 Python
Django ForeignKey与数据库的FOREIGN KEY约束详解
2020/05/20 Python
Python StringIO及BytesIO包使用方法解析
2020/06/15 Python
导致python中import错误的原因是什么
2020/07/01 Python
通过Python pyecharts输出保存图片代码实例
2020/11/25 Python
Python APScheduler执行使用方法详解
2020/12/10 Python
Python wordcloud库安装方法总结
2020/12/31 Python
中专毕业生的自我鉴定
2013/12/01 职场文书
医药代表个人求职信范本
2013/12/19 职场文书
股份合作协议书范本
2014/04/14 职场文书
网站推广策划方案
2014/06/04 职场文书
怎样写辞职信
2015/02/27 职场文书
python入门学习关于for else的特殊特性讲解
2021/11/20 Python