淘先锋技术网

首页 1 2 3 4 5 6 7

在PHP中针对错误的配置有如下

1

2

3

4

log_errors

display_errors

error_log

error_reporting

error_reporting设置了报告的类型,log_errors决定了是否记录错误,记录的地方由error_log指定,display_errors决定是否显示错误信息。

如果你曾经使用过PHP的try语句,或者你会有疑问,错误和异常有何不一样?

简单来说,try块中的代码如果有抛出异常,可以被catch截获。但是发生错误还是会发送给PHP的错误处理程序。比如如下这段程序:

1

2

3

4

5

6

7

8

9

10

11

12

function doSomeThing($var){

throw new Exception("Please stop hitting me");

}

try{

$f = file("/www/test.txt");

$a = 10/0;

doSomeThing($a);

}catch(Exception $e){

echo "----->Exception";

}

这里面同时发生了PHP错误和抛出异常。PHP错误可能会记录到错误日志,而异常被catch捕获进行处理。(注意,如果发生比较严重的错误,比如语法错误,PHP直接中断解析,后面的方法不会被执行,也就不会有异常抛出了),PHP错误的处理默认是有PHP本身的默认程序处理的,不过可以使用set_error_handler方法来设置一个自定义的错误处理方法。

关于PHP的错误处理,可以参考:

http://cn2.php.net/manual/zh/errorfunc.configuration.php

http://cn2.php.net/manual/zh/errorfunc.constants.php

http://cn2.php.net/manual/zh/function.set-error-handler.php

由于我们将使用set_error_handler函数设置一个自定义错误处理程序来替代PHP标准的错误处理程序,所以对这个函数用法必须来个彻底认识。

1

mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )

$error_handler设置回调函数,这个函数设置是有要求的,具体内容可参阅PHP文档,注意这个回调函数如果返回了false,则PHP的标准错误处理程序继续运行。我们看文档中的关于这个函数的描述:

“ 本函数可以用你自己定义的方式来处理运行中的错误, 例如,在应用程序中严重错误发生时,或者在特定条件下触发了一个错误(使用 trigger_error()),你需要对数据/文件做清理回收。

重要的是要记住 error_types 里指定的错误类型都会绕过 PHP 标准错误处理程序(默认是E_ALL | E_STRICT,就是全部错误类型都有自定义函数处理,否则没有包含的由PHP标准程序处理), 除非回调函数返回了 FALSE。 error_reporting() 设置将不会起到作用而你的错误处理函数继续会被调用 —— 不过你仍然可以获取 error_reporting 的当前值,并做适当处理(这里说的是error_reporting设置是针对标准错误处理程序的,它对自定义的错误处理程序无效)。 需要特别注意的是带 @ error-control operator 前缀的语句发生错误时,这个值会是 0(PHP中可以使用@前缀来强制一个语句不报告错误,实际的实现是把error_reporting设置为0,那么标准错误处理程序就不会处理错误,但是这个情况在使用了自定义错误处理程序时无效,有些错误自定义处理程序是无法处理的,实际上有些错误先于自定义错误处理程序前触发,它实际还是使用标准处理程序,所以@字符仍使用意义)。

同时注意,在需要时你有责任使用 die()。 如果错误处理程序返回了,脚本将会继续执行发生错误的后一行(注意这个,错误处理程序返回后脚本继续执行)。

以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT(这些级别不能使用自定义函数处理,实际上这些错误根本无法进入到自定义的错误处理程序,这些错误在PHP内核或编译时就被捕获,至于这些错误的处理则是由php.ini文件中的配置决定的,所以当在开发时,最好在php.ini中把display_errors调整为1,error_reporting调整为E_ALL | E_STRICT)。

如果错误发生在脚本执行之前(比如文件上传时),将不会 调用自定义的错误处理程序因为它尚未在那时注册。 ”

从上面的描述来看,我们总结一下:

比较严重的错误,自定义程序无法处理(实际是没有进入这个程序),自定义处理程序只能处理一般错误。一旦使用自定义处理程序来处理错误,那么error_reporting的设置对它是没有作用的(它只处理指定错误处理程序时给定的错误级别)。在开发时,为了能够显示和记录自定义程序无法处理的错误(比如严重错误),应该在php.ini中配置:

1

2

3

display_errors On

error_log /path/to/log/log.txt

error_reporting E_ALL | E_STRICT

注意error_reporting为E_ALL是表示所有错误,但是在PHP5.3中不包含E_STRICT,从PHP5.4开始才包含。display_errors表示从标准错误输出中输出错误,error_log指出记录错误日志的路径。

事实上,我们可以在脚本中改变这些值配置值(而不使用自定义错误处理函数),比如修改error_log,让它把错误记录到我们的指定的日志文件中。比如:http://blog.ifeeline.com/105.html中描述的就是这个情况,但是它只能记录一般错误,对于严重错误,还是根据php.ini配置文件的的设置去处理的。

