看手冊說define定義的常量只允許:
僅允許標量和 null。標量的類型是 integer, float,string 或者 boolean。 也能夠定義常量值的類型為 resource ,但並不推薦這麼做,可能會導致未知狀況的發生。
今天閱讀php源碼,發現define的第二個參數其實也可以是一個對象。
先貼一段示例:
'bar' = ('foo', foo;
// 輸出bar
接著來看看php中的define究竟是如何實現的:
***val_free == case_sensitive = (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, , &name, &name_len, &val, &non_cs) == = (zend_memnstr(name, , () - , name + (! (Z_OBJ_HT_P(val)->= val = Z_OBJ_HT_P(val)-> (Z_OBJ_HT_P(val)-> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) === & c.value = *&&= case_sensitive; == name_len+= (zend_register_constant(&c TSRMLS_CC) ==
注意以repeat開始的一段循環,還用到了goto語句T_T
這段代碼的作用為:
如何將object成6個類型之一呢?從代碼上看有2種手段:
(Z_OBJ_HT_P(val)->= val = Z_OBJ_HT_P(val)->// __toString()方法會在cast_object中被調用 (Z_OBJ_HT_P(val)-> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ===
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 。該結構體中的函數指針均用於操作object,比如讀取/修改對象屬性、獲取/調用對象方法等等...get和cast_object也是其中之一。
對於一般的對象,php提供了標准的cast_object函數zend_std_cast_object_tostring,代碼位於php-src/zend/zend-object-handlers.c中:
ZEND_API zend_std_cast_object_tostring(zval *readobj, zval *writeobj, type TSRMLS_DC) **= (ce->__tostring &&&readobj, ce, &ce->__tostring, , &retval) ||
從上述具體實現來看,默認的cast_object就是去尋找class中的__tostring方法然後調用...
回到剛開始的例子,('foo',
('PHP_INT_MAX', 1); ('FOO', 1); ('FOO', 2);
上面代碼包含了兩種情況,一是我們嘗試重新定義php內核的預定義常量,比如PHP_INT_MAX,這顯然會失敗。第二種情況是我們曾經在代碼的某個位置定義過了一個常量FOO,然後又在接下來的程序中再次定義它,這也會造成失敗。因此,在編碼時最好將所有需要定義的常量寫在一起,以免造成name重復。
對其name不做任何要求,當然也不需要name是一個合法的php變量名。因此,我們可以讓define的常量取一些稀奇古怪的名稱。例如:
('>_<', 123); >_<;
不過如果定義了這樣的常量,是沒法直接使用的,會報語法錯誤。正確的使用方法如下:
('>_<', 123); ('>_<');