PHP 커널에서 빈 isset 함수를 구현하는 방법

巴扎黑
풀어 주다: 2016-11-08 09:37:15
원래의
1271명이 탐색했습니다.

$TOC$

#### 몇 마디

원래 이 질문은 oschina에서 제기되었습니다:


그러나 그런 적이 없습니다. 수락 적절한 답변을 찾았으므로 직접 정리하려고 열심히 노력했습니다. 잘못된 부분이 있으면 공유해 주세요.

일반적인 함수는 ZEND_FUNCTION(xxx)과 같은 매크로 정의를 통해 구현됩니다. 이 사양은 이해하기 쉽고 소스 코드도 읽기 쉽습니다.

그러나empty() 및 isset()의 처리는 echo, eval 등과 유사하게 매우 특별합니다.

#### 준비

PHP opcode를 보기 위한 확장 vld, 다운로드:


PHP 소스 코드, 분기 => 원격/원본 /PHP -5.6.14

git clone http://git.php.net/repository/php-src.git -b PHP-5.6.14

PHP opcode 해당 참조:


> PHP 실행기 버전은 5.6.14이며, 다른 opcode 버전은 약간의 차이가 있을 수 있습니다.

PHP 커널 소스 코드 분석:


#### 분석 시작

샘플 코드 vld.php:

php $a = 0;
empty($a);
isset($a);

vld를 통해 opcode 보기, `php -d vld.active=1 vld.php `

작업 수: 10
컴파일된 변수: !0 = $a
line #* E I O op fetch ext return 피연산자
------------ - ------------------------------------------------- - -----------------------
2 0 E > EXT_STMT
1 ASSIGN                          EXT_STMT
3 ISSET_ISEMPTY_VAR 293601280 ~ 0 4 무료 ~ 1
4 5 EXT_STMT
6 isset_isempty_var 310378496 ~ 2! 0
7 무료 ~ 2
6 8 EXT_STMT
9> 1 반환

분기: # 0; 라인: 2-6; op: 9;

PHP 소스 코드 실행 시 구문 분석이 먼저 수행됩니다. 공백 및 isset의 yacc는 다음과 같습니다.

vim Zend/zend_언어_parser.y +1265

1265 Internal_functions_in_yacc:
1266 › › T_ISSET '(' isset_variables ')' { $$ = $3; }
1267 › |> T_EMPTY '(' 변수 ')'> { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC) }
1275
1276 isset_variables: 
1277 ›   ›   isset_variable> ›   ›   { $$ = $1; } 
1280 
1281 isset_variable: 
1282 ›   ›   변수">  ›   ›   ›   { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); } 

最终city执行了zend_do_isset_or_isempty,继续查找: 

git grep -in "zend_do_isset_or_isempty" 
Zend/zend_compile.c:6287:void zend_do_isset_or_is 비어 있음(int 유형, znode *결과, znode *variable TSRMLS_DC) /* {:{:{ */ 

vi Zend/zend_compile.c +6287 

