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

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

1
2
3
4
5
6
7
8
9
10
<?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类。

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
{
}

1
Yii::createWebApplication

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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方法就知道这些静态变量是干嘛用的了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/* 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版本的命名空间格式的类(例如:abc.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方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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() 语句。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论