이 글에서는 구조부터 시작하여 PHP 소스 코드를 사용하여 정적 변수, 상수, 매직 상수를 분석합니다.
1. 정적변수
우리 모두는 PHP 스크립트가 로드될 때 정적 변수가 로드된다는 것을 알고 있습니다. 즉, 1. 객체를 새로 만들지 않고도 직접 호출할 수 있습니다. 2. 그리고 정적 변수는 공용 영역에 저장됩니다. 동일한 클래스의 여러 개체가 함께 작동하여 정적 변수를 작동합니다. 3. 정적 변수의 메모리는 스크립트가 끝난 후에만 해제됩니다. 물어보세요, 왜요?
다음은 설명입니다
더 나은 분석과 이해를 위해 먼저 구조를 살펴보겠습니다.
정적 변수는 함수 구조 _zend_execute_data에 저장됩니다.
이 구조에는 op_array와 Symbol_table
이라는 두 가지 핵심 구조가 있습니다. 🎜>1.*symbol_table은 이 클래스에 다양한 변수를 저장합니다. 새 개체가 생성될 때마다 새 환경 공간이 열립니다. 자세한 내용은 PHP 커널--PHP Soul HashTble에 대한 간략한 설명을 참조하세요. 예제 2,
2. 함수의 컴파일된 opcode는 *op_array 구조에 저장됩니다. 공간 논리를 공유하여 환경 공간을 독립적으로 열지 않습니다. [매우 중요, 정적 근본 원인 달성]
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; };
Zend/zend_compiles.h 261줄, op_array 구조 코드
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번 호출하여 열린 심볼 테이블 [3부]
[t_3 실행_데이터] ---->[symbol_table_3]
[t_2 실행_데이터] ---->[symbol_table_2]
[t_1 실행_데이터] ---->[symbol_table_1]
*op_array->*정적 변수 테이블 [사본 1개]
결론:
类的变量是存储在 *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에는 compile_global 데이터와 excutor_global 데이터를 각각 얻는 두 개의 매크로 CG와 EG가 있습니다. 각각 고유한 function_table과 class_table을 가집니다.
게다가 php에서는 require가 함수로 실행되기 때문에 이때 EG와 CG간의 변환 방법을 알아두셔야 합니다)
위 내용은 정적 변수, 상수, 매직 상수의 원리에 대한 PHP 커널 상세 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!