クラス定数にも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:: を使います。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です