クラス定数にもvisibilityを付ける
クラス定数のvisibilityは、constにpublic/protected/privateを付けて公開範囲を絞る機能です。公開APIと内部実装の定数を分け、設計の意図をコードで示せます。
const にも public/private が付く
PHP 7.1 以降、クラス定数にもアクセス修飾子を付けられるようになりました。長らく定数はすべて public 扱いだったので、つい何も付けずに書きがちですが、公開範囲を絞れると設計の意図をだいぶ表現しやすくなりますね。外に見せたい定数と、内部の都合でしかない定数を分けられるわけです。
final class HttpClient
{
public const DEFAULT_TIMEOUT = 30; // 利用側に見せる
private const RETRY_LIMIT = 3; // 内部の都合
protected const USER_AGENT = 'buglabo/1.0';
}
echo HttpClient::DEFAULT_TIMEOUT; // 30
// echo HttpClient::RETRY_LIMIT; // Error: private
DEFAULT_TIMEOUT のような設定値は外から参照されることを想定していますが、RETRY_LIMIT は実装の内側で使うだけです。この違いを private で表明しておくと、後から中身をいじるときに「これは外に影響しない」と安心して変えられます。
public 定数は暗黙のAPIになる
気をつけたいのは、public 定数はそのままクラスの公開インターフェースの一部になるという点ですね。値を変えたり消したりすると、参照している側が壊れます。だから「外から使ってほしくない値」まで public にしておくと、うっかり依存されて後で身動きが取れなくなります。迷ったらまず private にして、必要になってから開けるのが安全だと思います。
final class OrderStatus
{
public const PENDING = 'pending';
public const PAID = 'paid';
// 内部の対応表は外に出さない
private const LABELS = [
self::PENDING => '未払い',
self::PAID => '支払い済み',
];
public static function label(string $status): string
{
return self::LABELS[$status] ?? '不明';
}
}
継承と定数のオーバーライド
private const はサブクラスから参照できないので、テンプレートメソッドのように子で上書きさせたいなら protected にする必要があります。self:: は定義したクラスの定数を、static:: は実際のクラスの定数を見に行くので、オーバーライドを効かせたいときは static:: を使う点も覚えておくといいですね。ここは意外と踏みやすい落とし穴だと思います。
class Base
{
protected const NAME = 'base';
public function name(): string { return static::NAME; }
}
class Child extends Base
{
protected const NAME = 'child';
}
echo (new Child)->name(); // child (static:: なので上書きが効く)
まとめ
定数の visibility は小さな機能ですが、「これは公開API、これは内部実装」という線引きをコードで示せるのが良いところだと思います。public 定数は変更が波及するので慎重に、内部専用は private にして守る、という原則で回すと後々の変更が楽になる気がします。とりあえず全部 public、から一歩進んでみる価値はありますね。
よくある質問
Q. クラス定数にpublic/privateはいつから付けられますか。
A. PHP 7.1以降です。それ以前は定数はすべてpublic扱いでした。
Q. public定数を後から変えると何が起きますか。
A. public定数は公開インターフェースの一部なので、値を変えたり消したりすると参照している側が壊れます。
Q. サブクラスで定数を上書きするには。
A. private constは子から参照できないのでprotectedにします。上書きを効かせたい参照は self:: でなく static:: を使います。