標籤:php原理 zend
我們先看一個php常量的定義方法
define(‘PRICE‘, 30);
之前,我一直把define和C中的宏定義理解一致,因此在使用的時候也只是將其當成簡單地字元替換。後來研究了PHP核心以後,發現PHP中的常量和宏定義完全不是一回事。
在PHP指令碼啟動並執行過程中,zend引擎會維護一個常量列表,對於普通使用者來說,可以對這個常量列表進行CRUD操作,api分別為
define():定義一個常量
defined(): 判斷一個常量是否存在
constant(): 得到一個常量的值
我們來看下PHP核心中常量的定義
typedef struct _zend_constant{zval value;int flags;char *name;uint name_len;int module_number;} _zend_constant;
其中value為普通的變數結構zval,在此基礎上,常量還定義了標記,常量名和模組號三個屬性。
1. 標記(flags)
首先看常量的標記屬性flag,目前可供選擇的幾個可能值分別為
使用者態(可通過define方法的第三個參數賦值):
1: case sensitive
0: case insensitive
核心態:
CONST_PERSISTANT: persistent
CONST_CT_SUBST: allow compile-time substitution
使用者定義的常量的標記預設為case sensitive, 也可通過define函數的第三個參數進行修改。對於核心態標記,CONST_PERSISTANT代表這個常量在記憶體申請的時候需要持久化。
是PHP指令碼啟動並執行的生命週期,我們知道多個request共用一次MINIT和MSHUTDOWN過程,而每個request有自己的RINIT和RSHUTDOWN過程,因此在MINIT中初始化的變數會常駐記憶體當中。這樣被標記為CONST_PERSISTANT的常量只會在MSHUTDOWN中才會被析構掉。這也就不難理解,在核心C代碼中,一些字串和數字作為代碼的一部分也被定義為PHP核心中的常量,它們通常就會被標記為CONST_PERSISTENT常量。
而CONST_CT_SUBST目前在核心裡只有5個(TRUE, FALSE, NULL, ZEND_THREAD_SAFE, ZEND_DEBUG_BUILD),
2.模組號(module_number)
模組號同樣分為使用者態和核心態,確切的說,模組號就是用來做此區分的。使用者定義的常量均為PHP_USER_CONSTANT, 除此以外,還有一些PHP內建的標準常量,例如E_ALL, E_WARNING等。在zend引擎啟動以後,zend會進行標準常量的註冊工作(zend_register_standard_constants()),一般來說,這些標準常量都被標記為持久化常量,即CONST_PERSISTENT
3. define()函數
define是PHP的內建介面,使用者會通過define來定義常量,其實該方法過程如下
4. 魔術常量
PHP中還提供了一種魔術常量,他們的值是隨著外部環境的變化而變化的,例如
__LINE__ 當前檔案的行號
__FILE__ 檔案的完整路徑
這些魔術常量不是真正的常量(_zend_constants),PHP核心在此法解析的時候就會將其替換掉。
最後附上define的源碼(在Zend/zend_builtin_functions.c中):
/* {{{ proto bool define(string constant_name, mixed value, boolean case_insensitive=false) Define a new constant */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;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;}/* class constant, check if there is name and make sure class is valid & exists */if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) {zend_error(E_WARNING, "Class constants cannot be defined or redefined");RETURN_FALSE;}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 = IS_INTERNED(name) ? name : zend_strndup(name, name_len);if(c.name == NULL) {RETURN_FALSE;}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;}}/* }}} */