## クラスの抽象化

PHP には、抽象クラス、抽象メソッド、抽象プロパティがあります。 abstract として定義された抽象クラスのインスタンスを生成することはできず、 1つ以上の抽象メソッドや抽象プロパティを含むクラスは抽象クラスでなければいけません。 abstract として定義されたメソッドは、そのメソッドのシグネチャと public または protected のアクセス権を宣言するのみで、 実装を定義することはできません。抽象プロパティは、 `get` や `set` の要件を宣言することができ、 実装はどちらか一方に対してのみ行えます。両方同時に実装することはできません。

抽象クラスから継承する際、親クラスの宣言で abstract としてマークされた 全てのメソッドは、子クラスで定義されなければなりません。加えて、 オブジェクトの継承 と シグネチャの互換性に関するルール に従わなければいけません。

PHP 8.4 から、抽象クラスは public または protected の抽象プロパティを宣言できるようになりました。 protected な抽象プロパティは、protected または public のスコープから読み書き可能なプロパティにより 要件が満たされます。

抽象プロパティは、通常のプロパティによって、または 必要な操作に対応した フック を定義したプロパティにより要件が満たされます。

例1 抽象メソッドの例

    <?php

    abstract class AbstractClass
    {
        // 拡張クラスにこのメソッドの定義を強制する
        abstract protected function getValue();
        abstract protected function prefixValue($prefix);

        // Common method
        public function printOut()
        {
            print $this->getValue() . "\n";
        }
    }

    class ConcreteClass1 extends AbstractClass
    {
        protected function getValue()
        {
            return "ConcreteClass1";
        }

        public function prefixValue($prefix)
        {
            return "{$prefix}ConcreteClass1";
        }
    }

    class ConcreteClass2 extends AbstractClass
    {
        public function getValue()
        {
            return "ConcreteClass2";
        }

        public function prefixValue($prefix)
        {
            return "{$prefix}ConcreteClass2";
        }
    }

    $class1 = new ConcreteClass1();
    $class1->printOut();
    echo $class1->prefixValue('FOO_'), "\n";

    $class2 = new ConcreteClass2();
    $class2->printOut();
    echo $class2->prefixValue('FOO_'), "\n";

    ?>

上の例の出力は以下となります。

    ConcreteClass1
    FOO_ConcreteClass1
    ConcreteClass2
    FOO_ConcreteClass2

例2 抽象メソッドの例

    <?php

    abstract class AbstractClass
    {
        // 抽象メソッドでは、必須の引数だけを定義しています
        abstract protected function prefixName($name);
    }

    class ConcreteClass extends AbstractClass
    {
        // 子クラスでは、親のシグネチャにないオプションパラメータを定義することもあるでしょう
        public function prefixName($name, $separator = ".")
        {
            if ($name == "Pacman") {
                $prefix = "Mr";
            } elseif ($name == "Pacwoman") {
                $prefix = "Mrs";
            } else {
                $prefix = "";
            }

            return "{$prefix}{$separator} {$name}";
        }
    }

    $class = new ConcreteClass();
    echo $class->prefixName("Pacman"), "\n";
    echo $class->prefixName("Pacwoman"), "\n";

    ?>

上の例の出力は以下となります。

    Mr. Pacman
    Mrs. Pacwoman

例3 抽象プロパティの例

    <?php

    abstract class A
    {
        // 継承するクラスは、public に読み取り可能なプロパティを持たなければなりません
        abstract public string $readable {
            get;
        }

        // 継承するクラスは、protected または public に書き込み可能なプロパティを持たなければなりません
        abstract protected string $writeable {
            set;
        }

        // 継承するクラスは、protected または public で読み書き可能なプロパティを持たなければなりません
        abstract protected string $both {
            get;
            set;
        }
    }

    class C extends A
    {
        // 要件を満たし、さらに書き込みも可能にしているため有効です
        public string $readable;

        // public に読み取り可能でないため、要件を満たしません
        protected string $readable;

        // 要件を正確に満たしているため有効です
        // protected のスコープからのみ書き込みが可能です
        protected string $writeable {
            set => $value;
        }

        // protected から public にアクセス権を拡張しており、問題ありません
        public string $both;
    }

    ?>

抽象プロパティにはフックを実装できます。 前の例のように、`get` または `set` のどちらかを、定義せず宣言のみ行います。

例4 フック付きの抽象プロパティの例

    <?php

    abstract class A
    {
        // set のデフォルト実装（オーバーライド可能）を提供し、
        // 子クラスが get を実装するよう要求しています
        abstract public string $foo {
            get;

            set {
                $this->foo = $value;
            }
        }
    }

    ?>

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