## オブジェクトの継承

プログラミング言語の原則としてよくみられるものに継承があります。 PHP はオブジェクトモデルにおいてこの継承を利用しています。 多くのクラスとオブジェクトとの連携に継承は関係しています。

例えば、クラスを拡張するとき、サブクラスは親クラスから public と、protected のメソッドや、プロパティや定数をすべて引き継ぎます。 (子の)クラスが親のメソッドをオーバーライドしない限り、 親のメソッドの機能が保持されます。

これは、機能を定義して抽象化するのに便利です。 また、同じようなオブジェクトに機能を追加する際に、 共通機能を再実装する必要がなくなります。

親クラスの private メソッドは、子クラスからアクセスすることができません。 その結果として、子クラスは通常の継承のルールを無視して private メソッドそのものを再実装することができてしまいます。 しかし、PHP 8.0.0 より前のバージョンでは、 `final` と `static` に関連する制限が private メソッドに適用されていました。 PHP 8.0.0 以降では、private メソッドの規則が強制されるのは `private final` として宣言されたコンストラクタのみになりました。 なぜなら、static として宣言されたファクトリメソッドを使う場合に、 コンストラクタを無効にする方法として `private final` が用いられるからです。

メソッドやプロパティ、そして定数の アクセス権 に関するルールは、子クラスで緩めることが可能です。 たとえば、 親クラスで `protected` なメソッドは 子クラスで `public` としてマークできます。 しかし、厳しくすることはできません。 つまり、親クラスで `public` なプロパティを 子クラスで `private` にすることはできません。 これの例外はコンストラクタです。 コンストラクタのアクセス権は厳しくすることができます。 たとえば、親クラスの `public` なコンストラクタは、 子クラスで `private` としてマークできます。

> 注意:

> オートローディングが有効になっていない限り、 クラスの定義は実際に使うより前になければなりません。 別のクラスを継承したクラスの場合は、 そのクラスより前に親クラスが宣言されていなければなりません。 この規則が適用されるのは、別のクラスやインターフェイスを継承したクラスです。

> 注意:

> 読み取りと書き込みが両方可能なプロパティを、 readonly としてオーバーライドしてはいけません。逆も同じです。

>     <?php

>     class A {
>         public int $prop;
>     }
>     class B extends A {
>         // 正しくありません: read-write -> readonly
>         public readonly int $prop;
>     }
>     ?>

例1 継承の例

    <?php

    class Foo
    {
        public function printItem($string)
        {
            echo 'Foo: ' . $string . PHP_EOL;
        }

        public function printPHP()
        {
            echo 'PHP is great.' . PHP_EOL;
        }
    }

    class Bar extends Foo
    {
        public function printItem($string)
        {
            echo 'Bar: ' . $string . PHP_EOL;
        }
    }

    $foo = new Foo();
    $bar = new Bar();
    $foo->printItem('baz'); // 出力: 'Foo: baz'
    $foo->printPHP();       // 出力: 'PHP is great.'
    $bar->printItem('baz'); // 出力: 'Bar: baz'
    $bar->printPHP();       // 出力: 'PHP is great.'

    ?>

### 内部クラスと戻り値の型の互換性

PHP 8.1 より前のバージョンでは、 ほとんどの内部クラスやメソッドは戻り値の型を宣言していませんでしたし、 それらを継承する際にもあらゆる戻り値の型を指定することができていました。

PHP 8.1.0 以降では、 ほとんどの内部メソッドが、戻り値の型を "とりあえず" 宣言するようになりました。 この場合、それらを継承したメソッドの戻り値の型は、 親と互換性があるものにすべきです。 そうしない場合、推奨されない警告が発生します。 注意すべきなのは、明示的に戻り値の型を宣言していない場合でも、 シグネチャが一致していないとみなされ、 推奨されない警告が発生することです。

PHP のバージョン間の互換性を保ちたいがために、 戻り値の型を宣言できない場合、 アトリビュート ReturnTypeWillChange を追加することで警告を抑止できます。

例2 戻り値の型を宣言せずにメソッドをオーバーライドする場合

    <?php
    class MyDateTime extends DateTime
    {
        public function modify(string $modifier) { return false; }
    }

    // PHP 8.1.0 以降では、"Deprecated: Return type of MyDateTime::modify(string $modifier) should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice"
    ?>

例3 間違った型を宣言してメソッドをオーバーライドする場合

    <?php
    class MyDateTime extends DateTime
    {
        public function modify(string $modifier): ?DateTime { return null; }
    }

    // PHP 8.1.0 以降では、"Deprecated: Return type of MyDateTime::modify(string $modifier): ?DateTime should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice"
    ?>

例4 間違った型を宣言してメソッドをオーバーライドする場合でも、警告を抑制する

    <?php
    class MyDateTime extends DateTime
    {
        /**
         * @return DateTime|false
         */
        #[\ReturnTypeWillChange]
        public function modify(string $modifier) { return false; }
    }

    // 警告は発生しません。
    ?>

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open