web-dev-qa-db-ja.com

PHP-子クラスに定数を定義させる

PHPでは抽象定数を使用できないことに気付きました。

子クラスに定数を強制的に定義する方法はありますか(抽象クラ​​スの内部メソッドの1つで使用する必要があります)?

43
Alex

constantconstantです; PHPに限り、abstractまたはprivate定数はありませんが、回避策はあります。

サンプル抽象クラス

abstract class Hello {
    const CONSTANT_1 = 'abstract'; // Make Abstract
    const CONSTANT_2 = 'abstract'; // Make Abstract
    const CONSTANT_3 = 'Hello World'; // Normal Constant
    function __construct() {
        Enforcer::__add(__CLASS__, get_called_class());
    }
}

これで問題ありません

class Foo extends Hello {
    const CONSTANT_1 = 'HELLO_A';
    const CONSTANT_2 = 'HELLO_B';
}
new Foo();

バーはエラーを返します

class Bar extends Hello {
    const CONSTANT_1 = 'BAR_A';
}
new Bar();

Songoはエラーを返します

class Songo extends Hello {

}
new Songo();

エンフォーサクラス

class Enforcer {
    public static function __add($class, $c) {
        $reflection = new ReflectionClass($class);
        $constantsForced = $reflection->getConstants();
        foreach ($constantsForced as $constant => $value) {
            if (constant("$c::$constant") == "abstract") {
                throw new Exception("Undefined $constant in " . (string) $c);
            }
        }
    }
}
28
Baba

これはちょっとした「ハック」かもしれませんが、ほんの少しの労力でジョブを実行しますが、子クラスで定数が宣言されていない場合は、異なるエラーメッセージが表示されます。

自己参照定数宣言は構文的に正しく、問題なく解析され、その宣言が実行時に実際に実行された場合にのみエラーをスローするため、抽象クラスの自己参照宣言mustは子でオーバーライドされるそれ以外のクラスでは致命的なエラーが発生します:Cannot declare self-referencing constant

この例では、抽象親クラスFooは、すべての子に変数NAMEを宣言するように強制します。このコードは正常に実行され、Donaldを出力します。ただし、子クラスFoolingnotを宣言した場合、致命的なエラーがトリガーされます。

<?php

abstract class Foo {

    // Self-referential 'abstract' declaration
    const NAME = self::NAME;

}

class Fooling extends Foo {

    // Overrides definition from parent class
    // Without this declaration, an error will be triggered
    const NAME = 'Donald';

}

$fooling = new Fooling();

echo $fooling::NAME;
37
WebSmithery

残念なことに...定数は、錫について述べたとおりの定数です。一度定義すると、再定義することはできません。そのため、その方法では、PHPの抽象的な継承またはインターフェースを介して定義を要求することはできません。

ただし...定数が親クラスのコンストラクターで定義されているかどうかを確認できます。そうでない場合は、例外をスローします。

abstract class A
{
    public function __construct()
    {
        if (!defined('static::BLAH'))
        {
            throw new Exception('Constant BLAH is not defined on subclass ' . get_class($this));
        }
    }
}

class B extends A
{
    const BLAH = 'here';
}

$b = new B();

これはあなたの最初の説明からこれを行うことを考えることができる最良の方法です。

18
Jamie Rumbelow

いいえ、まだ抽象メソッドなどの他の方法を試すことができます:

abstract class Fruit
{
    abstract function getName();
    abstract function getColor();

    public function printInfo()
    {
        echo "The {$this->getName()} is {$this->getColor()}";
    }
}

class Apple extends Fruit
{
    function getName() { return 'Apple'; }
    function getColor() { return 'red'; }

    //other Apple methods
}

class Banana extends Fruit
{
    function getName() { return 'banana'; }
    function getColor() { return 'yellow'; }

    //other banana methods
}  

または静的メンバー:

abstract class Fruit
{
    protected static $name;
    protected static $color;

    public function printInfo()
    {
        echo "The {static::$name} is {static::$color}";
    }
}

class Apple extends Fruit
{
    protected static $name = 'Apple';
    protected static $color = 'red';

    //other Apple methods
}

class Banana extends Fruit
{
    protected static $name = 'banana';
    protected static $color = 'yellow';

    //other banana methods
} 

ソース

7
Songo

Php 7.2でテストされていますが、5.3以降では、この動作をアーカイブするために遅延静的バインディングを活用できます。ほとんどの原因では、実行時に致命的エラーを処理する必要がないため、例外と同じエラーを達成する致命的なエラーがスローされます。必要に応じて、カスタムエラーハンドラを簡単に実装できます。

だから、私にとっては次のように機能します:

<?php

abstract class Foo {

    public function __construct() {
        echo static::BAR;
    }

}


class Bar extends Foo {
    const BAR = "foo bar";
}

$bar = new Bar();    //foo bar

constを削除すると、以下が得られます:

Fatal error: Uncaught Error: Undefined class constant 'BAR' in ...
1
Code Spirit