困惑:PHP致命错误:在第0行的Unknown中没有堆栈帧抛出异常?

时间:2012-05-07 16:09:19

标签: php exception error-handling

我发现错误的一个常见原因是an exception being thrown from within an exception handler。我很确定在我正在尝试调试的应用程序中不会发生这种情况......但我已将所有初始化处理行放在try.cat中的index.php顶部。*

显然也可能因为some things cannot be serialized to be stored in a session而发生。这个应用程序最多将数组存储到会话中(相当多),但我确信它不会存储任何与众不同的内容。

有人评论说它发生在他们身上,因为他们的主键是needed to be CHAR(32) instead of INT(11)。这个应用程序中的PK都是INT。

其他建议是,PHP 5.3.3 fixed in 5.3.6full diskneed to typecast a SimpleXML value可能会出现问题。我们碰巧运行PHP 5.3.3,但在这种情况下升级必须是最后的手段。它并不总是如此。

更新/注意:我实际上无法自己重现错误,只能在日志中看到它,请参阅下面的段落,了解相信错误发生的地方......

*从错误日志中,似乎至少发生了一个地方是index.php。我推断这只是因为它在某些条目中通过引用URL表示。 try / catch代码目前仅围绕脚本的“顶部”初始化部分,下面主要是HTML输出。输出中有一些PHP代码(虽然很简单),所以我可能需要对它进行测试。这是catch部分,它不会在日志中产生任何输出:

} catch (Exception $e) {
    error_log(get_class($e)." thrown. Message: ".$e->getMessage(). "  in " . $e->getFile() . " on line ".$e->getLine());
    error_log('Exception trace stack: ' . print_r($e->getTrace(),1));
}

非常感谢有关此的任何提示!

编辑:PHP作为Apache模块运行(服务器API:Apache 2.0处理程序)。我不认为有任何PHP加速器在使用,但它可能只是我不知道如何分辨。 listed on Wikipedia中没有一个在phpinfo()中。

据我所知,MPM是prefork。这是我第一次看到MPM:

# ./httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c

12 个答案:

答案 0 :(得分:18)

问题

简而言之,你有一个异常抛出某个地方,你不知道到现在为止你无法重现错误:它只发生在一些人身上,但不适合你。你知道它会发生在其他人身上,因为你会在错误日志中看到它。

重现问题

由于您已经消除了需要重现错误的常见原因。如果您知道哪个参数会导致错误,则应该很容易找到错误。

  • 如果你知道所有的POST / GET参数,很可能就足够了。
  • 如果您无法使用这些进行复制,则需要了解其他请求标头。例如用户代理,接受编码,......
  • 如果您仍然无法重现,则变得非常困难:错误可能取决于状态(会话),当前时间,源IP地址等。

自定义日志方法

让我们开始简单:要获取所有参数,您可以在受影响的php文件的最开头写一些类似的内容:

file_put_contents("/path/to/some/custom_error_log", date()."\n".print_r(get_defined_vars(), true), FILE_APPEND | LOCK_EX);

不要忘记custom_error_log文件必须可写入您的php应用程序。然后,当错误日志中发生错误时,在custom_error_log文件中找到相应的行。希望每秒没有多少请求,以便您仍然可以识别请求。也许错误日志中的一些其他参数如source ip可以帮助您识别请求(如果您的错误日志显示)。 从该数据中,使用相同的POST / GET参数重建请求。

tcpdump方法

下一个非常简单的选项,但要求您在目标计算机上具有root访问权限是安装tcpflow。然后创建一个文件夹,cd进入该文件夹,然后执行(以root身份)tcpflow "port 80"。选项(端口80)是pcap过滤器表达式。要查看您可以执行的所有操作,请参阅man pcap-filter。这些过滤器表达式可以做很多事情。

