【发布时间】:2012-01-16 10:44:07
【问题描述】:
我有一个将 JSON 返回给客户端的网关脚本。 在脚本中,我使用set_error_handler 来捕获错误并且仍然有一个格式化的返回。
它会出现“允许的内存大小已用尽”错误,但我不想使用 ini_set('memory_limit', '19T') 之类的东西来增加内存限制,我只想返回用户应该尝试其他方法,因为它使用了很多内存。
有什么好的方法可以捕获致命错误吗?
【问题讨论】:
我有一个将 JSON 返回给客户端的网关脚本。 在脚本中,我使用set_error_handler 来捕获错误并且仍然有一个格式化的返回。
它会出现“允许的内存大小已用尽”错误,但我不想使用 ini_set('memory_limit', '19T') 之类的东西来增加内存限制,我只想返回用户应该尝试其他方法,因为它使用了很多内存。
有什么好的方法可以捕获致命错误吗?
【问题讨论】:
您可以通过使用此函数来获取进程已经消耗的内存大小 memory_get_peak_usage 文档位于http://www.php.net/manual/en/function.memory-get-peak-usage.php 我认为如果您可以在内存限制之前添加一个条件来重定向或停止进程会更容易几乎达到了这个过程。 :)
【讨论】:
正如this answer 建议的那样,您可以使用register_shutdown_function() 注册一个回调,该回调将检查error_get_last()。
您仍然需要管理由违规代码生成的输出,无论是由 @ (shut up) 运算符还是 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 shutdown。不幸的是,ErrorException 异常对象没有被抛出,因为致命错误触发了脚本终止,随后仅在关闭函数中捕获。
您可以查看shutdown函数中的$error数组了解具体原因,并做出相应响应。一种建议可能是针对您的 Web 应用程序重新发出请求(在不同的地址,或者当然使用不同的参数)并返回捕获的响应。
我建议保持 error_reporting() 高(-1 的值),并使用(正如其他人所建议的)错误处理 @987654332 @和ErrorException。
【讨论】:
caught at shutdown。在尝试捕获行之前,我复制了上面粘贴的确切代码。有什么帮助吗?如果我将它放在根文件的 index.php 中,那么它可以工作
如果您需要在发生此错误时执行业务代码(记录、备份上下文以供将来调试、发送电子邮件等),注册关闭功能是不够的:您应该以某种方式释放内存。
一种解决方案是在某处分配一些紧急内存:
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;
}
【讨论】:
array (E_NOTICE, E_WARNING) 实际上会发现折旧通知和其他不需要的小问题。在某些情况下,您可能需要重写以删除 !否定并将其替换为in_array($err['type'], array (E_ERROR))
new SplFixedArray(65536);。在我的系统上,每个空数组元素占用 16 个字节。
虽然@alain-tiemblo 解决方案完美运行,但我放置此脚本是为了展示如何在 php 脚本中保留超出对象范围的一些内存。
// memory is an object and it is passed by reference
function shutdown($memory) {
// unsetting $memory does not free up memory
// I also tried unsetting a global variable which did not free up the memory
unset($memory->reserve);
}
$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('❤', 1024 * 1024);
register_shutdown_function('shutdown', $memory);
<?php
function getMemory(){
return ((int) (memory_get_usage() / 1024)) . 'KB';
}
// memory is an object and it is passed by reference
function shutdown($memory) {
echo 'Start Shut Down: ' . getMemory() . PHP_EOL;
// unsetting $memory does not free up memory
// I also tried unsetting a global variable which did not free up the memory
unset($memory->reserve);
echo 'End Shut Down: ' . getMemory() . PHP_EOL;
}
echo 'Start: ' . getMemory() . PHP_EOL;
$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('❤', 1024 * 1024);
echo 'After Reserving: ' . getMemory() . PHP_EOL;
unset($memory);
echo 'After Unsetting: ' . getMemory() . PHP_EOL;
$memory = new stdClass();
// reserve 3 mega bytes
$memory->reserve = str_repeat('❤', 1024 * 1024);
echo 'After Reserving again: ' . getMemory() . PHP_EOL;
// passing $memory object to shut down function
register_shutdown_function('shutdown', $memory);
输出将是:
Start: 349KB
After Reserving: 3426KB
After Unsetting: 349KB
After Reserving again: 3426KB
Start Shut Down: 3420KB
End Shut Down: 344KB
【讨论】:
new SplFixedArray(65536);。在我的系统上,每个空数组元素占用 16 个字节。