相对这种记录错误的方法,对应还有一种使用自定义函数的实现方法。这个也是Magento中使用的方法。Magento中有一个叫开发者模式的设置。先记着这个。我们先看看错误处理程序设置的进入点,在index.php文件中有:

1

2

3

error_reporting(E_ALL | E_STRICT);

//接下来有一行注释的代码

#ini_set('display_errors', 1);

首先修改了错误报告的类型,我们知道,如果使用自定义错误处理程序,根本不受它的影响(指error_reporting设置的错误级别)。接下来的代码是设置是否显示错误,这应该是在没有使用自定义错误处理程序时配合使用的,要不然这两句代码大可以清理的了。

我们的应用从App的run方法或init方法开始,run方法调用baseInit,baseInit调用_initEnvironment,_initEnvironment内又调用setErrorHandler(self::DEFAULT_ERROR_HANDLER)方法:

1

2

3

4

5

6

//setErrorHandler(self::DEFAULT_ERROR_HANDLER)  DEFAULT_ERROR_HANDLER->mageCoreErrorHandler

public function setErrorHandler($handler)

{

set_error_handler($handler);

return $this;

}

可见,直接使用set_error_handler函数直接设置错误处理程序为mageCoreErrorHandler,这个函数在app/code/core/Mage/Core/functions.php中定义。

1

2

3

4

5

6

7

8

9

function mageCoreErrorHandler($errno, $errstr, $errfile, $errline){

//...

$errorMessage .= ": {$errstr}  in {$errfile} on line {$errline}";

if (Mage::getIsDeveloperMode()) {

throw new Exception($errorMessage);

} else {

Mage::log($errorMessage, Zend_Log::ERR);

}

}

这里省了一段代码,它罗列的所有错误代码,然后组建一个字符串$errorMessage,然后根据是否是开发模式,如果是就直接抛出异常,否则就把它记录到日志中。如果是记录到日志中,程序继续运行,现在问题是,如果抛出异常,谁来捕获这个异常,如何处理,程序是否继续运行?

这个需要回到App的run方法的包装函数Mage::run()函数中回答这个问题:

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

public static function run($code = '', $type = 'store', $options = array())

{

try {

// .....

self::$_app->run(array(

'scope_code' => $code,

'scope_type' => $type,

'options'    => $options,

));

} catch (Mage_Core_Model_Session_Exception $e) {

header('Location: ' . self::getBaseUrl());

die();

} catch (Mage_Core_Model_Store_Exception $e) {

require_once(self::getBaseDir() . DS . 'errors' . DS . '404.php');

die();

} catch (Exception $e) {

if (self::isInstalled() || self::$_isDownloader) {

self::printException($e);

exit();

}

try {

self::dispatchEvent('mage_run_exception', array('exception' => $e));

if (!headers_sent()) {

header('Location:' . self::getUrl('install'));

} else {

self::printException($e);

}

} catch (Exception $ne) {

self::printException($ne, $e->getMessage());

}

}

}

很明显,如果自定义错误处理程序抛出异常,就在这里被捕获处理,有些异常是给出链接,有些则是打印输出。这意味,要是打开开发者模式,可以直接查看到错误的输出。

在index.php中有如下代码:

1

2

3

if (isset($_SERVER['MAGE_IS_DEVELOPER_MODE'])) {

Mage::setIsDeveloperMode(true);

}

两个办法让这个代码生效,把条件去掉,或者在配置中设置MAGE_IS_DEVELOPER_MODE这个变量(一般直接去掉条件快速有实在)。

另外,在自定义的错误处理程序中,如果不是开发模式则不会抛出异常,错误就被记录到日志中,这个路径可以在后台配置:

System->Configuration->Developer->Log Settings

58de0e834f9c8ca91200e1ad786e76f6.png

在调用Mage::log()函数时记录到System Log File Name,调用Mage::logException时记录到Exceptions Log File Name,实际上logException是log()函数的包装器,只是指定了不同的名字。

到此已经讨论了大部分内容了,我们需要谨记,仅仅依靠Magento中的自定义错误处理程序还是不够的,在开发时务必在PHP中做配置以快速找到错误。而在Magento开启开发者模式则可以直接输出异常信息,另外,可以有效利用Mage:log()来调试程序,它不受配置的影响,可以让它输出到自己期望的地方去。

继续查看在事件触发回调函数调用时的代码:

1

2

3

4

5

6

7

8

9

10

#File: app/code/core/Mage/Core/Model/App.php

protected function _callObserverMethod($object, $method, $observer)

{

if (method_exists($object, $method)) {

$object->$method($observer);

} elseif (Mage::getIsDeveloperMode()) {

Mage::throwException('Method "'.$method.'" is not defined in "'.get_class($object).'"');

}

return $this;

}

当回调函数不存在时,如果在开发模式下,就会抛出异常,这个可以让我们知道哪些回调函数不存在。

Magento中通过使用自定义的错误处理出现,把PHP的错误变成抛出异常。

(责任编辑:最模板)