接上篇:Yii分析1:web程序入口(2)
本文分析前两篇文章用到的一些函数。
上一篇提到在preloadComponents的时候会调用getComponent,我们来看一下getComponent的细节:
Yii_PATH/base/CModule.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//第二个参数标识如果是空则创建之,默认为true
public function getComponent($id,$createIfNull=true)
{
if(isset($this->_components[$id]))
return $this->_components[$id];
//_componentConfig是在configure方法中由setComponents中已经保存的变量
else if(isset($this->_componentConfig[$id]) && $createIfNull)
{
$cOnfig=$this->_componentConfig[$id];
unset($this->_componentConfig[$id]);
//如果没有设置enable参数或者enable参数为true
if(!isset($config['enabled']) || $config['enabled'])
{
Yii::trace("Loading \"$id\" application component",'system.web.CModule');
unset($config['enabled']);
//创建组件,并完成初始化
$compOnent=Yii::createComponent($config);
$component->init();
return $this->_components[$id]=$component;
}
}
}
|
下面是Yii::createComponent的实现:
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
|
public static function createComponent($config)
{
//如果配置项内容是一个字符串,则它是类名,如果是数组,那么数组的class成员是类名
if(is_string($config))
{
$type=$config;
$cOnfig=array();
}
else if(isset($config['class']))
{
$type=$config['class'];
unset($config['class']);
}
else
throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.'));
//如果类不存在,则使用import来引入,而不是用autoload
if(!class_exists($type,false))
$type=Yii::import($type,true);
//创建组件时,可以带更多的初始值进行初始化
if(($n=func_num_args())>1)
{
$args=func_get_args();
if($n===2)
$object=new $type($args[1]);
else if($n===3)
$object=new $type($args[1],$args[2]);
else if($n===4)
$object=new $type($args[1],$args[2],$args[3]);
else
{
unset($args[0]);
$class=new ReflectionClass($type);
// Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+
// $object=$class->newInstanceArgs($args);
//如果参数大于4个,那么调用newInstance方法来进行初始化
$object=call_user_func_array(array($class,'newInstance'),$args);
}
}
else
$object=new $type;
//将配置中的配置项赋值给组件的成员
foreach($config as $key=>$value)
$object->$key=$value;
return $object;
|
=========================================================================
我们再来看看errorhandler和exceptionhandler:
Yii_PATH/base/CApplication.php
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
|
public function handleError($code,$message,$file,$line)
{
if($code & error_reporting())
{
// disable error capturing to avoid recursive errors
//恢复原错误handler(内建的错误handler),避免本函数触发错误handler,从而递归调用
restore_error_handler();
restore_exception_handler();
$log="$message ($file:$line)";
if(isset($_SERVER['REQUEST_URI']))
$log.=' REQUEST_URI='.$_SERVER['REQUEST_URI'];
Yii::log($log,CLogger::LEVEL_ERROR,'php');
try
{
//使用一个error事件类来触发error事件,然后显示log信息,这里暂不分析
$event=new CErrorEvent($this,$code,$message,$file,$line);
$this->onError($event);
if(!$event->handled)
{
// try an error handler
if(($handler=$this->getErrorHandler())!==null)
$handler->handle($event);
else
$this->displayError($code,$message,$file,$line);
}
}
catch(Exception $e)
{
$this->displayException($e);
}
$this->end(1);
}
}
|
dispalyError代码如下:
$message ($file:$line)
\n"; echo ''; debug_print_backtrace(); echo ''; } else { echo "PHP Error [$code]\n"; echo "
$message
\n"; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public function displayError($code,$message,$file,$line)
{
//如果是调试,打印调用栈,否则不打印
if(YII_DEBUG)
{
echo "PHP Error [$code]\n";
echo "
$message ($file:$line) \n";
echo '
';
debug_print_backtrace();
echo '';
}
else
{
echo "PHP Error [$code]\n";
echo "
$message \n";
}
}
|
参考:
1
2
3
|
设定了error hanlder之后,某些级别的错误是不会触发设置的函数的,例如:E_ERROR(运行期错误,如内存分配错误),E_PARSE(解释错误,即语法),E_CORE_ERROR(在PHP内核startup阶段发生的错误),E_CORE_WARNING(在PHP内核startup阶段发生的warning),E_COMPILE_ERROR(编译错误,zend引擎生成),E_COMPILE_WARNING(编译warning,zend引擎生成)
关于startup和zend引擎请参考php内核方面的资料
|
handleException与Error相似:
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 function handleException($exception)
{
// disable error capturing to avoid recursive errors
restore_error_handler();
restore_exception_handler();
$category='exception.'.get_class($exception);
//如果是Http异常,获取状态码
if($exception instanceof CHttpException)
$category.='.'.$exception->statusCode;
$message=(string)$exception;
if(isset($_SERVER['REQUEST_URI']))
$message.=' REQUEST_URI='.$_SERVER['REQUEST_URI'];
Yii::log($message,CLogger::LEVEL_ERROR,$category);
//接下来同handleError
try
{
$event=new CExceptionEvent($this,$exception);
$this->onException($event);
if(!$event->handled)
{
// try an error handler
if(($handler=$this->getErrorHandler())!==null)
$handler->handle($event);
else
$this->displayException($exception);
}
}
catch(Exception $e)
{
$this->displayException($e);
}
$this->end(1);
}
|
参考:
1
|
call_user_function可以传递儿女和内置的或者用户自定义的函数,除了语言结构如array(), echo(), empty(), eval(), exit(), isset(), list(), print(), unset()
|