本篇透過PHP源碼,從結構入手來對靜態變量,常量,魔術常數進行分析。
1.靜態變數
我們都知道,靜態變數是在PHP腳本載入時就載入了,即1.不用new其物件就可以直接調用,2.且靜態變數儲存在公共區域同一類別的多個物件共同操作一個靜態變量,3.靜態變數只有在腳本結束後記憶體才會釋放,針對這三個問題,想問一句,為什麼?
下邊展開敘述 先看其結構,更好進行分析了解。靜態變數是儲存在函數結構體_zend_execute_data 中的,
而這個結構體中,有兩個很關鍵的結構體,op_array和symbol_table
#1.*symbol_table 存放這類裡邊的各種變數等,每次new物件時,都會開啟新的環境空間,詳見PHP核心--淺談PHP靈魂HashTble事例二,
2.函數編譯後的opcode儲存在*op_array;結構中,儲存的也就是這個函數的邏輯,每次new物件時,公用一個空間邏輯,不會自己在獨立開啟環境空間【很關鍵,實作靜態根本原因】
Zend/zend_compiles.h 384行,執行環境結構體
struct _zend_execute_data {
struct _zend_op *opline;
zend_function_state function_state;
zend_op_array *op_array;//!!!!!函数编译后的执行逻辑,编译后的opcode二进制代码,称为op_array,公用一个逻辑
zval *object;
HashTable *symbol_table;//!!!!!此函数的符号表地址,每次new会开辟一个新的空间《---------
struct _zend_execute_data *prev_execute_data;
zval *old_error_reporting;
zend_bool nested;
zval **original_return_value;
zend_class_entry *current_scope;
zend_class_entry *current_called_scope;
zval *current_this;
struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
call_slot *call_slots;
call_slot *call;
};
struct _zend_op_array { /* Common elements */ zend_uchar type; ... /* static variables support */ HashTable *static_variables;//294行 ,静态变量 ... }
範例:
t1() { $a +=1 ; static $b +=1; t1(); t1(); } //加自身共调用3次
結果$a每回都是1,而$b = 1,2,3
原因如下:
三次呼叫函數開啟的符號表【3份】
[t_3 execute_data] ---->[symbol_table_3]
[t_2 execute_data] ---->[symbol_table_2]
# [t_1 execute_data] ---->[symbol_table_1]
*op_array->*靜態變數表【一份】
##結論:
##############################类的变量是存储在 *symbol_table中的,每个都有其作用域,每次实例化都会在开辟一个环境空间(详见Hashtable第二部分的举例);而静态变量不同,如代码所示,它存储在op_array里边,op_array是什么,编译生成的opcode代码,存储的是函数的逻辑,不管new多少个对象,这个逻辑是公用的,而静态变量也存储在这个结构中,所以实现了同一类的不同对象可以公用一个静态变量,也解释了在PHP层面,静态变量为什么不用new就直接调用。解释了问题一二,
因为静态变量存储在op_array里边,op_array是在脚本执行结束后释放,所以其也在这个时候释放.,解释问题三。
2.常量
首先看下常量与变量的区别,常量是在变量的zval结构的基础上添加了一额外的元素。如下所示为PHP中常量的内部结构。
常量的结构 (Zend/zend_constants.h文件的33行)
typedef struct _zend_constant { zval value; /* zval结构,PHP内部变量的存储结构 */ char *name; /* 常量名称 */ uint name_len; int flags; /* 常量的标记如 CONST_PERSISTENT | CONST_CS */ int module_number; /* 模块号 */ } zend_constant;
结构体如上,name,name_len一目了然,值得一提的是zval与变量中存储的zval结构一模一样,(详见PHP内核的存储机制(分离/改变))
主要解释下flag与module_number
1.flags:
c.flags = case_sensitive / case insensitive ; // 1,0
赋值给结构体字段是否开启大小写敏感
2.module_number:
1.PHP_USER_CONSTANT:用户定义的常量
(define函数定义的常量的模块编号都是)
2.REGISTER_MAIN_LONG_CONSTANT:PHP内置定义常量
比如错误报告级别E_ALL, E_WARNING,PHP_VERSION等常量,都是持久化常量,最后才销毁
3.魔术常量
说是常量,其实每次值在不同位置,可能是不相同的,原因是为什么呢?
PHP内核会在词法解析时将这些常量的内容赋值进行替换,而不是在运行时进行分析。 如下PHP代码:
以__FUNCTION__为例, 在Zend/zend_language_scanner.l文件中,__FUNCTION__是一个需要分析的元标记(token):
就是这里,当当前中间代码处于一个函数中时,则将当前函数名赋值给zendlval(也就是token T_FUNC_C的值内容), 如果没有,则将空字符串赋值给zendlval(因此在顶级作用域名中直接打印__FUNCTION__会输出空格)。 这个值在语法解析时会直接赋值给返回值。这样我们就在生成的中间代码中看到了这些常量的位置都已经赋值好了。
只要記住,上邊程式碼實現的功能,將__FUNCTION__在詞法分析時,轉換成當時對應的值。
(php中有CG和EG兩個宏,分別取得compile_global資料和excutor_global的數據,它們分別有各自的function_table和class_table,
另外php中的require是作為函數來執行的,因此這個時候需要知道EG和CG之間是如何轉換的。
以上是PHP核心-靜態變數,常數,魔術常數原理的具體介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!