2022-04-15

[php-src読書録]その14: 継承

php-srcのコードリーディングした内容をコツコツ残すテスト。その14

引き続きclass。今日は継承

<?php

class Base
{
  public function call()
  {
    echo 123;
  }
}

class Hoge extends Base
{
}

(new Hoge)->call();

opcode

$_main:
     ; (lines=5, args=0, vars=0, tmps=3)
     ; (before optimizer)
     ; /path/to/14.php:1-18
     ; return  [] RANGE[0..0]
0000 V0 = NEW 0 string("Hoge")
0001 DO_FCALL
0002 INIT_METHOD_CALL 0 V0 string("call")
0003 DO_FCALL
0004 RETURN int(1)
LIVE RANGES:
     0: 0001 - 0002 (new)

Base::call:
     ; (lines=2, args=0, vars=0, tmps=0)
     ; (before optimizer)
     ; /path/to/14.php:7-10
     ; return  [] RANGE[0..0]
0000 ECHO int(123)
0001 RETURN null

extendsが定義されていると、 ce->parent_name にスーパークラスの名前がセットされる。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7721-L7724

parent_name を使って EG(class_table) からzend_class_entryを引いてくる。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_compile.c#L7764-L7765

zend_do_inheritance_ex() で継承の設定が色々行われる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1456-L1458

子クラスのデフォルトプロパティをセット
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1443-L1454

親クラスのデフォルトプロパティをセット
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1474-L1483

offsetの位置を調整
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1541:L1541

parentにも同じ名前のプロパティがあればチェックするだけ、 parentにあってchildになければ追加する、といったことをやっている
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1551-L1553
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1225-L1282

constructorと言いつつ全マジックメソッドを良い感じにオーバーライド
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L112:L112

同名のデフォルトプロパティはparent側にマージ。childの値をparentに代入し、child側をUNDEFする。 child_info->offsetにparent_info->offsetを代入することで、parent側に向くようにしている。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1258-L1260

private, public, abstract

メソッドの場合は do_inheritance_check_on_method_ex() でアクセス修飾子のチェックを行っている

parentがfinalの場合はエラー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1051-L1058

child staticの設定状態とparent staticの設定状態が異なる場合はエラー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1063-L1076

↓は以下のようなコードの場合にエラーにしている
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1079-L1086

<?php

class Base
{
    public function call() {}
}

abstract class Hoge extends Base
{
    public abstract function call();
}

childがprivate、parentがpublicといった感じでchildがアクセス制限を狭める場合にエラー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1122-L1130

プロパティの場合

parentのstaticとchildのstaticの設定状態が変わる場合はエラー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1236:L1236

parentとchildのreadonlyの設定状態が変わる場合はエラー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1241

childがprivate、parentがpublicといった感じでchildがアクセス制限を狭める場合にエラー
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L1250

abstractのチェック

abstractな関数を持っていたり、abstractな関数を持つクラスを継承している場合、zend_verify_abstract_class() でチェックされる
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L3050-L3052

abstractな関数を持っていて(継承して実装している場合は除く)、abstractクラスではない場合エラーになる。
https://github.com/php/php-src/blob/PHP-8.1.4/Zend/zend_inheritance.c#L2313-L2344

このエントリーをはてなブックマークに追加