PHPでは抽象定数を使用できないことに気付きました。
子クラスに定数を強制的に定義する方法はありますか(抽象クラスの内部メソッドの1つで使用する必要があります)?
constant
はconstant
です; 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);
}
}
}
}
これはちょっとした「ハック」かもしれませんが、ほんの少しの労力でジョブを実行しますが、子クラスで定数が宣言されていない場合は、異なるエラーメッセージが表示されます。
自己参照定数宣言は構文的に正しく、問題なく解析され、その宣言が実行時に実際に実行された場合にのみエラーをスローするため、抽象クラスの自己参照宣言mustは子でオーバーライドされるそれ以外のクラスでは致命的なエラーが発生します:Cannot declare self-referencing constant
。
この例では、抽象親クラスFoo
は、すべての子に変数NAME
を宣言するように強制します。このコードは正常に実行され、Donald
を出力します。ただし、子クラスFooling
がnotを宣言した場合、致命的なエラーがトリガーされます。
<?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;
残念なことに...定数は、錫について述べたとおりの定数です。一度定義すると、再定義することはできません。そのため、その方法では、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();
これはあなたの最初の説明からこれを行うことを考えることができる最良の方法です。
いいえ、まだ抽象メソッドなどの他の方法を試すことができます:
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
}
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 ...