现在tcpflow将记录端口80上的所有tcp连接,通过组合属于一个连接的包并将此数据转储到文件来重建完整数据交换,每个连接创建两个新文件,一个用于传入数据,一个用于传出数据。现在,再次根据错误日志中的时间戳和文件的上次修改时间戳,找到导致错误的连接的文件。然后,您将获得完整的http请求标头。您现在可以完全重建HTTP请求,包括设置相同的accept-encoding,user-agent等。您甚至可以将请求直接传递到netcat,重放确切的请求。请注意,像sessionid这样的参数可能会妨碍你。如果php发现会话过期,您可能只是重定向到登录或其他意外的事情。您可能需要交换会话ID等内容。

嘲笑更多东西

如果这些都没有帮助,并且您无法在计算机上重现错误,那么您可以尝试模拟难以模拟的所有内容。例如源ip地址。这可能会使一些特技成为必要,但有可能:您可以使用ssh与" -w"连接到您的服务器。选项,创建隧道接口。然后将违规的IP地址分配给您自己的计算机并设置路由(路由添加主机)规则以将隧道用于特定的IP。如果您可以将两台计算机直接连接在一起,那么您甚至可以在没有隧道的情况下进行连接。

不要嘲笑应该是最神圣的会议。您可以使用print_r(get_defined_vars())方法读取所有会话变量。然后,您需要创建一个具有完全相同变量的会话。

询问用户

另一种选择实际上是询问用户他在做什么。也许你可以按照与他相同的步骤进行复制。

如果这些都没有帮助

如果这些都没有帮助......那么......那就非常困难了。知识产权已经非常不可能了。它可能是一个GEO-IP库导致来自特定区域的IP错误,但这些都是不太可能的事情。如果以上都没有帮助您重现问题,那么您可能只是在custom_log_file-call / tcpflow生成的所有数据中找不到正确的请求。尝试通过获得更准确的时间戳来增加您的机会。你可以在php中使用microtime()作为date()的替代品。检查您的网络服务器,如果您能在错误日志中获得比秒更精确的内容。编写自己的" tail"的实现,为您提供更准确的时间戳,...减少系统负载,这样您就不必从那么多数据中进行选择(尝试另一个一天中的时间,用户负载到不同的服务器,...)

一旦可以重现

,就可以解决问题

现在一旦你可以重现它应该是在公园散步找到实际的原因。您可以通过反复试验找到导致错误的参数,或者将其与导致错误的其他请求进行比较,寻找相似之处。然后你可以看到这个参数做了什么,哪些库访问它等等。你可以逐个禁用每个使用该参数的组件,直到你再也不能重现为止。然后你得到了你的组件,可以深入探讨这个问题。

告诉我们你找到了什么。我很好奇; - )。

答案 1 :(得分:4)

我也有这样的错误。发现我在会话类中返回了一个sql对象(由session_handler使用),而不是返回任何内容,或者至少不返回sql对象。首先看看你的_write和_read方法,如果你也返回一些不正确的东西。

Notice: ... Unknown on line 0 - How to find correct line, it's NOT "line 0"

答案 2 :(得分:3)

不是将代码包装在try / catch块中,而是在注册异常处理程序时会发生什么?很明显,你的try / catch块没有捕获异常,从而导致错误记录到Apache。通过注册处理程序,您可以确保处理任何未捕获的异常。

此外,如果您在应用程序中使用名称空间,请确保在catch块中编写\ Exception(或通过use语句包含Exception类)。

答案 3 :(得分:2)

这可能有点晚,但我将网站从本地移动到远程服务器时发现了一个问题。我正在使用Concrete5 cms本地开发了我的站点(xampp中的Windows 8),然后上传到运行Cent 0S的远程服务器

默认情况下,Windows mysql不区分大小写,并创建了一个小写数据库。一旦将其上传到远程服务器,我收到了“在第0行的未知中没有堆栈帧的情况下抛出异常?”

然后我更正了数据库表格案例,我的网站又开始工作了。

答案 4 :(得分:2)

对我们来说,这个错误是由于无意中序列化SimpleXML对象造成的。

