ホームページ  >  記事  >  バックエンド開発  >  PHP カーネルで空の isset 関数を実装する方法

PHP カーネルで空の isset 関数を実装する方法

巴扎黑
巴扎黑オリジナル
2016-11-08 09:37:151217ブラウズ

$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: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/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 ステートメントのパフォーマンス分析にさらに詳しくなると思います。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。