官方文件聲明 define() 僅接受標量與 null,但閱讀 PHP 核心原始碼發現:若物件實作 __toString() 方法,實際上可作為常數值定義。
<?php
class A {
public function __toString() {
return 'bar';
}
}
$a = new A();
define('FOO', $a);
echo FOO; // 輸出:bar
關鍵邏輯位於 Zend/zend_builtin_functions.c 的 ZEND_FUNCTION(define):
zend_parse_parameters("sz|b", ...),第二參數為 zval*,可接收任意型別。repeat: 標籤的 switch 判斷 Z_TYPE_P(val)。IS_OBJECT,嘗試透過物件處理器(handlers)轉換為標量:case IS_OBJECT:
if (!val_free) {
// 1. 嘗試呼叫 get handler(如 __get、Iterator 等)
if (Z_OBJ_HT_P(val)->get) {
val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
goto repeat;
}
// 2. 嘗試 cast_object(預設會呼叫 __toString)
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
zend_std_cast_object_tostring(Zend/zend_object_handlers.c)會檢查類別是否定義 __tostring,若有則呼叫並回傳字串;否則失敗。
(string)$obj 或 $obj->__toString() 後再 define。Constants may only evaluate to scalar values 警告,表示物件無法轉換為字串。