自定义错误处理程序,用于处理对象代码内外的错误

时间:2012-09-09 17:20:29

标签: php oop error-handling procedural-programming

我知道Stackoverflow上有很多与自定义错误处理程序相关的问题。但是,在阅读了其中许多内容以及PHP手册之后,我仍然无法解决我的问题。因此,我发布了这个问题。

我的脚本目前以这种方式构建:

require 'file.php';
require 'anotherFile.php';
// several more "require" here. These files contain many functions

function myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext){
    // some code to handle errors here

}

class myObject {
    function __construct() {
        // set the values here
    }

    function computeSomething() {
        ...
        doFunction2();
        ...
    }

    function SomethingBadHappened()
    {
    }
}

function doFunction1() {
    // for some reason, an error happens here
    // it is properly handled by the current error handler
}

function doFunction2() {
    // for some reason, an error happens here
    // since it got called by $obj, I want the error handler to run $obj->SomethingBadHappened();
    // but $obj is not known in myErrorHandler function!
}

set_error_handler('myErrorHandler');

// some procedural code here
doFunction1();
doAnotherThing();

// then I use objects
$obj = new myObject();
$obj->run();

// then I may use procedural code again
doSomethingElse();

我的自定义错误处理程序已经正常工作。它捕获并处理在设置错误处理程序后执行的代码中发生的所有PHP错误。

我的问题:

如果在myObject类的方法中发生错误,我想调用非静态方法:

$obj->SomethingBadHappened();

$obj不在myErrorHandler的范围内。如何在错误处理程序中访问$obj以调用$obj的成员函数?
我目前有300KB的PHP代码,我无法更改所有函数的签名以添加$obj作为参数(函数太多了!)。

我读到可以将自定义错误处理程序定义为对象的方法。但是,如果我这样做,它将无法捕获在创建myObject($ obj)实例之前发生的错误。

我也读过关于异常的内容,但它似乎没有帮助解决我的问题。 我不愿意使用全局变量。这里有两个问题可以解释为什么应该避免全局变量:

2 个答案:

答案 0 :(得分:5)

一个有趣的Q,似乎没有人想要跳起来所以我会给你我的2cents价值,一切都由IMO认定......

错误处理是整齐地处理错误,最终以某种形式的用户友好方式向最终用户报告,加上可能为应用程序支持放置内部取证。一般来说,它应该尝试公开或解释应用程序上下文 - 太难以及其特定的上下文特性,因此您最终会打开一堆蠕虫。

如果您想在上下文中按层次结构处理错误,那么您现在已经真正进入了异常和异常处理的世界,这是一个完全不同的运行时模型。在这里你可以捕获 - 原则上 - 处理应用程序异常并保留对象上下文,但这可能会被证明难以实现,我想不出任何有助于此的标准模式。

如果$obj是单身人士(并且您愿意考虑使用单身人士模板),那么这将允许您按照自己的意愿行事。

另一方面,如果你想要的只是一些有限的背景,例如: “处理客户记录XXXX”,那么为什么不使用静态方法将错误处理程序封装在类中,使用另一个注册方法来注册推定的上下文,甚至注册回调,这将允许您注册特定于对象的方法提供这种背景?当然,您需要非常小心地使用类/对象函数来验证任何已注册的回调仍然在有效范围内,以避免潜在的嵌套错误,但是这种框架可以变得可行。

Jocelyn的反馈后面的脚注

我理解pro和con Singletons的论点,这就是为什么我不自己使用它们并且在我最初的答案中也弃用了它们。但是,它们在使用时确实提供了一些优势,这就是为什么某些最佳应用程序(如MediaWiki引擎)仍在使用它们。 PHP范围的约束仍然存在。为了从另一个类或函数调用$someObject->method(),它必须在调用函数的范围内。没有办法解决这个问题。所以你有一些选择:

  • 如果只有一个someClass对象在任何时候处于活动状态,那么您可以通过多种方法将其置于全局范围内,最简单的方法是复制对象一些全局变量,或使用静态方法返回它(无论如何都是经典单例someClass::get()方法所做的。

  • 另一方面,如果您有多个可以推定在范围内的对象,那么您可以将对象作为参数传递,但绝对不需要这样做。您可以使用push方法将错误处理程序注册到这些方法。一个示例实现是使用4个静态方法和静态私有变量创建和错误处理程序类:

    • <强> myErrorHandler()即可。您描述的错误处理程序,但现在是一个静态类方法。

    • <强> initErrorHandler()即可。你这叫它来注册上面的静态方法。

    • registerClassCallback($ callback),其中$ callback是标准array($obj,'method')参数。

    • unregisterClassCallback($ callback),其中$ callback是相同的array($obj,'method')参数。

    • <强> $ registeredCallbacks 即可。由registerClassCallback()添加并unregisterClassCallback()删除的私有已注册回调数组。 myErrorHandler()可以使用class_exists()method_exists()简单地对此进行预测,以便在调用之前验证每个已注册的回调是否仍在范围内。

这将做你想要的。请记住,在php中,对变量的对象赋值实际上是对象的句柄。您可以将对象(句柄)复制到任何变量上下文。这不会复制或深层复制基础对象。它实际上是一个引用(虽然在细节上与真正的PHP对象引用略有不同 - 但这是一个不同的Q&amp; A)。

答案 1 :(得分:0)

我终于决定了这个设计:

  • 我当前的错误处理程序保持不变(函数myErrorHandler)。它包含在发生错误时(代码中的任何位置)执行的代码
  • 在课程myObject
  • 我添加了另一个错误处理程序(函数myObject_error_handler
  • myObject的构造函数注册新的错误处理程序
  • 函数myObject_error_handler现在可以调用函数SomethingBadHappened,然后调用myErrorHandler来处理错误

这样做的主要好处是它只需要对现有代码进行很少的更改:

  • 我班上的一种新方法
  • 在构造函数
  • 中注册新的错误处理程序
  • 从新错误处理程序
  • 调用旧错误处理程序

新代码是:

require 'file.php';
require 'anotherFile.php';
// several more "require" here. These files contain many functions

function myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
    // some code to handle errors here

}

class myObject {
    function __construct() {
        // set the values here

        // register another error handler
        set_error_handler(array($this, 'myObject_error_handler'));
    }

    function myObject_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
        // call any number of methods of the current class
        $this->SomethingBadHappened();

        // then call the other error handler
        myErrorHandler($errno, $errstr, $errfile, $errline, $errcontext);
    }

    function computeSomething() {
        ...
        doFunction2();
        ...
    }

    function SomethingBadHappened() {
        // does something when an error happens
    }
}

function doFunction1() {
    // for some reason, an error happens here
    // it is properly handled by the current error handler
}

function doFunction2() {
    // for some reason, an error happens here
    // now the function myObject_error_handler will be called automatically
}

set_error_handler('myErrorHandler');

// some procedural code here
doFunction1();
doAnotherThing();

// then I use objects
$obj = new myObject();
$obj->run();

// then I may use procedural code again
set_error_handler('myErrorHandler');
doSomethingElse();