首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP define 第二參數可接受物件(含 __toString 魔術方法)

     編輯:PHP 綜合知識

文檔與實際行為差異

官方文檔宣稱 define 僅接受標量與 null,但原始碼實作支援實作 __toString 的物件。

實例驗證

<?php
class A {
    public function __toString() {
        return 'bar';
    }
}
$a = new A();
define('foo', $a);
echo foo; // 輸出 bar
?>

原始碼分析(ZEND_FUNCTION(define))

關鍵邏輯在 switch (Z_TYPE_P(val)) 中的 IS_OBJECT 分支:

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");
    // ... 返回 FALSE

轉換機制

  1. 優先嘗試 get handler(取得物件值)
  2. 否則嘗試 cast_object handler 轉為字串
  3. 預設 cast_object 為 zend_std_cast_object_tostring,會呼叫 __toString
  4. 轉換成功後視為字串常量值

標準 cast_object 實作

ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type TSRMLS_DC) {
    switch (type) {
        case IS_STRING:
            ce = Z_OBJCE_P(readobj);
            if (ce->__tostring &&
                (zend_call_method_with_0_params(&readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
                // 成功取得字串
            }
            return FAILURE;
    }
    return FAILURE;
}

結論

  • define('CONST', $obj) 實際儲存的是 $obj->__toString() 的返回值
  • 此行為屬於實作細節,非官方承諾 API,生產環境建議僅傳入標量
  • 理解此機制有助於除錯異常常量定義行為
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved