$TOC$
#### 一言
もともとこの質問は oschina で行われたものです:
しかし、適切な答えが得られなかったので、間違いがあれば自分で整理するために一生懸命努力しました。場所、コミュニケーションを歓迎します。
通常の関数はZEND_FUNCTION(xxx)などのマクロ定義で実装されており、この仕様は理解しやすく、ソースコードも読みやすいです。
しかし、empty() と isset() の処理はかなり特殊で、echo、eval などと似ています。
#### 準備
PHP オペコードを表示するための拡張 vld、ダウンロード:
PHP ソース コード、ブランチ => Remotes/origin/PHP-5.6.14
git clone http://git. net/repository/php-src.git -b PHP-5.6.14
PHP オペコード対応リファレンス:
> PHP executor のバージョンは 5.6.14 ですが、オペコードの他のバージョンには微妙な違いがある可能性があります。
PHP カーネル ソース コードの分析:
####
サンプル コード vld.php の分析を開始します:
$a = 0;
isset($a); ;
vld を通じてオペコードを表示、`php -d vld.active=1 vld.php`
オペの数: 10
コンパイルされた変数: !0 = $a
line #* E I O op ext return オペランド
--- -------------------------------------------------- --------------------------------
2 0 E > EXT_STMT
1 ASSIGN EXT_STMT
3 ISSET_ISEMPTY_VAR 293601280 ~1 !0 4 無料END_ISSET_ISEMPTY_VAR がオペコードに表示されます。段階的に分析してみましょう。
PHP ソースコードを実行すると、最初に構文解析が実行されます。empty と isset の yacc は次のとおりです。
vim Zend/zend_ language_parser.y +1265
1265 inner_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); }
最終终都执行了zend_do_isset_or_isempty,继续查找:
git grep -in "zend_do_isset_or_isempty"
Zend/zend_compile.c:6287:void zend_do_isset_or_isempty(int type, *結果、znode *変数 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() に変換できます */
6 296 › › › 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 このオペコードが完了しました、IS_CV パラメータが量であるかどうかを判断します。数値パラメータ、低バージョンはサポートされていません。
ZEND_[オペコード]_SPEC_(変化量タイプ 1)_(変化量タイプ 2)_HANDLER
変化量タイプ 1 と変化量タイプ 2 は選択可能です、同時に存在する場合は、左と右です。関連する実行状況に応じて判定されます。 _vm_execute.h:44235: ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_VAR_HANDLER、
Zend/zend_vm_execute.h:44238: ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_CONST_HANDLER、
Zend/zend_vm_execute.h:44240: Zend/zend_vm_exe Cute.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_I SEMPTY_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) {
38 014 › › if (isset && Z_TYPE_PP(値) != 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かemptyかを判定して別の処理をするもので、Z_TYPE_PP、i_zend_is_trueは別の判定になります。
エコーと他の処理は似ており、プロセスに応じて詳細に分析できます。重要なのは、マッピング テーブルに従って、対応するハンドラー処理関数を見つけることです。
これらの処理の流れを理解すると、PHP ステートメントのパフォーマンス分析にさらに詳しくなると思います。