我读过这个:
我们有这个错误:
PHP Fatal error: Cannot redeclare fcXXX()
(previously declared in /xx.class.php:3007) in /xx.class.php on line 3014
是。的 line 3014
即可。在/xx.class.php
的代码源中,在函数fcXXX()
之前有一些类声明,类似于(简称):
class P1 {
...
}
class P2 {
...
}
function fcXXX() {
}
所以我得不到的是,在致命的“PHP Fatal error: Cannot redeclare class
”之前应该出现“Cannot redeclare function php
”类型的错误。
更奇怪的是,如果仔细阅读,问题就会在函数声明结束时出现。这是代码行:
3007 function fcXXX($p) {
3008 $a = strpos($p, '.');
3009 if ($a) {
3010 return trim(substr($p, $a+1));
3011 } else {
3012 return trim($p);
3013 }
3014 }
两件奇怪的事情:
有没有人遇到过这个问题,在哪里检查/测试来解决它? (请不要评论如何优化/代码有多糟糕,因为它不是我的,我不会修改它)
答案 0 :(得分:2)
你可能递归include
或require
- 定义。你有没有设置自动加载器?你知道它们是如何工作的吗,因为你可能想为每个类定义创建一个文件(ASAP)
考虑一下:
//index.php
include('someFile.php');
include('xx.class.php');
//someFile:
include('xx.class.php');//ERROR
//or even worse, in xx.Class.php:
include('someFile.php');//includes file that includes this file, which will include someFile again, which includes this file, which...
要避免这种情况,请使用include_once
或require_once
。这样可以避免两次包含相同的文件。
至于导致订单错误的原因:
在您收到错误无法重新声明类之前,您收到错误“无法重新声明函数的主要原因是,我认为,仅仅是因为PHP编译代码的方式。<登记/>
PHP不再保留您的代码了。你的代码被编译成字节码,所以它......我们说......在这个过程中发生了很大的变化
在PHP继续解析类之前,只需要解析 的函数。为什么?这很简单,因为PHP类方法本质上也是函数
他们使用zend_object_handlers
指针调用,在调用方法时,zval
已通过zend_class_entry
,以便zend_function
可以访问所有人范围内的数据(所有对象属性和方法以及您拥有的内容)。我不知道你的C知识是怎样的,但如果你想测试它:
ZEND_API zval* zend_call_method(
zval **object_pp,
zend_class_entry *obj_ce,
zend_function **fn_proxy,
const char *function_name,
int function_name_len,
zval **retval_ptr_ptr,
int param_count,
zval* arg1,
zval* arg2 TSRMLS_DC
) {
int result;
zend_fcall_info fci;
zval z_fname;
zval *retval;
HashTable *function_table;
zval **params[2];
params[0] = &arg1;
params[1] = &arg2;
fci.size = sizeof(fci);
/* fci.function_table = NULL;
* will be read form zend_class_entry of object if needed
*/
fci.object_ptr = object_pp ? *object_pp : NULL;
fci.function_name = &z_fname;
fci.retval_ptr_ptr = retval_ptr_ptr ? retval_ptr_ptr : &retval;
fci.param_count = param_count;
fci.params = params;
fci.no_separation = 1;
fci.symbol_table = NULL;
if (!fn_proxy && !obj_ce) {
/* no interest in caching and no information
* already present that is needed later inside
* zend_call_function.
*/
ZVAL_STRINGL(&z_fname, function_name, function_name_len, 0);
fci.function_table = !object_pp ? EG(function_table) : NULL;
result = zend_call_function(&fci, NULL TSRMLS_CC);
} else {
zend_fcall_info_cache fcic;
fcic.initialized = 1;
if (!obj_ce) {
obj_ce = object_pp ? Z_OBJCE_PP(object_pp) : NULL;
}
if (obj_ce) {
function_table = &obj_ce->function_table;
} else {
function_table = EG(function_table);
}
if (!fn_proxy || !*fn_proxy) {
if (zend_hash_find(
function_table, function_name,
function_name_len+1,
(void **) &fcic.function_handler) == FAILURE
) {
/* error at c-level */
zend_error(
E_CORE_ERROR,
"Couldn't find implementation for method %s%s%s",
obj_ce ? obj_ce->name : "",
obj_ce ? "::" : "", function_name
);
}
if (fn_proxy) {
*fn_proxy = fcic.function_handler;
}
} else {
fcic.function_handler = *fn_proxy;
}
fcic.calling_scope = obj_ce;
if (object_pp) {
fcic.called_scope = Z_OBJCE_PP(object_pp);
} else if (obj_ce &&
!(EG(called_scope) &&
instanceof_function(EG(called_scope), obj_ce TSRMLS_CC))) {
fcic.called_scope = obj_ce;
} else {
fcic.called_scope = EG(called_scope);
}
fcic.object_ptr = object_pp ? *object_pp : NULL;
result = zend_call_function(&fci, &fcic TSRMLS_CC);
}
if (result == FAILURE) {
/* error at c-level */
if (!obj_ce) {
obj_ce = object_pp ? Z_OBJCE_PP(object_pp) : NULL;
}
if (!EG(exception)) {
zend_error(
E_CORE_ERROR,
"Couldn't execute method %s%s%s",
obj_ce ? obj_ce->name : "",
obj_ce ? "::" : "", function_name
);
}
}
if (!retval_ptr_ptr) {
if (retval) {
zval_ptr_dtor(&retval);
}
return NULL;
}
return *retval_ptr_ptr;
}
正如您在此处所见,为了使方法可以调用,必须定义所有zend_function
。由于构造函数也是函数,我怀疑xx.Class.php
文件中的所有函数都已被解析。由于这些方法是在自己的(类)范围内定义的,因此在zend_object
实际注册之前不会存在名称冲突(除非有重复的方法)。
此外,任何体面的OO代码都会注册自动加载器功能。如果解析器不知道该function
关键字的作用,那么对象会是什么?
还有更多的东西,不仅如此,但我认为这必然是PHP注意到为什么你在注意到你正在重新定义一个类之前重新定义一个函数的原因。
可以找到一些有用的链接,其中包含有关zend引擎如何使用函数/方法的更详细说明here
保持带有the actual source标签的标签总是很有用,这样你就可以看到他们正在谈论的代码