PHP常量的實現和操作

| Comments

存儲結構

常量存儲在哈希表EG(zend_constants)中。

常量的結構定義為:

1
2
3
4
5
6
7
typedef struct _zend_constant {
  zval value;
  int flags;
  char *name;
  uint name_len;
  int module_number;
} zend_constant;

value是常量的值,是一個zval。name是常量名。module_number是模塊被加載時,PHP內核在MINIT和RINIT方法的原型裡默認傳遞的一個值,作為模塊清理時的線索,在註冊常量的接口裡直接傳遞即可。

flags是常量的標識或標識組合:

  • CONST_CS
  • CONST_PERSISTENT
  • CONST_CT_SUBST

CONST_CS表示常量名對大小寫敏感,對應PHP函數define()的第三個參數,TRUE、FALSE、NULL這些常量名對大小寫是不敏感的。CONST_PERSISTENT表示常量在請求結束後被保存,只在PHP進程結束時才銷毀,一般在MINIT中定義的常量應該指定此參數,RINIT中定義的不指定。CONST_CT_SUBST表示在編譯時可替換,TRUE、FALSE、NULL、ZEND_THREAD_SAFE、ZEND_DEBUG_BUILD屬於此類。

常量的聲明

常量的聲明方法有兩種,簡單的使用宏函數族REGISTER_*_CONSTANT():

REGISTER_NULL_CONSTANT(name, flags) REGISTER_BOOL_CONSTANT(name, bval, flags) REGISTER_LONG_CONSTANT(name, lval, flags) REGISTER_DOUBLE_CONSTANT(name, dval, flags) REGISTER_STRING_CONSTANT(name, str, flags) REGISTER_STRINGL_CONSTANT(name, str, len, flags)

由於不需指定常量名長度,所以name參數應直接使用字符串,而不是char*。

如需使用變量作為name參數,使用zend_register_*_constant()函數族,並指定變量名長度(sizeof(name))。上面的宏函數其實是對這族函數的封裝。

void zend_register_long_constant(char *name, uint name_len, long lval, int flags, int module_number TSRMLS_DC) void zend_register_double_constant(char *name, uint name_len, double dval, int flags, int module_number TSRMLS_DC) void zend_register_bool_constant(const char *name, uint name_len, zend_bool bval, int flags, int module_number TSRMLS_DC) void zend_register_string_constant(char *name, uint name_len, char *strval, int flags, int module_number TSRMLS_DC) void zend_register_stringl_constant(char *name, uint name_len, char *strval, uint strlen, int flags, int module_number TSRMLS_DC)

除此之外,還有REGISTER_MAIN_*_CONSTANT和REGISTER_NS_*_CONSTANT兩組宏函數。前者用於定義像E_ERROR這樣的PHP標準常量,後者定義有命令空間的常量。

define()和const

  • define()是函數,在運行時定義常量
    • 不能定義類常量
    • 可以在條件語句中使用
    • 可以指定常量是否對大小寫敏感
    • 可以用表達式作為常量值
    • 只定義全局常量,不支持命名空間
  • const是語句,在編譯時定義常量
    • 可以定義類常量
    • 不能在條件語句中使用
    • 定義的常量對大小寫敏感
    • 不支持表達式作為常量值
    • 若腳本定義了命名空間,聲明的常量屬於該命名空間

魔術常量

__LINE__
__FILE__
__DIR__
__FUNCTION__
__CLASS__
__METHOD__
__NAMESPACE__

魔術常量是在編譯時(具體地說是詞法分析時,見Zend/zend_language_scanner.l)被替換,確切地說,這些不是真正意義上的常量,只是個模板佔位符。

Comments