php-srcのコードリーディングした内容をコツコツ残すテスト。その15
今日はinterfaceとtrait
interface
<?php
interface Base
{
public function hoge();
}
class Hoge implements Base
{
public function hoge()
{
}
}
opcode
$_main:
; (lines=2, args=0, vars=0, tmps=0)
; (before optimizer)
; /path/to/15.php:1-14
; return [] RANGE[0..0]
0000 DECLARE_CLASS string("hoge")
0001 RETURN int(1)
Base::hoge:
; (lines=1, args=0, vars=0, tmps=0)
; (before optimizer)
; /path/to/15.php:5-5
; return [] RANGE[0..0]
0000 RETURN null
Hoge::hoge:
; (lines=1, args=0, vars=0, tmps=0)
; (before optimizer)
; /path/to/15.php:10-12
; return [] RANGE[0..0]
0000 RETURN null
implementsした場合は DECLARE_CLASS
opcodeが出力されているのがポイント。
前回はuseとimplementsがないパターンの継承パターンだったが、useとimplementsがある場合はフローがちょっと変わってくる。
interfaceも zend_compile_class_decl()
でコンパイルされる
そのinterfaceをimplementsした場合は zend_compile_implements()
が呼ばれる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7733:L7733
クラスが継承しているインターフェースの数とインターフェースを num_interfaces
interface_names
にセットする
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7598-L7599
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER
は do_bind_class()
を呼び出す
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
do_bind_class(RT_CONSTANT(opline, opline->op1), (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
do_bind_class()
は zend_bind_class_in_slot()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L1164:L1164
zend_bind_class_in_slot()
は zend_do_link_class()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L1131:L1131
zend_do_link_class()
は zend_do_implement_interfaces()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L2846:L2846
zend_do_implement_interfaces()
は do_implement_interface()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1767:L1767
do_implement_interface()
は do_interface_implementation()
を呼び出す
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1772
do_inherit_iface_constant()
や do_inherit_method()
で継承
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1650:L1650
zend_do_inherit_interfaces()
ではimplements先のインターフェースが継承しているインターフェースを追加する
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1666:L1666
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1310-L1320
例えば、C1 => I1 => I2 => I3 という感じでimplementsしている場合、ce->interfaces
にはI2はI3, I1はI2+I3が入る。
C1では自身がimplementsしているI1とI1のインターフェースの ce->interfaces
が入るのでI1,I2,I3がセットされることになる。
interfaceのメソッドを実装していないときのハンドリングはabstractと同じ仕組み
trait
traitも zend_compile_class_decl()
によって zend_class_entry
にコンパイルされる。
useした場合は zend_do_bind_traits()
が呼ばれる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L2830-L2832
zend_do_bind_traits()
ではmethodやpropertyのバインディング(クラスへのコピー)を行う。
メソッドは zend_do_traits_method_binding()
でuseしたtraitの数だけ zend_traits_copy_functions()
が呼ばれる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L2123-L2129
traitのmethodをコピー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1901
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1922
zend_hash_update_ptr
で ce->function_table
のハッシュテーブルを更新し、メソッドを追加
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1843-L1844
alias
aliasが入っているとここでmodifierを変更している
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1904-L1920
<?php
trait T
{
public function call()
{}
}
class C
{
use T {
call as private;
}
}
別名が入っている場合は↓のルートで名前やmodifierを変更してメソッドを追加している
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1872-L1897
<?php
trait T
{
public function call()
{}
}
class C
{
use T {
call as public call2;
}
}
優先度などのチェックはここでやっている
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1959-L2026
insteadofを入れている場合は対象のクラスから除外する関数名を引けるように exclude_tables
にセットしている
除外している場合は zend_hash_find(exclude_table, fnname)
がNULLじゃなくなるので関数がコピーされない。これによってtraitの関数の優先度を制御している
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L2107-L2121
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1899:L1899