php-srcのコードリーディングした内容をコツコツ残すテスト。その13
引き続きclass。今日はプロパティのデフォルト値, static method
<?php
class Hoge
{
public $hoge = 1;
public static function call() {
echo 123;
}
}
echo (new Hoge)->hoge;
echo Hoge::call();
opcode
$_main:
; (lines=8, args=0, vars=0, tmps=4)
; (before optimizer)
; /path/to/13.php:1-14
; return [] RANGE[0..0]
0000 V0 = NEW 0 string("Hoge")
0001 DO_FCALL
0002 T2 = FETCH_OBJ_R V0 string("hoge")
0003 ECHO T2
0004 INIT_STATIC_METHOD_CALL 0 string("Hoge") string("call")
0005 V3 = DO_UCALL
0006 ECHO V3
0007 RETURN int(1)
LIVE RANGES:
0: 0001 - 0002 (new)
Hoge::call:
; (lines=2, args=0, vars=0, tmps=0)
; (before optimizer)
; /path/to/13.php:7-9
; return [] RANGE[0..0]
0000 ECHO int(123)
0001 RETURN null
プロパティのデフォルト値
zend_compile_prop_decl()
は zend_const_expr_to_zval()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7352:L7352
zend_const_expr_to_zval()
は ZVAL_COPY
でresultにastのzvalをコピー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L9914
ce->default_properties_table
にデフォルトのプロパティを設定。offsetは property_info->offset
を利用。オブジェクトのプロパティ配置と同じになる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L4148-L4149
ZEND_NEW_SPEC_CONST_UNUSED_HANDLER
のハンドラで object_init_ex()
=> _object_and_properties_init()
=> _object_properties_init()
と呼び出していき、プロパティをセットしているところはこの辺
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L1509-L1527
Static Method Call
zend_compile_static_call()
でコンパイルされる
op1にクラス名、op2にメソッド名がセットされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L4616-L4625
あとは zend_compile_call_common()
などで引数や呼び出しのコンパイルを行う。
INIT_STATIC_METHOD_CALL
opcodeは ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER
でハンドリングされる。
まとめるとざっとこんな感じ
ce = zend_fetch_class_by_name(Z_STR_P(RT_CONSTANT(opline, opline->op1)), Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
function_name = RT_CONSTANT(opline, opline->op2);
fbc = zend_std_get_static_method(ce, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (RT_CONSTANT(opline, opline->op2) + 1) : NULL));
call = zend_vm_stack_push_call_frame(call_info,
fbc, opline->extended_value, ce);
call->prev_execute_data = EX(call);
EX(call) = call;
zend_class_entryを取得してstatic methodなzend_functionを取得して、zend_class_entryのコンテキストで関数を実行している。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_object_handlers.c#L1383:L1383
インスタンスメソッドなのにstatic関数呼び出しするとエラーにしている。
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
if (Z_TYPE(EX(This)) == IS_OBJECT && instanceof_function(Z_OBJCE(EX(This)), ce)) {
ce = (zend_class_entry*)Z_OBJ(EX(This));
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_HAS_THIS;
} else {
zend_non_static_method_call(fbc);
HANDLE_EXCEPTION();
}
インスタンスメソッドを呼び出す場合、EX(This)のタイプはIS_OBJECTかつEX(This)のオブジェクトは対象クラスの関数である必要がある、という分岐。
また、 staticコンテキストで $this->xxx
という感じで呼び出そうとするとエラーになる。 ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_HANDLER
にはこんな感じな処理が入っている。
if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) {
zval *result = EX_VAR(opline->result.var);
ZVAL_OBJ(result, Z_OBJ(EX(This)));
Z_ADDREF_P(result);
ZEND_VM_NEXT_OPCODE();
} else {
ZEND_VM_TAIL_CALL(zend_this_not_in_object_context_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
staticメソッド呼び出しではEX(This)にクラスが入るが、インスタンスメソッド呼び出しではオブジェクトが入っている。