php define的第二個參數使用方法_PHP教程

來源:互聯網
上載者:User
看手冊說define定義的常量只允許:
僅允許標量和 null。標量的類型是 integer, float,string 或者 boolean。 也能夠定義常量值的類型為 resource ,但並不推薦這麼做,可能會導致未知狀況的發生。
今天閱讀php源碼,發現define的第二個參數其實也可以是一個對象。
先貼一段樣本:
複製代碼 代碼如下:
class A {
public function __toString() {
return 'bar';
}
}

$a = new A();
define('foo', $a);
echo foo;
// 輸出bar

接著來看看php中的define究竟是如何?的:
複製代碼 代碼如下:
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;
}

// 如果define類常量,則報錯
if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) {
zend_error(E_WARNING, "Class constants cannot be defined or redefined");
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);
goto repeat;
} 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;
break;
}
}
}
/* no break */
default:
zend_error(E_WARNING,"Constants may only evaluate to scalar values");
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; /* non persistent */
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,則實際定義的常量時直接使用這些值
對於object,則需要將object轉成上述6個類型之一(如果轉型之後依然是object,則繼續轉型)
如何將object成6個類型之一呢?從代碼上看有2種手段:
複製代碼 代碼如下:
if (Z_OBJ_HT_P(val)->get) {
val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
goto repeat;
}
// __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;
break;
}
}

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 int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) /* {{{ */
{
zval *retval;
zend_class_entry *ce;

switch (type) {
case IS_STRING:
ce = Z_OBJCE_P(readobj);

// 如果使用者的class中定義了__toString,則嘗試調用
if (ce->__tostring &&
(zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
……

}
return FAILURE;
……
}
return FAILURE;
}

從上述具體實現來看,預設的cast_object就是去尋找class中的__tostring方法然後調用…
回到剛開始的例子,define(‘foo', $a) ,由於$a是A的執行個體,並且class A中定義了__toString,因此實際上foo常量就等於toString的傳回值bar。

http://www.bkjia.com/PHPjc/825008.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/825008.htmlTechArticle看手冊說define定義的常量只允許: 僅允許標量和 null。標量的類型是 integer, float,string 或者 boolean。 也能夠定義常量值的類型為 resource ,...

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.