在 __destruct() 中,如何查看当前是不是存在异常?

Posted

技术标签:

【中文标题】在 __destruct() 中,如何查看当前是不是存在异常?【英文标题】:In __destruct(), how can you see if an exception is currently in flight?在 __destruct() 中,如何查看当前是否存在异常? 【发布时间】:2016-01-16 22:46:33 【问题描述】:

如何查看当前是否存在异常,即堆栈正在展开?

在下面的示例中,您将如何实现isExceptionInFlight()

<?php

class Destroyer

    function __destruct()   
        if (isExceptionInFlight()) 
            echo 'failure';
         else 
            echo 'success';
        
    


function isExceptionInFlight() 
    // ?????


function createAndThrow()

    $var = new Destroyer;
    throw new \Exception;


createAndThrow();

这样做的目的是实现 D 的 scope 语句,该语句可用作多种其他语言的库。这使您可以摆脱嵌套的 try-catch 块,从而更容易正确地执行回滚事务。

附录1:

我查看了 Zend PHP 引擎,executor_globals.exception 似乎就是我正在寻找的东西 (https://github.com/php/php-src/blob/master/Zend/zend_globals.h)。但是,当我在 __destruct() 期间检查它时,此值始终为 nullptr。知道我接下来应该看哪里吗?

附录2:

检查executor_globals.opline_before_exception 取得了一些进展。但是,当异常被捕获时,它不会重置为nullptr

附录3:

我找到了following code (line 135)

/* Make sure that destructors are protected from previously thrown exceptions.
 * For example, if an exception was thrown in a function and when the function's
 * local variable destruction results in a destructor being called.
 */
old_exception = NULL;
if (EG(exception)) 
    if (EG(exception) == object) 
        zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
     else 
        old_exception = EG(exception);
        EG(exception) = NULL;
    

zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
if (old_exception) 
    if (EG(exception)) 
        zend_exception_set_previous(EG(exception), old_exception);
     else 
        EG(exception) = old_exception;
    

这似乎在积极阻止我做我想做的事,并解释了为什么executor_globals.exception 总是nullptr

【问题讨论】:

【参考方案1】:

虽然我不推荐,但我过去已经实现了。我的方法(简单地说)是这样的:

实现自定义异常类

class MyException extends Exception 
    public static $exceptionThrown = false;

    public function __construct($your parameters) 
         self::$exceptionThrown = true;
    


现在,每个异常都应该是您自己的异常实现,而不是默认的异常。

class Destroyer 
    public function __destruct() 
        if(MyException::exceptionThrown() 
            Database::rollback();
         else 
            Database::commit();
        
    

【讨论】:

如果这个异常被捕获了怎么办? MyException::exceptionThrown 不会仍然是 true,即使它已被解决? 是的。就像我说的,我真的不推荐这种方法,因为有很多框架更完整。但是在使用该代码时,我比较新手,我喜欢使用 HTTP 失败之类的异常。在使用的时候,我只遇到过一次捕获异常的问题,所以我只是在catch块里面加了一个“MyException::$exceptionThrown = false”。由于我没有类似的麻烦,所以我就让它过去了。

以上是关于在 __destruct() 中,如何查看当前是不是存在异常?的主要内容,如果未能解决你的问题,请参考以下文章

关于php析构函数__destruct()的问题

在php中实现Serializable接口时正确调用__destruct方法?

PHP中的11个魔术方法总结:__construct,__destruct__call等

在__destruct析构函数里操作文件出现的问题

魔术方法 __unset __isset __destruct

php __destruct 为啥通过 file_get_contents 得到不同的结果