在 PHP 中安全地捕获“允许的内存大小已用尽”错误

2022-08-30 08:15:36

我有一个网关脚本,它将JSON返回给客户端。在脚本中,我使用set_error_handler来捕获错误,并且仍然具有格式化的返回。

它受到“允许的内存大小耗尽”错误的影响,但不是使用像ini_set(“memory_limit”,“19T”)这样的东西来增加内存限制,我只想返回用户应该尝试其他东西,因为它使用了很多内存。

有什么好的方法可以捕获致命错误吗?


答案 1

正如这个答案所建议的那样,您可以使用 注册一个回调来检查 。register_shutdown_function()error_get_last()

您仍然必须管理从违规代码生成的输出,无论是由(闭嘴)操作员还是@ini_set('display_errors', false)

ini_set('display_errors', false);

error_reporting(-1);

set_error_handler(function($code, $string, $file, $line){
        throw new ErrorException($string, null, $code, $file, $line);
    });

register_shutdown_function(function(){
        $error = error_get_last();
        if(null !== $error)
        {
            echo 'Caught at shutdown';
        }
    });

try
{
    while(true)
    {
        $data .= str_repeat('#', PHP_INT_MAX);
    }
}
catch(\Exception $exception)
{
    echo 'Caught in try/catch';
}

运行时,此输出 。遗憾的是,不会引发异常对象,因为致命错误会触发脚本终止,随后仅在关闭函数中捕获。Caught at shutdownErrorException

您可以在关机功能中检查阵列以获取有关原因的详细信息,并做出相应的响应。一个建议可能是针对 Web 应用程序(在不同的地址,或者当然使用不同的参数)重新发出请求,并返回捕获的响应。$error

我建议保持高电平(值为 -1),并使用(如其他人所建议的)错误处理其他所有和 。error_reporting()set_error_handler()ErrorException


答案 2

如果发生此错误时需要执行业务代码(日志记录、备份上下文以供将来调试、通过电子邮件发送等),则注册关闭函数是不够的:您应该以某种方式释放内存。

一种解决方案是在某个地方分配一些紧急内存:

public function initErrorHandler()
{
    // This storage is freed on error (case of allowed memory exhausted)
    $this->memory = str_repeat('*', 1024 * 1024);

    register_shutdown_function(function()
    {
        $this->memory = null;
        if ((!is_null($err = error_get_last())) && (!in_array($err['type'], array (E_NOTICE, E_WARNING))))
        {
           // $this->emergencyMethod($err);
        }
    });
    return $this;
}

推荐