如果您正在使用5.3.3的SimpleXML对象,请确保在将序列中的值序列化时,将节点值转换为您需要的任何值(例如字符串)。

<强>之前

  $token = $response->Token->Value;
  /* token saved in session, results in line 0 error */

<强>后

$token = (string) $response->Token->Value;
  /* token saved in session, no error */

答案 5 :(得分:2)

我意识到这个问题已经得到了回答,但我会补充一下,因为它可能有助于某人:

我设法(无意中)在没有堆栈帧的情况下产生错误,该函数使用自己的错误处理程序来维持对执行的控制,同时调用潜在的&#34;危险的&#34;功能,像这样:

// Assume the function my_error_handler() has been defined to convert any
// PHP Errors, Warnings, or Notices into Exceptions.

function foo() {
    // maintain control if danger() crashes outright:
    set_error_handler('my_error_handler');

    try {
        // Do some stuff.

        $r = danger();
    } catch (Exception $e) {
        $r = 'Bad Stuff, Man!';
    }

    restore error_handler();
    return $r;
}

&#34;无法追踪的失败&#34;会在程序执行结束时发生 如果逻辑在&#34;做一些事情&#34;直接从foo()返回,绕过对restore_error_handler()的调用。我从经验中得到的是:

  1. PHP维护错误处理程序的堆栈,每次调用set_error_handler()时都会更深/更高。
  2. 如果您将错误处理程序推入堆栈并且在程序退出之前不要自行清理,则可能会发生错误的事情#34;通常&#34;。
  3. 这是一个难以隔离的难题 - 我基本上将问题缩小到上述功能,然后盯着它直到我的眼睛流血。

    那么,我知道现在知道什么,我怎么跟踪这个呢?因为我不知道如何检查PHP错误处理程序&#34; stack&#34;直接地,我认为使用Singleton对象封装PHP错误处理程序的所有设置/恢复操作可能是有意义的。至少那时可以在正常退出程序之前检查Singleton的状态,如果&#34;悬挂&#34;在PHP吓坏之前,检测到错误处理程序以生成合理的失败/警告消息。

答案 6 :(得分:1)

错误地填充Illuminate Eloquent模型的Fillable属性后,我遇到了相同的错误。请注意数组的最后3个元素,其中一个是缺少昏迷

protected $fillable = [
    'budget',        
    'routestatus' ,
    'userroutenumber'
    'totalmovingseconds',
    'totalidleseconds'
];

答案 7 :(得分:0)

我遇到了同样的错误,它似乎将服务器从centos 5升级到centos 6并将PHP从5.4降级到5.3。实际问题是PHP apc,未正确配置。检查你的APC。我使用的是Symfony2,所以你可以在Symfony Unable to allocate memory for pool

找到一些帮助

答案 8 :(得分:0)

产生此错误的一种简单方法是使用register_globals = On的旧服务器。那么你只需要两行代码:

<?php
    $_SESSION["my_var"] = "string";
    $my_var = new MyClass(); //could be any class, i guess
?>

只要您重新加载此页面一次,您就会收到Exception thrown without a stack frame in Unknown on line 0 - 错误。似乎类的实例和(会话)变量之间存在冲突 至少这是我如何得到这个很难调试的烦人错误。

答案 9 :(得分:0)

我有完全相同的错误。一个非常空间的情况:如果你将一个未命名的函数(闭包)钩子连接到一个对象实例的钩点。之后,您尝试序列化此对象。

答案 10 :(得分:0)

当我在一些Symfony包上更改名称空间时,我遇到了这个问题。删除symfony缓存目录中的文件可以解决问题。

答案 11 :(得分:0)

您可能在数据库中有一个损坏/不一致的表。尝试转储数据库。如果你得到一个错误的时间。修复该表和问题应该消失。

正是出于这个原因,干净安装才起作用。干净的安装就是干净的。

mysqlcheck应该可以工作,但是如果它没有显示并且问题仍然在上面做。