【问题标题】:Custom error handler to handle errors inside and outside Object code自定义错误处理程序来处理对象代码内部和外部的错误
【发布时间】:2012-09-09 17:20:28
【问题描述】:

我知道 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 个问题解释了为什么应该避免使用全局变量:

【问题讨论】:

    标签: php oop error-handling procedural-programming


    【解决方案1】:

    一个有趣的问题,似乎没有人愿意跳,所以我会给你我的 2cents 价值,一切都由 IMO 限定......

    错误处理是指以某种用户友好的方式整齐地处理错误并结束向最终用户报告,另外还可能为应用程序支持制定内部取证。一般来说,它应该尝试公开或解释应用程序上下文——这太难了,而且本质上是特定于上下文的,所以你最终会打开一堆蠕虫。

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

    如果$obj 是单例(并且您愿意考虑使用单例模板),那么这将允许您做您想做的事情。

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

    Jocelyn 反馈后的脚注

    我理解赞成和反对单身人士的论点,这就是为什么我自己不使用它们,并且在我最初的回答中也弃用了它们。然而,当小心使用时,它们确实提供了一些优势,这就是为什么一些同类最佳的应用程序(如 MediaWiki 引擎)仍然使用它们的原因。 PHP 范围的约束仍然存在。如果您要从另一个类或函数调用 $someObject->method(),那么它必须在调用函数的范围内。根本没有办法解决这个问题。所以你有一些选择:

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

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

      • myErrorHandler()。您描述的错误处理程序,但现在是静态类方法。

      • initErrorHandler()。你调用它来注册上面的静态方法。

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

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

      • $registeredCallbacksregisterClassCallback() 添加和 unregisterClassCallback() 从中删除的已注册回调的私有数组。 myErrorHandler() 可以简单地使用 class_exists()method_exists() 对此进行 foreach,以在调用之前验证每个注册的回调仍在范围内。

    这将满足您的要求。请记住,在 php 中,对变量的对象赋值实际上是对象的句柄。您可以将对象(句柄)复制到任何变量上下文。这不会复制或深复制底层对象。它实际上是一个引用(尽管在语义上与真正的 PHP 对象引用略有不同——但这是一个不同的问答)。

    【讨论】:

    • 我不想使用全局变量或单例。如果我使用 static 方法将错误处理程序封装在我的类中,我将如何访问此方法中的$this?你能举一个简单的例子,类似于我在问题中发布的代码吗?
    • 查看 MediaWiki 或 PHP callbacks 的源代码和用户示例。您将直接访问该方法,该对象将注册一个回调以根据请求提交所需的上下文。
    • 我按照您的建议查看了 Mediawiki 的源代码。在不同的文件中有六个对set_error_handler 的不同调用。我无法理解它如何帮助解决我的问题。我用更多细节更新了我的问题,也许它更好地解释了我试图获得的行为。
    • @Jocelyn,我已经根据您的评论更新了我的答案。我关于 MW 的观点是关于它如何使用 回调。它不会尝试以您想要的方式滥用范围。希望我更新的答案能更好地解释我的建议:)
    【解决方案2】:

    我最终决定了这个设计:

    • 我当前的错误处理程序保持不变(函数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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-18
      • 2010-12-26
      • 1970-01-01
      • 1970-01-01
      • 2018-04-29
      • 2011-06-01
      相关资源
      最近更新 更多