-
- class A {
- public function __toString() {
- return 'bar';
- }
- }
- $a = new A();
- define('foo', $a);
- echo foo;
- // 出力バー
コードをコピー
PHP での定義の実装方法:
-
ZEND_FUNCTION(define)
- {
- char *name;
- int name_len;
- zval *val;
- zval *val_free = NULL;
- zend_bool non_cs = 0;
- int case_sensitive = CONST_CS ;
- zend_constant c;
// 3 つのパラメータ、string、zval、bool を受け取ります
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) {
- return;
- }
// 大文字と小文字は区別されますか?
- if(non_cs) {
- case_sensitive = 0;
- }
/ / クラス定数を定義するとエラーが報告されます
- if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) {
- zend_error(E_WARNING, "クラス定数は使用できません)定義または再定義される" );
- RETURN_FALSE;
- }
// 実際の値を取得し、val
- repeat:
- switch (Z_TYPE_P(val)) {
- case IS_LONG:
- で保存しますcase IS_DOUBLE:
- case IS_STRING :
- case IS_BOOL:
- case IS_RESOURCE:
- case IS_NULL:
- Break;
- case IS_OBJECT:
- if (!val_free) {
- if (Z_OBJ_HT_P(val)->get) {
- val_free = val = Z_OBJ_HT_P(val)- >get(val TSRMLS_CC);
- gotorepeat;
- } else if (Z_OBJ_HT_P(val)->cast_object) {
- ALLOC_INIT_ZVAL(val_free);
- if (Z_OBJ_HT_P(val)->cast_object) (val, val_free, IS_STRING TSRMLS_CC) == SUCCESS) {
- val = val_free;
- ブレーク;
- }
- }
- }
- /* ブレークなし */
- デフォルト:
- zend_error(E_WARNING,"定数はスカラー値にのみ評価できます");
- if ( val_free) {
- zval_ptr_dtor(&val_free);
- }
- RETURN_FALSE;
- }
-
- //定数を構築
- c.value = *val;
- zval_copy_ctor(&c.value);
- if (val_free) {
- zval_ptr_dtor(&val_free);
- }
- c.flags = case_sensitive; /* 非永続的 */ // 大文字と小文字を区別しない場合は 0、大文字と小文字を区別する場合は 1
- c.name = zend_strndup(name, name_len);
- c.name_len = name_len+ 1;
- c.module_number = PHP_USER_CONSTANT; // カーネル以外の定数、ただしユーザー定義の定数をマークします
- // 定数を登録します
- if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE ;
- }
- }
-
-
-
コードをコピーします
repeatで始まるループではgotoステートメントT_Tも使用されることに注意してください
このコードの機能は次のとおりです。
int、float、string、bool、resource、null については、実際に定数を定義するときにこれらの値を直接使用します。
オブジェクトの場合、オブジェクトを上記の 6 つのタイプのいずれかに変換する必要があります (変換後もオブジェクトである場合は、変換を続行します)
オブジェクトを 6 つのタイプのいずれかに変換するにはどうすればよいですか?コードの観点から見ると、次の 2 つの方法があります。
if (Z_OBJ_HT_P(val)->get) {- val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
- gotorepeat;
- }
- // __toString() メソッドはCast_object で呼び出されます
- else if (Z_OBJ_HT_P(val)->cast_object) {
- ALLOC_INIT_ZVAL(val_free);
- if (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS)
- {
- val = val_free;
- ブレーク;
- }
- }
-
-
-
コードをコピー
1、Z_OBJ_HT_P(val)->get、マクロ展開後、(*val).value.obj.handlers->get
2、Z_OBJ_HT_P(val)->cast_object、マクロ展開後は (*val).value.obj.handlers->cast_object になります
handlers は、多くの関数ポインターを含む構造体です。詳細な定義については、_zend_object_handlers を参照してください。この構造体の関数ポインタは、オブジェクト属性の読み取り/変更、オブジェクト メソッドの取得/呼び出しなど、オブジェクトを操作するために使用されます。get や Cast_object もその中に含まれます。
一般オブジェクトの場合、PHP は標準の Cast_object 関数 zend_std_cast_object_tostring を提供します。コードは php-src/zend/zend-object-handlers.c にあります。
-
ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
- {
- zval *retval;
- zend_class_entry *ce;< ;/p& gt ;
switch (type) {
- case IS_STRING:
- ce = Z_OBJCE_P(readobj);
-
- // __toString がユーザーのクラスで定義されている場合、
- if (ce->__tostring &&
- ) (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(例外))) {
- ……
-
- }
- return FAILURE;
- ……
- }
- return FAILURE;
- }
-
-
-
コードをコピー
上記の特定の実装から、デフォルトの Cast_object はクラス内で __tostring メソッドを見つけて呼び出します...
最初の例、define('foo', $a) に戻ります。$a は A のインスタンスであり、__toString はクラス A で定義されているため、foo 定数は実際には toString の戻り値 bar と等しくなります。
追記: 細かい詳細については引き続き調べてください。
1、定義には戻り値があります
通常、定数は次のように直接定義します: define('foo', 123); ただし、define の実装から判断すると、戻り値があります。マニュアルの説明によると:
成功した場合は TRUE を返し、失敗した場合は FALSE を返します。
失敗とはどのような状況で定義されますか?
例えば:
define('PHP_INT_MAX', 1); // FALSEを返します
define('FOO', 1); // を返します
- define(' FOO', 2); // Return FALSE
-
-
-
-
コードをコピー
上のコードには 2 つの状況が含まれています。1 つは、PHP_INT_MAX などの PHP カーネルの事前定義定数を再定義しようとする場合です。 、明らかに失敗します。 2 番目のケースは、コード内のどこかで定数 FOO を定義し、次のプログラムで再度定義する場合で、これも失敗の原因になります。したがって、名前の重複を避けるために、コーディング時に定義する必要があるすべての定数をまとめて記述することが最善です。
2、定数名に制限はありません
名前が XXX::YYY の形式であるかどうかのみを決定する、define の実装をもう一度確認してみましょう。
言い換えれば、define にはその名前に対する要件がほとんどなく、もちろん、名前が正当な PHP 変数名である必要もありません。したがって、define の定数に奇妙な名前を付けることができます。例えば:
define('>_<', 123); // Return TRUE echo >_<; // 構文エラー- しかし、そのような定数が定義されている場合、 is を直接使用できない場合は、構文エラーが報告されます。正しい使用法は次のとおりです。
define('>_<', 123); // TRUEecho constant('>_<') // 出力 123
|