我在CodeIgniter支持的网站上遇到了一个令人困惑的问题。一个PHP类——TChild——会间歇性地开始抛出"cannot redclare class"错误,并破坏使用它的每个页面。我为确保声明代码只运行一次而付出的努力已经变得非常荒谬,但无济于事。tchild.php的顶部目前看起来是这样的:
(!( isset( $GLOBALS['tchild_counter'] ) ))
? $GLOBALS['tchild_counter'] = 1
: $GLOBALS['tchild_counter']++ ;
log_message("info", "tchild has been included " . $GLOBALS['tchild_counter'] . " times");
if($GLOBALS['tchild_counter'] != 1) {
log_message("info", "STOP RUNNING TWICE");
} else {
if ( ! defined('BASEPATH')) {
log_message("error", "BASEPATH not set; no direct script access allowed");
exit('No direct script access allowed');
}
log_message("info", "Basepath is fine, checking if tchild exists (proc".getmypid().")");
log_message("info", "why does that last line seem to run twice?");
if ( ! class_exists('TChild')) {
log_message("info", "TChild does not exist, creating it");
if(class_exists('tchild')) {
log_message("info", "tchild apparently exists?");
} else {
log_message("info","okay, I'm extra-sure that tchild doesn't exist");
}
class TChild extends ActiveRecord'Model {
这是一大堆一些东西,但至少它应该确保TChild只定义一次,对吧?嗯,不。大多数情况下,它工作得很好,但偶尔服务器会进入某种奇怪的状态,尽管有相反的证据,但它会决定TChild被声明两次。从我添加的大量日志记录语句中,我可以肯定,tchild.php一开始只包含在一个require_sonce中,而且它肯定不会多次到达类定义。(然而,即使运行正常,它也会吐出两个"停止运行两次"日志条目。我不知道为什么,但至少这不会破坏任何东西。)
一旦它开始失败,它将继续破坏一切(通常)几分钟,然后由于与最初失败一样神秘的原因自行修复。
我不知道这里发生了什么。在谷歌上搜索后,我尝试将apc.enabled=0添加到php.ini中,但这对崩溃或性能都没有影响。(我不认为它一开始就被启用了,但它值得一试。)
更新:
啊哈,在第三方库中有一个require语句,它不止一次地包含了该文件。我仍然不知道为什么这个类被重新声明,尽管我在里面包了所有的if/else块,但至少现在一切都正常了。
与其只是重复文件被多次包含的事实,不如让PHP告诉你它来自哪里:
- http://php.net/manual/en/function.debug-print-backtrace.php
- 受John B以上评论的启发,
__FILE__
可能会显示您的文件不知何故有两个名称
在log_message("info","STOP RUNNING TWICE")之后返回如何;回来或退出(0);您的文件似乎不止一次被包含,并且您从未停止在代码的任何部分。它只是到达class TChild extends ActiveRecord'Model
所在的部分,然后被再次声明。
如果我在你刚刚做的地方:
if ( ! defined('BASEPATH')) {
log_message("error", "BASEPATH not set; no direct script access allowed");
exit('No direct script access allowed');
}
if (class_exists('TChild') || class_exists('tchild')) {
log_message("info", "class already exists returning");
return; // or exit(0);
}
class TChild extends ActiveRecord'Model {
…
你问我,你应该检查文件是从哪里包含的,除非它是从其他代码堆包含的,这很难追踪。但是上面的代码应该足够了。您可以使用debug_backtrace
从其他实用程序(可以是xhprof或xdebug)中检查文件的包含位置。