仕方がないので、PHP の字句解析、構文解析、オペコードの生成、実行、プロセス全体に至るまで、グローバル キーワードの実装を詳細に体系的に分析しました。
スクリプトに記述する場合:
$var = "ラルエンス";
関数サンプル(){
グローバル $var;
}
?>
、PHP が関数スコープ内のグローバル変数をどのように見つけるかご存知ですか?
前回の記事 (PHP 原則の深い理解: オペコード) で述べたように、PHP の実行は次の段階を経ます。
1.スキャン(Lexing)、PHPコードを言語フラグメント(トークン)に変換します
2. トークンを解析し、単純で意味のある表現に変換する
3. コンパイル、式を Opocdes にコンパイルします
4. 実行では、オペコードを一度に1つずつ順番に実行することで、PHPスクリプトの機能を実現します。
次に、最初の段階は当然のことながら、字句解析段階での
のスキャンです。
グローバル $var;
次のように解析されます:
T_GLOBAL 変数;
次は解析段階です:
T_GLOBAL 変数;
Yacc の受け渡しルール:
ステートメント:
T_GLOBAL global_var_list ';'
....
global_var_list:
global_var_list ',' global_var { zend_do_fetch_global_variable(&$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC) }
global_var { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC) }
;
このうち、zend_do_fetch_global_variable は実際にオペコードを生成する関数です:
zend_op *opline;
……
opline->opcode = ZEND_FETCH_W;
opline->result.op_type = IS_VAR;
……
opline->op2.u.EA.type = ZEND_FETCH_GLOBAL_LOCK;
ZEND_FETCH_W の op_handler は次のとおりです:
ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONSTTMPVARCV, ANY) { ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, タイプ, BP_VAR_W) }
zend_fetch_var_adress_helper を見てみましょう:
..... target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC);/* if (!target_symbol_table) { ZEND_VM_NEXT_OPCODE() }*/ if (zend_hash_find(target_symbol_table, varname->value.str) .val, varname->value.str.len+1, (void **) &retval) == FAILURE) { switch (type) { case BP_VAR_R: case BP_VAR_UNSET: zend_error(E_NOTICE,"未定義の変数: %s", Z_STRVAL_P(varname)); /* ブレークが意図的に欠落しています */ case BP_VAR_IS: retval = &EG(uninitialized_zval_ptr) Break; case BP_VAR_RW: zend_error(E_NOTICE,"未定義の変数: %s", Z_STRVAL_P(varname));意図的に */ case BP_VAR_W: { zval *new_zval = &EG(uninitialized_zval); new_zval->refcount++(target_symbol_table, varname->value.str.val, varname->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval) } EMPTY_SWITCH_DEFAULT_CASE() } }
コアは zend_get_targer_symbol_table 関数であることがわかります:
static inline HashTable *zend_get_target_symbol_table(zend_op *opline, temp_variable *Ts, int type, zval *variable TSRMLS_DC){ switch (opline->op2.u.EA.type) { case ZEND_FETCH_LOCAL: return EG(active_symbol_table); ZEND_FETCH_GLOBAL: ケース ZEND_FETCH_GLOBAL_LOCK: &EG(シンボルテーブル) を返す; ケース ZEND_FETCH_STATIC: if (!EG(active_op_array)->static_variables) { ALLOC_HASHTABLE(EG(active_op_array)->static_variables); gt ; static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); } return EG(active_op_array)->static_variables() } return NULL;}
問題は明らかです。つまり、変数をグローバル化すると、Zend はグローバルのsymbol_table にアクセスしてそれを見つけ、対応する変数をグローバルのsymbol_table に割り当てます。
このメカニズムを通じて、グローバル変数が実装されます。