6287 void zend_do_isset_or_isempty(int type, znode *result, znode *variable TSRMLS_DC) /* {{{ */ 
6288 { 
6289 ›   zend_op *last_op; 
6290 
6291 ›   zend_do_end_variable_parse(변수, BP_VAR_IS, 0 TSRMLS_CC); 
6292 
6293 ›   if (zend_is_function_or_method_call(variable)) { 
6294 ›   ›   if (type == ZEND_ISEMPTY) { 
6295 ›   ›   ›   /*empty(func())는 다음으로 변환될 수 있습니다. !func() */ 
6296 ›   ›   ›   zend_do_unary_op(ZEND_BOOL_NOT, 결과, 변수 TSRMLS_CC); 
6297 ›   ›   } else { 
6298 ›   ›   ›   zend_error_noreturn(E_COMPILE_ERROR, "함수 호출 결과에 isset()을 사용할 수 없습니다(대신 "null !== func()"을 사용할 수 있음)") ; 
6299 ›   ›   } 
6300 
6301 ›   ›   반품; 
6302 ›   } 
6303 
6304 ›   if (variable->op_type == IS_CV) { 
6305 ›   ›   last_op = get_next_op(CG(active_op_array) TSRMLS_CC); 
6306 ›   ›   last_op->opcode = ZEND_ISSET_ISEMPTY_VAR; 

最后一行 6306,ZEND_ISSET_ISEMPTY_VAR 这个opcode ude来了,IS_CV 判断参数是否为变weight。 
注意zend_is_function_or_method_call(variable),当isset(fun ($a)) ,函数参数写法会报错,비어있습니다 5.5版本开始支持函数参数,低版本不支持.是核心,请参阅:  

 
opcode 对应处理函数的命name规律: 

ZEND_[opcode]_SPEC_(变weight类型1)_(变weight类型2)_HANDLER 

变weight类型1화变수량 2개 ,如果同时存 ,那就是左值 and 右值 ,归纳有下几类: VAR TMP CV UNUSED CONST 这样可以根据来关的执行场景来判定。

所以 ZEND_ISSET_ISEMPTY_VAR 对应의 핸들러 보기: 

Zend/zend_vm_execute.h:44233:   ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_CONST_HANDLER, 
Zend/zend_vm_execute.h:44235:   ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_VAR_HANDLER, vm_execute.h:44236:   ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED_HANDLER, 
Zend/zend_vm_execute.h:44238: ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_CONST_HANDLER, 
Zend/zend_vm_execute.h:44240:   ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_VAR_HANDLER, 
Zend/zend_vm_execute.h:44241:   ZEND_ISSET_ISEMPTY_VAR _SPEC_TMP_UNUSED_HANDLER, 
Zend/zend_vm_execute.h:44243:   ZEND_ISSET_ISEMPTY_VAR_SPEC_VAR_CONST_HANDLER, 
Zend/zend_vm_execute.h: 44245:   ZEND_ISSET_ISEMPTY_VAR_SPEC_VAR_VAR_HANDLER, 
Zend/zend_vm_execute.h:44246:   ZEND_ISSET_ISEMPTY_VAR_SPEC_VAR_UNUSED_HANDLER, 
Zend/zend_vm_execute.h:44253:   ZEND_IS SET_ISEMPTY_VAR_SPEC_CV_CONST_HANDLER, 
Zend/zend_vm_execute.h:44255:   ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_VAR_HANDLER, 
Zend/zend_vm_execute. h:44256:   ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_UNUSED_HANDLER, 

저는 ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_VAR_HANDLER 这个处理函数: 
vim Zend/zend_vm_execute. h +37946 

38013 ›   if (opline->extended_value & ZEND_ISSET) { 
38014 ›   ›   if (isset && Z_TYPE_PP(value) != IS_NULL) { 
38015 ›   ›   ›   ZVAL_BOOL(&EX_T(opline->result.var).tmp_var, 1); 
38016 ›   ›   } else { 
38017 ›   ›   ›   ZVAL_BOOL(&EX_T(opline->result.var).tmp_var, 0); 
38018 ›   ›   } 
38019 ›   } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { 
38020 ›   ›   if (!isset || !i_zend_is_true(*value)) { 
38021 › › › ZVAL_BOOL(&EX_T(opline->result.var).tmp_var, 1);
38022 › › } else {
38023 › › ZVAL_BOOL(&EX_T(opline->result.var).tmp_var , 0);
38024 › › }

위의 if ... else는 isset인지 비어 있는지 판단한 후 다른 처리를 하는 것이며, Z_TYPE_PP, i_zend_is_true는 다른 판단입니다.
에코와 기타 처리는 유사하며, 처리에 따라 자세히 분석할 수 있습니다. 핵심은 매핑 테이블에 따라 해당 핸들러 처리 기능을 찾는 것입니다.

이러한 처리 흐름을 이해하고 나면 PHP 문의 성능 분석에 더 익숙해질 것이라고 믿습니다.

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