$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版本开始支持函数参数,低版本不支持.是核心,请参阅:
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 문의 성능 분석에 더 익숙해질 것이라고 믿습니다.