1

我知道 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) 的实例之前发生的错误。

我还阅读了有关异常的信息,但这似乎无助于解决我的问题。我不愿意使用全局变量。这里有两个问题解释了为什么应该避免使用全局变量:

4

2 回答 2

5

一个有趣的问题,似乎没有人想跳,所以我会给你我的 2 美分,一切都符合 IMO 的要求......

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

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

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

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

Jocelyn 反馈后的脚注

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

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

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

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

    • 初始化错误处理程序()。你调用它来注册上面的静态方法。

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

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

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

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

于 2012-09-10T12:11:21.783 回答
1

我最终决定了这个设计:

  • 我当前的错误处理程序保持不变(函数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();
于 2012-09-29T17:00:37.127 回答