";无法重新声明函数“”;而不是“;无法重新声明类";


"Cannot redeclare function" instead of "Cannot redeclare class"

我读过这个:

  • PHP致命错误:无法重新声明类
  • 无法重新声明函数php

我们有这个错误:

 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() {
}

所以我不知道在致命的"Cannot redeclare function php"之前应该有一个类型为"PHP Fatal error: Cannot redeclare class"的错误。

更奇怪的是,如果你仔细阅读,问题出现在函数声明的末尾。这是代码行:

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 }

两件奇怪的事:

  1. 在函数中没有"include"或"require"或其他什么,因此理论上不可能有这种错误
  2. 3014行在第3007行中引发错误(请仔细观察之前的详细错误)

有人遇到过这个问题吗?在哪里检查/测试可以解决这个问题?(请不要评论如何优化/代码有多糟糕,因为它不是我的,我不会修改它)

您可能递归地使用includerequire定义。你设置了自动加载器了吗?你知道它们是如何工作的吗?因为你可能想为每个类定义创建一个文件(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_oncerequire_once。这样可以避免两次包含同一个文件。

至于是什么原因导致错误的顺序
我认为,在得到错误cannot redclare class之前,得到错误"cannot redelare function"的主要原因只是因为PHP如何编译代码
PHP不再让您的代码保持原样。你的代码被编译成字节码,所以它。。。我们应该说。。。在这个过程中发生了很大的变化
只有的函数需要进行解析,PHP才能继续解析类。为什么?这很简单,因为PHP类方法本质上也是函数
它们是使用zend_object_handlers指针调用的,在调用方法时,传递zvalzend_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引擎如何与函数/方法一起工作的更详细描述
在手边保留一个带有实际源的选项卡总是很有用的,这样您就可以查看他们谈论的

代码