$TOC$
#### Ein paar Worte
Ursprünglich wurde diese Frage auf Oschina gestellt:
Aber so war es noch nie Ich habe akzeptiert, dass ich eine passende Antwort gefunden habe, also habe ich hart daran gearbeitet, sie selbst zu klären. Wenn es Fehler gibt, teilen Sie diese bitte mit.
Übliche Funktionen werden durch Makrodefinitionen wie ZEND_FUNCTION(xxx) implementiert. Diese Spezifikation ist leicht zu verstehen und der Quellcode ist leicht zu lesen.
Aber die Verarbeitung von empty() und isset() ist etwas Besonderes, ähnlich wie echo, eval usw.
#### Vorbereitungsarbeiten
Erweitertes VLD zum Anzeigen von PHP-Opcode, Download:
PHP-Quellcode, Zweig => remotes/origin / PHP-5.6.14
git clone http://git.php.net/repository/php-src.git -b PHP-5.6.14
PHP-Opcode entsprechende Referenz:
> Die PHP-Executor-Version ist 5.6.14, andere Versionen des Opcodes können geringfügige Unterschiede aufweisen.
PHP-Kernel-Quellcode-Analyse:
#### Analyse starten
Beispielcode vld.php:
php $a = 0;
empty($a);
isset($a);
Opcode über vld anzeigen, `php -d vld.active=1 vld.php `
Anzahl der Operationen: 10
kompilierte Variablen: !0 = $a
Zeile #* E I O op fetch ext return operands
------------ - ------------------------------------------------- - ----------------------
2 0 E > EXT_STMT
1 ASSIGN EXT_STMT
3 ISSET_ISEMPTY_VAR 293601280 ~ 1!
4 Frei ~ 1
4 5 EXT_STMT
6 isset_isempty_var 310378496 ~ 2! 0
7 frei ~ 2
6 8 EXT_STMT
9> Rückgabe 1
Zweig: # 0; Zeile: 2- 6; sop: 9;
Beim Ausführen des PHP-Quellcodes wird zuerst die Syntaxanalyse durchgeführt. Der Yacc von empty und isset lautet wie folgt:
vim Zend/zend_lingual_parser.y 1265
1265 internal_functions_in_yacc :
1266 › › T_ISSET '(' isset_variables ')' { $$ = $3; }
1267 › |› T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC ); }
1275
1276 isset_variables:
1277 › › isset_variable› › › { $$ = $1; }
1280
1281 isset_variable:
1282 › › Variable› › › › { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
Zend_do_isset_or_isempty , t_or_isempty(int type, znode *result, 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(variable, BP_VAR_IS, 0 TSRMLS_CC);
6292
6293 › if (zend_is_function_or_method_call(variable)) {
6294 › › if (type == ZEND_ISEMPTY) {
6295 › › › /* empty(func()) kann umgewandelt werden in !func() */
6296 › › › zend_do_unary_op(ZEND_BOOL_NOT, result, variable TSRMLS_CC);
6297 › › } else {
6298 › › › zend_error_noreturn(E_COMPILE_ERROR, „Isset() kann nicht für das Ergebnis eines Funktionsaufrufs verwendet werden (Sie können stattdessen „null !== func()“ verwenden)“) ;
6299 › › }
6300
6301 › › zurückgeben;
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 出来了,IS_CV. 判断参数是否为变量able), 当isset(fun($a)), 函数参数写法会报错, empty 5.5 版本开始支持函数参数,低版本不支持。
opcode 是由 zend_execute. 最终会对应处理函数的查找,这个是核心,请参阅:
opcode 对应处理函数的命名规律:
变量类型1和变量类型2是可选的Sie können dies auch tun, indem Sie VAR TMP CV UNUSED CONST verwenden定。
所以 ZEND_ISSET_ISEMPTY_VAR 对应的handler如下:
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,
Zend/zend_v m_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_ ISSET_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, 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 › › }
Das obige if ... dient dazu, zu beurteilen, ob es isset oder leer ist, und dann eine unterschiedliche Verarbeitung durchzuführen. Z_TYPE_PP, i_zend_is_true sind unterschiedliche Beurteilungen.
Das Echo und andere Verarbeitungen sind ähnlich, Sie können sie entsprechend dem Prozess im Detail analysieren. Der Schlüssel besteht darin, die entsprechende Handler-Verarbeitungsfunktion gemäß der Zuordnungstabelle zu finden.
Nachdem Sie diese Verarbeitungsabläufe verstanden haben, werden Sie meiner Meinung nach mit der Leistungsanalyse von PHP-Anweisungen besser vertraut sein.