php-srcのコードリーディングした内容をコツコツ残すテスト。その11
今回はいよいよ(?)class。
コンストラクタと初期化部分から
<?php
class Hoge
{
public $fuga;
public function __construct($fuga)
{
$this->fuga = $fuga;
}
}
new Hoge(111);
opcode
$_main:
; (lines=5, args=0, vars=1, tmps=3)
; (before optimizer)
; /path/to/11.php:1-20
; return [] RANGE[0..0]
0000 V1 = NEW 1 string("Hoge")
0001 SEND_VAL_EX int(111) 1
0002 DO_FCALL
0003 ASSIGN CV0($hoge) V1
0004 RETURN int(1)
LIVE RANGES:
1: 0001 - 0003 (new)
Hoge::__construct:
; (lines=4, args=1, vars=1, tmps=1)
; (before optimizer)
; /path/to/11.php:7-10
; return [] RANGE[0..0]
0000 CV0($fuga) = RECV 1
0001 ASSIGN_OBJ THIS string("fuga")
0002 OP_DATA CV0($fuga)
0003 RETURN null
class定義は zend_compile_class_decl()
でコンパイルされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7646:L7646
だいたいこんな感じな処理をしていて、zend_class_entryの設定を行っている。
zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
// ...
ce->type = ZEND_USER_CLASS;
ce->name = name;
zend_initialize_class_data(ce, 1);
ce->ce_flags |= decl->flags;
// ...
ce->info.user.filename = zend_string_copy(zend_get_compiled_filename());
ce->info.user.line_start = decl->start_lineno;
ce->info.user.line_end = decl->end_lineno;
zend_compile_stmt(stmt_ast);
zend_compile_stmt()
でプロパティやメソッドのコンパイルが行われる。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7744
zend_compile_prop_group()
が呼ばれる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L10024
zend_compile_prop_group()
は zend_compile_prop_decl()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7410:L7410
zend_compile_prop_decl()
は zend_declare_typed_property()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7395:L7395
property_info->offset
をセットしつつ default_properties_count
をインクリメント
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L4138-L4140
色々設定してproperties_info
のハッシュテーブルにセット
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L4172-L4190
コンストラクタは zend_compile_func_decl()
でコンパイルされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7172:L7172
メソッドや関数をコンパイルする共通関数で、__construct
というメソッドがコンパイルされる
メソッドの場合 zend_begin_method_decl()
が呼ばれる。
zend_begin_method_decl()
は zend_add_magic_method()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7100:L7100
コンストラクタの場合は ce->constructor
に zend_function
の値がセットされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L2613-L2614
zend_compile_new()
はNEWをemitしている
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L4675-L4684
その後、 zend_compile_call_common()
が呼ばれ、引数のコンパイルと呼び出し処理のopcodeがemitされる。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L4686
ZEND_DO_FCALLがこの辺でemitされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L3713:L3713
コンストラクタ内のプロパティ代入はこのあたりでコンパイルされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L3242-L3251
opcode
ZEND_NEW
は ZEND_NEW_SPEC_CONST_UNUSED_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);
constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
call = zend_vm_stack_push_call_frame(
ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS,
constructor,
opline->extended_value,
Z_OBJ_P(result));
objectの初期化
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L1659-L1666
クラスごとにプロパティの領域が変わるが、オブジェクトごとに確保すべきプロパティの領域は zend_object_properties_size()
で取得している
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_objects_API.h#L81-L86
プロパティのzvalをコピーして初期化
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_API.c#L1507-L1528
ZEND_ASSIGN_OBJ
は ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_OP_DATA_CV_HANDLER
で処理される
だいたいこんな感じな処理をしている。
object = &EX(This);
value = _get_zval_ptr_cv_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC);
value = zobj->handlers->write_property(zobj, name, value, (IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL);
opline+1はOP_DATAの値を取得している。
write_property
は関数ポインタで zend_std_write_property()
がセットされる。
この関数内の zend_get_property_offset()
で properties_info
のハッシュテーブルからオフセットを取得する。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_object_handlers.c#L774:L774
そのオフセットを使って OBJ_PROP(zobj, property_offset)
でプロパティのポインタを取得し、zend_assign_to_variable()
でプロパティに値をセットする。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_object_handlers.c#L799-L800