困惑:PHP 致命错误:在第 0 行的 Unknown 中没有堆栈帧的情况下抛出异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10485371/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Baffled: PHP Fatal error: Exception thrown without a stack frame in Unknown on line 0?
提问by groovenectar
I have found that one common reason for the error is an exception being thrown from within an exception handler. I'm quite sure this doesn't happen in the application I'm trying to debug... But I've put all the initialization processing lines at the top of index.php in a try/catch.*
我发现错误的一个常见原因是从异常处理程序中抛出的异常。我很确定这不会发生在我正在尝试调试的应用程序中......但我已经将所有初始化处理行放在了 index.php 顶部的 try/catch 中。*
It can apparently also happen because some things cannot be serializedto be stored in a session. At most this application stores arrays into the session (quite a bit), but I'm confident that it doesn't store anything too out of the ordinary in it.
它显然也可能发生,因为有些东西无法序列化以存储在 session 中。这个应用程序最多将数组存储到会话中(相当多),但我相信它不会在其中存储任何异常的东西。
Someone commented that it happened to them because their primary key needed to be CHAR(32) instead of INT(11). The PK's in this app are all INTs.
有人评论说这发生在他们身上是因为他们的主键需要是 CHAR(32) 而不是 INT(11)。此应用程序中的 PK 都是 INT。
Other suggestions are that it could be a problem with PHP 5.3.3fixed in 5.3.6, full disk, and a need to typecast a SimpleXML value. We do happen to be running PHP 5.3.3, but upgrading would have to be a last resort in this case. It hasn't always been doing this.
其他建议是它可能是PHP 5.3.3在 5.3.6 中修复的问题、完整磁盘以及需要对 SimpleXML 值进行类型转换。我们碰巧运行的是 PHP 5.3.3,但在这种情况下升级必须是最后的手段。它并不总是这样做。
UPDATE/NOTE: I actually can't reproduce the error myself, only see it happening in the logs, see below paragraph for where I believethe error is happening...
更新/注意:我实际上无法自己重现错误,只能在日志中看到它发生,请参阅下面的段落,了解我认为错误发生的地方...
* From the error logs, it seems likely that at least one place it is happening is index.php. I am deducing this only because it is indicated in some entries by a referring URL. The try/catch code is currently only around the "top" initialization portion of the script, below that is mostly the HTML output. There is some PHP code in the output (pretty straightforward stuff though), so I may need to test that. Here is the catch part, which is not producing any output in the logs:
* 从错误日志来看,似乎至少有一个地方是 index.php。我推断这只是因为它在某些条目中由引用 URL 指示。try/catch 代码目前仅围绕脚本的“顶部”初始化部分,其下方主要是 HTML 输出。输出中有一些 PHP 代码(虽然很简单),所以我可能需要测试一下。这是捕获部分,它不会在日志中产生任何输出:
} 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));
}
Would really appreciate any tips on this!
真的很感激这方面的任何提示!
EDIT: PHP is running as an Apache module (Server API: Apache 2.0 Handler). I don't think there are any PHP accelerators in use, but it could just be that I don't know how to tell. None of the ones listed on Wikipediaare in phpinfo().
编辑:PHP 作为 Apache 模块运行(服务器 API:Apache 2.0 处理程序)。我认为没有使用任何 PHP 加速器,但可能只是我不知道如何判断。的那些没有在维基百科上列出的是phpinfo()函数。
As far as I can tell the MPM is prefork. This is the first I'd ever looked into the MPM:
据我所知,MPM 是 prefork。这是我第一次研究 MPM:
# ./httpd -l
Compiled in modules:
core.c
prefork.c
http_core.c
mod_so.c
采纳答案by yankee
The problem
问题
In short you have a exception thrown somewhere, you have no idea where and up until now you could not reproduce the error: It only happens for some people, but not for you. You know that it happens for other people, because you see that in the error logs.
简而言之,您在某处抛出了异常,您不知道在哪里并且直到现在您都无法重现错误:它只发生在某些人身上,而不会发生在您身上。您知道它发生在其他人身上,因为您在错误日志中看到了这一点。
Reproduce the problem
重现问题
Since you have already eliminated the common reasons you will need to reproduce the error. If you know which parameter will cause the error it should be easy to locate the error.
由于您已经消除了需要重现错误的常见原因。如果您知道哪个参数会导致错误,那么定位错误应该很容易。
- Most likely it is enough if you know all the POST/GET parameters.
- If you can't reproduce with just these, you need to know additional request headers. Such as user agent, accept-encoding,...
- If you still can't reproduce, then it becomes very difficult: The error may depend on a state (a session), the current time, the source ip address or the like.
- 如果您知道所有 POST/GET 参数,很可能就足够了。
- 如果你不能只用这些来重现,你需要知道额外的请求标头。例如用户代理,接受编码,...
- 如果还是不能重现,那就很难了:错误可能取决于状态(会话)、当前时间、源ip地址等。
The custom log method
自定义日志方法
Let's start simple: To get all parameters you can write in the very beginning of the affected php file something like:
让我们从简单的开始:要获取所有参数,您可以在受影响的 php 文件的最开头编写如下内容:
file_put_contents("/path/to/some/custom_error_log", date()."\n".print_r(get_defined_vars(), true), FILE_APPEND | LOCK_EX);
Don't forget that the custom_error_log file must be writable to your php application. Then, when the error occurs in the error log, find the corresponding lines in your custom_error_log file. Hopefully there are not to many requests per second so that you can still identify the request. Maybe some additional parameters in the error log like source ip can help you identify the request (if your error log shows that). From that data, reconstruct a request with the same POST/GET parameters.
不要忘记 custom_error_log 文件必须可写入您的 php 应用程序。然后,当错误日志中出现错误时,在您的 custom_error_log 文件中找到相应的行。希望每秒没有太多请求,以便您仍然可以识别请求。也许错误日志中的一些附加参数(如 source ip)可以帮助您识别请求(如果您的错误日志显示)。根据该数据,使用相同的 POST/GET 参数重建请求。
The tcpdump method
tcpdump 方法
The next option that is very simple as well, but requires you to have root-access on your target machine is to install tcpflow. Then create a folder, cd into that folder and simply execute (as root) tcpflow "port 80". The option (port 80) is a pcap filter expression. To see all you can do with that, see man pcap-filter. There is a lot what these filter expressions can do.
下一个选项也非常简单,但要求您在目标机器上具有 root 访问权限,即安装 tcpflow。然后创建一个文件夹, cd 进入该文件夹并简单地执行(以 root 身份)tcpflow "port 80"。选项(端口 80)是一个 pcap 过滤器表达式。要查看您可以用它做什么,请参阅man pcap-filter。这些过滤器表达式可以做很多事情。
Now tcpflow will record all tcp connections on port 80, reconstruct the full data exchange by combining the packages belonging to one connection and dump this data to a file, creating two new files per connection, one for incoming data and one for outgoing data. Now find the files for a connection that caused an error, again based on the timestamp in your error log and by the last modified timestamp of the files. Then you get the full http request headers. You can now reconstruct the HTTP request completely, including setting the same accept-encoding, user-agent, etc. You can even pipe the request directly into netcat, replaying the exact request. Beware though that some arguments like a sessionid might be in your way. If php discovers that a session is expired you may just get a redirect to a login or something else that is unexpected. You may need to exchange things like the session id.
现在 tcpflow 将记录端口 80 上的所有 tcp 连接,通过组合属于一个连接的包来重建完整的数据交换并将这些数据转储到一个文件中,每个连接创建两个新文件,一个用于传入数据,一个用于传出数据。现在再次根据错误日志中的时间戳和文件的最后修改时间戳查找导致错误的连接的文件。然后您将获得完整的 http 请求标头。您现在可以完全重构 HTTP 请求,包括设置相同的接受编码、用户代理等。您甚至可以将请求直接通过管道传输到 netcat,重放准确的请求。请注意,像 sessionid 这样的一些参数可能会妨碍您。如果 php 发现会话已过期,您可能只会重定向到登录名或其他意外情况。
Mocking more things
嘲笑更多的东西
If none of this helps and you can't reproduce the error on your machine, then you can try to mock everything that is hard to mock. For example the source ip adress. This might make some stunts necessary, but it is possible: You can connect to your server using ssh with the "-w" option, creating a tunnel interface. Then assign the offending ip adress to your own machine and set routes (route add host ) rules to use the tunnel for the specific ip. If you can cable the two computers directly together then you can even do it without the tunnel.
如果这些都没有帮助并且您无法在您的机器上重现错误,那么您可以尝试模拟所有难以模拟的内容。例如源IP地址。这可能需要一些特技,但这是可能的:您可以使用带有“-w”选项的 ssh 连接到您的服务器,创建一个隧道接口。然后将有问题的 ip 地址分配给您自己的机器并设置路由(路由添加主机)规则以使用特定 ip 的隧道。如果您可以将两台计算机直接连接在一起,那么您甚至可以在没有隧道的情况下进行。
Don't foget to mock the session which should be esiest. You can read all session variables using the method with print_r(get_defined_vars()). Then you need to create a session with exactly the same variables.
不要忘记嘲笑应该是最简单的会话。您可以使用带有 print_r(get_defined_vars()) 的方法读取所有会话变量。然后您需要使用完全相同的变量创建一个会话。
Ask the user
询问用户
Another option would be actually ask the user what he was doing. Maybe you can follow the same steps as he and can reproduce.
另一种选择实际上是询问用户他在做什么。也许您可以按照与他相同的步骤进行操作并可以重现。
If none of this helps
如果这些都没有帮助
If none of that helps... well... Then it gets seriously difficult. The IP-thing is already highly unlikely. It could be a GEO-IP library that causes the error on IPs from a specific region, but these are all rather unlikely things. If none of the above helped you to reproduce the problem, then you probably just did not find the correct request in all the data generated by the custom_log_file-call / tcpflow. Try to increase your chances by getting a more accurate timestamp. You can use microtime() in php as a replacement for date(). Check your webserver, if you can get something more accurate than seconds in your error log. Write your own implementation of "tail", that gives you a more accurate timestamp,... Reduce the load on the system, so that you don't have to choose from that much data (try another time of day, load of users to different servers,...)
如果这些都没有帮助......好吧......那么它就会变得非常困难。IP 的事情已经不太可能了。它可能是一个 GEO-IP 库,导致特定区域的 IP 出现错误,但这些都是不太可能的事情。如果以上都没有帮助您重现问题,那么您可能只是没有在 custom_log_file-call / tcpflow 生成的所有数据中找到正确的请求。尝试通过获得更准确的时间戳来增加您的机会。您可以在 php 中使用 microtime() 作为 date() 的替代。检查您的网络服务器,如果您可以在错误日志中获得比秒更准确的信息。编写您自己的“tail”实现,它为您提供更准确的时间戳,...减少系统负载,这样您就不必从那么多数据中进行选择(尝试一天中的另一个时间,
circle the problem once you can reproduce
一旦您可以重现问题,请圈出问题
Now once you can reproduce it should be a walk in the park to find the actual cause. You can find the parameter that causes the error by trial and error or by comparing it to other requests that caused an error, too, looking for similarities. And then you can see what this parameter does, which libraries access it, etc. You can disable every component one by one that uses the parameter until you can't reproduce anymore. Then you got your component and can dive into the problem deeper.
现在,一旦您可以重现,就应该在公园里散步以找到真正的原因。您可以通过反复试验或通过将其与导致错误的其他请求进行比较来查找导致错误的参数,以寻找相似之处。然后你可以看到这个参数的作用,哪些库访问它等等。你可以一个一个地禁用使用该参数的每个组件,直到你不能再重现。然后你得到了你的组件,可以更深入地研究问题。
Tell us what you found. I am curious ;-).
告诉我们你发现了什么。我好奇 ;-)。
回答by djot
I had such an error, too. Found out that I returned a sql object in my session class (that was used by the session_handler) instead of returning nothing or at least not the sql object. First look into your _write and _read methods, if you too return some incorrect stuff.
我也有这样的错误。发现我在会话类(由 session_handler 使用)中返回了一个 sql 对象,而不是什么都不返回,或者至少不返回 sql 对象。首先查看你的 _write 和 _read 方法,如果你也返回了一些不正确的东西。
Notice: ... Unknown on line 0 - How to find correct line, it's NOT "line 0"
回答by Peter Kruithof
Instead of wrapping code in a try/catch block, what happens when you register an exception handler? Clearly your try/catch block is not catching the exception, thus resulting in the errors logged to Apache. By registering a handler, you can be sure any uncaught exception is handled.
注册异常处理程序时会发生什么,而不是将代码包装在 try/catch 块中?很明显,您的 try/catch 块没有捕获异常,从而导致错误记录到 Apache。通过注册处理程序,您可以确保处理任何未捕获的异常。
Also, if you're using namespaces in your application, make sure you write \Exception in your catch block (or include the Exception class via a use statement).
此外,如果您在应用程序中使用命名空间,请确保在 catch 块中写入 \Exception (或通过 use 语句包含 Exception 类)。
回答by Sean
This may be a little late but one issue I discovered when moving a site from a local to a remote server. I was using Concrete5 cms had developed my site locally(windows 8 in xampp) and then uploaded to a remote server running Cent 0S
这可能有点晚了,但是我在将站点从本地服务器移动到远程服务器时发现了一个问题。我正在使用 Concrete5 cms 在本地开发我的站点(xampp 中的 windows 8),然后上传到运行 Cent 0S 的远程服务器
Windows mysql by default is case insensitive and created a lower case database. Once this was uploaded to the remote server I received the "Exception thrown without a stack frame in Unknown on line 0?"
默认情况下,Windows mysql 不区分大小写并创建了一个小写数据库。一旦将其上传到远程服务器,我就会收到“在第 0 行的 Unknown 中没有堆栈帧的情况下抛出的异常?”
I then corrected the database tables case and my site started working again.
然后我更正了数据库表案例,我的网站又开始工作了。
回答by David
For us, this error was due to inadvertently serializing SimpleXML objects.
对我们来说,这个错误是由于无意中序列化了 SimpleXML 对象。
If you are using SimpleXML objects with 5.3.3, make sure you are are casting the node values to whatever you need (e.g. string) if you are serializing the values in the session.
如果您在 5.3.3 中使用 SimpleXML 对象,请确保在序列化会话中的值时将节点值转换为您需要的任何值(例如字符串)。
Before:
之前:
$token = $response->Token->Value;
/* token saved in session, results in line 0 error */
After:
之后:
$token = (string) $response->Token->Value;
/* token saved in session, no error */
回答by Peter
I realize this question has already been answered, but I'll add this since it may help someone:
我意识到这个问题已经得到了回答,但我会添加这个,因为它可能对某人有所帮助:
I managed to (unintentionally) produce errors without a stack frame from a function which used its own error handler to maintain control of execution while calling a potentially "dangerous" function, like this:
我设法(无意中)在没有堆栈帧的情况下从函数中产生错误,该函数使用自己的错误处理程序来保持对执行的控制,同时调用潜在的“危险”函数,如下所示:
// 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;
}
The "untraceable failure" would happen at the end of the program executionif the logic in "Do some stuff" returned from foo() directly, bypassing the call to restore_error_handler(). What I took away from the experience is this:
如果“做一些事情”中的逻辑直接从 foo() 返回,绕过对 restore_error_handler() 的调用,那么“无法追踪的失败”将在程序执行结束时发生。我从这次经历中学到的是:
- PHP maintains a stackof error handlers which gets deeper/taller with each call to set_error_handler().
- Bad Stuff can happen if you push error handlers onto the stack and don't clean up after yourself before the program exits "normally".
- PHP 维护了一堆错误处理程序,每次调用 set_error_handler() 都会变得更深/更高。
- 如果您将错误处理程序推送到堆栈上并且在程序“正常”退出之前不自行清理,则可能会发生坏事。
This was a tough bug to isolate - I basically narrowed the problem down to the above function and then stared at it until my eyes bled.
这是一个很难隔离的错误 - 我基本上将问题缩小到上述功能,然后盯着它直到我的眼睛流血。
So how would I have tracked this down, knowing what I know now? Since I don't know of any way to inspect the PHP error handler "stack" directly, I'm thinking it might make sense to use a Singleton object to encapsulate all set/restore operations for PHP error handlers. At least then it would be possible to inspect the state of the Singleton before exiting the program normally, and if "dangling" error handlers are detected to generate a sensible failure/warning message before PHP freaks out.
那么,知道我现在所知道的,我将如何追踪到这一点?由于我不知道有什么方法可以直接检查 PHP 错误处理程序“堆栈”,因此我认为使用 Singleton 对象来封装 PHP 错误处理程序的所有设置/恢复操作可能是有意义的。至少可以在正常退出程序之前检查 Singleton 的状态,并且如果检测到“悬空”错误处理程序以在 PHP 崩溃之前生成合理的故障/警告消息。
回答by Akos
I had completely the same error. A very spacial case: if you connect an unnamed function (closure) hook to an object instance's hook point. After that you try to serialize this object.
我有完全相同的错误。一个非常特殊的情况:如果将未命名的函数(闭包)挂钩连接到对象实例的挂钩点。之后,您尝试序列化此对象。
回答by Dennis Berg
I had the same error after filling the Illuminate Eloquent model's Fillable property incorrectly. Note the last 3 elements of the array, one is missing a coma.
在错误地填充 Illuminate Eloquent 模型的 Fillable 属性后,我遇到了同样的错误。请注意数组的最后 3 个元素,其中一个缺少 coma。
protected $fillable = [
'budget',
'routestatus' ,
'userroutenumber'
'totalmovingseconds',
'totalidleseconds'
];
回答by ssjcory
This problem occurred for me when I changed the namespace on a few Symfony bundles. Deleting the files in the the symfony cache directory fixed the issue.
当我更改一些 Symfony 包上的命名空间时,我遇到了这个问题。删除 symfony 缓存目录中的文件解决了这个问题。
回答by Roland Booth
Likely you have a corrupt/inconsistent table in the database. Try dumping the database. If you get a error that's the time. Repair that table and the issue should go away.
您可能在数据库中有一个损坏/不一致的表。尝试转储数据库。如果你得到一个错误,那就是时间。修理那张桌子,问题就会消失。
It is for this reason why clean install works. The clean install is just that clean.
正是因为这个原因,干净安装才有效。干净的安装就是那么干净。
mysqlcheck should work but if it does not show and issue still do above.
mysqlcheck 应该可以工作,但如果它没有显示并且问题仍然在上面执行。

