存储结构

常量存储在哈希表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)被替换,确切地说,这些不是真正意义上的常量,只是个模板占位符。