クラス定数は上書き可能です。
class FOO{
public const HOGE = 1;
}
class BAR extends FOO{
public const HOGE = 2;
}
var_dump(BAR::HOGE); // 2
定数なのに上書き可能とはこれ如何に。
クラス定数ではない通常の定数は当然ですが上書きできません。
const HOGE = 1;
const HOGE = 2; // E_WARNING
echo HOGE; // 1
エラーではなくE_WARNING止まりってのはよくわかりませんが、ともかく値は変更できていません。
定数は上書きできないのにクラス定数は上書きできるというのはよくわかりませんね。
まあ、実は他の言語もだいたいこんな仕様だったりするのですが。
class A{
static int num=0;
}
class B extends A{
static int num=10;
}
そんなわけで絶対に上書きされたくないクラス定数を、PHP8.1から書けるようになります。
class FOO{
final public const HOGE = 1;
}
えっpublicより前に置くのかよ。
まあ順番以外はJavaのstatic final
とだいたい同じです。
ということで以下はFinal class constantsのRFCの日本語訳です。
PHP RFC: Final class constants
Introduction
現在のところ、クラス定数は常に子クラスによってオーバーライド可能です。
このため幾つかの問題が発生します。
まず、遅延静的束縛において、PHPエンジンはクラス定数の参照を最適化できません。
このため、static::FOO
や$this::FOO
においてFOO
がオーバーライドされているという悲観的可能性を常に考えなければなりません。
さらに重要な問題としては、クラス定数が一意である保証がされないことです。
クラス内で常にself::FOO
で参照していたとしても、子クラスがその値を変更することを妨げることはできません。
クラス定数にfinal修飾子を適用することができれば、子クラスが値を上書きできないようにするという意図が明白になります。
ちなみにインターフェイス定数は既にfinalです。
interface I
{
public const X = "i";
}
class C implements I
{
public const X = "bar";
}
// Fatal error: Cannot inherit previously-inherited or override constant X from interface I
ところが、中間クラスを噛ませるとオーバーライド可能になってしまうという不整合があります。
interface I
{
public const X = "i";
}
class C implements I
{
}
class D extends C
{
public const X = "d";
}
// エラー出ない
Proposal
クラス定数にfinal修飾子を適用可能にします。
これにより、クラス定数のオーバーライドを禁止することができます。
class Foo
{
final public const X = "foo";
}
class Bar extends Foo
{
public const X = "bar";
}
// Fatal error: Bar::X cannot override final constant Foo::X
また、インターフェイス定数はデフォルトではオーバーライド可能になり、final修飾子を適用することでオーバーライド禁止になります。
interface I
{
public const X = "i";
final public const Y = "i";
}
class C implements I
{
public const X = "c"; // OK
public const Y = "c"; // NG
}
// Fatal error: C::Y cannot override final constant I::Y
Reflection
定数がfinalであるかどうかを調べるReflectionClassConstant::isFinal()
メソッドが追加されます。
Backward Incompatible Changes
後方互換性のない変更はありません。
Vote
投票数の2/3の賛成で受理されます。
本RFCは2021/05/19から2021/06/02にかけて投票が行われ、賛成29反対4の賛成多数で可決されました。
PHP8.1で導入されます。
感想
これまでfinalはクラスとメソッドにしか書けず、プロパティや定数には書けませんでした。
PHP8.1からは定数にも書けるようになります。
プロパティには引き続き書くことができませんが、こちらはreadonlyがちょうどいい代替になるでしょう。
しかし順番は絶対public final const HOGE = 1;
のほうがいいと思うんだけどどうなんだろう。
他言語に合わせるとすればpublic const final HOGE = 1;
ですが、PHPではconstに型宣言を書けないので、finalの位置が居心地悪くなってしまいます。
public const final int HOGE = 1;
と書ければ最も自然、というか他言語と同じ順番なので楽なのですけどね。
まあともかく、これでようやく変更されないことが保証されたクラス定数を作ることができるようになりました。