如何更改HTTP响应并在析构函数中发生异常时显示错误消息? [英] How to change HTTP response and show an error message when exception happens in destructor?

查看:156
本文介绍了如何更改HTTP响应并在析构函数中发生异常时显示错误消息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一种情况,PHP函数尝试通过HTTP 302重定向浏览器,但是在退出中调用的析构函数中抛出异常。实际的代码是组件:



composer.json

  {
require:{
symfony / debug:〜2.5
}
}

test.php

 <?php 

require('vendor / autoload.php');

使用Symfony\Component\Debug\Debug;

Debug :: enable();

class A
{
public function __destruct()
{
throw new \Exception(Hey dude!);
}
}

$ a = new A;

该组件已启用,但不显示预期的异常。


I have a situation where a PHP function attempts to redirect the browser via an HTTP 302, but an exception is being thrown in a destructor called upon 'exit'ing. The actual code in question is the _doRedirect() method of SimpleSAML but here's a simplified situation:

header('Location: http://somewhere.com', TRUE, 302);
exit; // end script execution

The 'exit' is triggering a destructor for an unrelated class and the error details are getting written out to the HTTP response... but no human notices the error because the browser executes the HTTP 302 redirect.

Ideally I'd like to change the status code to HTTP 500 so the browser would just render the page with the error on it. But is that possible? If not, what options do I have?

解决方案

Unfortunately, you are meeting a by-design issue.

  • The set_error_handler function does not handle uncaught exceptions (which is a level E_ERROR error):

From the documentation:

The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.

  • The reguster_shutdown_function function is called after your exit call (or when the end of the script is reached), but before your objects destruction.

So if you are trying such code:

<?php

register_shutdown_function(function() {
  $error = error_get_last();
  if (!is_null($error)) {
    header_remove();
    header("HTTP/1.1 500 Internal Server Error");
    // do something with $error
  }
  else {
    echo "no error happened";
  }
});

class A
{
   public function __destruct()
   {
     throw new \Exception("Hey dude!");
   }
}

header("HTTP/1.1 301 Moved Permanently");
$a = new A;

You'll get "no error happened" and the fatal error displayed, and the 301 will remain unchanged.

If you think you may destroy your object from inside the register_shutdown_function callback, you'll meet a scoping issue where your object is referenced in its original scope, and in the register_shutdown_function's scope: as one reference of the object still exist, it will not be destroyed.

<?php

class A
{
   public function __destruct()
   {
     throw new \Exception("Hey dude!");
   }
}

$a = new A; /* global scope */

register_shutdown_function(function() use ($a /* local scope */) {
  try {
    unset($a); /* removed from local scope but another reference still exists in global scope */
  } catch (\Exception $e) {
    echo "Catched!\n";
  }
});

/* destructor called here */


Even well-known debug components do not support this.

For example using the Symfony2 Debug component:

composer.json

{
  "require": {
    "symfony/debug": "~2.5"
  }
}

test.php

<?php

require('vendor/autoload.php');

use Symfony\Component\Debug\Debug;

Debug::enable();

class A
{
   public function __destruct()
   {
     throw new \Exception("Hey dude!");
   }
}

$a = new A;

The component is enabled but does not display the nice expected exception.

这篇关于如何更改HTTP响应并在析构函数中发生异常时显示错误消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