通过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.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版本的命名空间格式的类(例如:\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方法:
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() 语句。