おそらく、定義を再帰的にinclude
またはrequire
-ingしています。オートローダーの設定はお済みですか?クラス定義ごとにファイルを作成したい場合があるため (できるだけ早く)、それらがどのように機能するか知っていますか。
このことを考慮:
//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
。これにより、同じファイルを 2 回含めることを回避できます。
エラーの順序について:クラスを再宣言できないというエラーが発生する前に「関数を再宣言できません」
というエラーが発生する主な理由は、単に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
実際に登録される前に名前の競合が発生することはありません (メソッドが重複していない限り)。
さらに、オブジェクト指向コードの適切なビットは、オートローダー機能を登録します。function
パーサーがそのキーワードから何を判断するかを知らなかった場合、オブジェクトは何になるでしょうか?
それだけではありませんが、クラスを再定義していることに気付く前に、関数を再定義しようとしている理由に PHP が気付くのはそのためだと思います。
Zendエンジンが関数/メソッドでどのように機能するかについてのより詳細な説明を含むいくつかの便利なリンクがここにあり
ます。について話している