クラス定数

定数とクラス定数

定数はご存知かと思います。
PHPでは定数をdefine関数で定義します。

define(定数名, 値)

ですね。

この定数ってグローバル定数として定義されますよね。
なのでdefine関数で定義した定数はどこでも使用できます。
関数の中でもクラスの中でも。

クラス定数はこれと似たようなものですが、名前から察せられるとおりクラス内に定義する定数です。
クラスのメンバとして定義するのです。

<?php

class Product
{
    // 税率
    const TAX_RATE = 0.05;

    // 金額
    private $price = 0;
   
    // 価格設定
    public function setPrice($price)
    {
        $this->price = $price;
    }

    // 税込み価格取得
    public function getSalePrice()
    {
        // 定数をクラス内で参照
        $tax = $this->price * self::TAX_RATE;
        $price = $this->price + $tax;
        return $price;
    }
}

// 定数をクラス外から参照
var_dump(Product::TAX_RATE);

$prd = new Product();
$prd->setPrice(100);
$price = $prd->getSalePrice();

var_dump($price);

?>
float(0.05)
float(105)

クラス定数は、クラス内に

const 定数名 = 値

の形式で定義します。
アクセス修飾子は付けられません。なぜならクラス定数は必ずpublicだからです。
定数名には「$」は付けません。define関数での定義と同じですね。

そして定義されたクラス定数は、クラスの外からは

クラス名::定数名

クラス内からの参照は

self::定数名

となります。

見たとおり、クラス定数はクラスのインスタンスを生成せずに、直接参照できます。理屈的には静的メンバと同様です。ていうか、クラス定数も静的メンバです。
静的フィールドとの違いは値の変更が出来るか出来ないかだけのものです。

で、必ずpublicということは、結局クラス定数も通常の定数と同じくグローバルという事になります。

クラス定数の利用価値

ではクラス定数はどういう時に使うのでしょうか。
基本的にはクラス内で共通に使用される、設定値の格納用として役割が多いです。
先ほどの例のような使い方です。
定数はクラス外からも参照は可能ですが、クラス内で使用するパターンが多いです。

もう一つ、定数のパッケージングという使い方があります。
例えば次の例を見てください。

<?php

class Sex
{
    const MAN = 1;
    const WOMAN = 2;
}

class Person
{
    private $name;
    private $sex;
    
    public function __construct($name, $sex)
    {
        $this->name = $name;
        $this->sex = $sex;
    }
    
    public function scream()
    {
        if (Sex::MAN == $this->sex) {
            return 'グワアアア';
        } else if (Sex::WOMAN == $this->sex) {
            return 'キャアアア';
        } else {
            return '性別がおかしいです';
        }
    }
}

$person = new Person('花子', Sex::WOMAN);
var_dump($person->scream());

?>
string(15) "キャアアア"

Sexという、定数のみが定義されたクラスが定義されています。
システム上、男性は数値の1、女性は数値の2として区別すると決め事をするとします。
決め事とは言え、人間にとっては直感的に1と男性または2と女性は結びつきませんので、わかりにくくなります。

そのため、「Sex」クラスで定数「MAN」を1に、「WOMAN」を2として設定し、
例えばPersonのコンストラクタで「new Person('花子', 2)」と書くよりも、
「new Person('花子', Sex::WOMAN)」と書いたほうが直感的にわかりやすくなります。

また例えば現状は男が1、女が2という決め事だとしても、
何かの理由でこれを逆にしなければならなくなったとしましょう。
1や2とか言う数値を直書きしていたら、いろんなところに書いている1や2を全て逆転させなくてはなりません。
一つでも修正漏れがあるとそのまま重大なバグですね。
そこで定数で設定しておけば、Sexクラス内の設定値を逆転させるだけでよくなるというわけです。

まあこのへんはクラス定数に限った話ではなく、普通の定数でもいいんじゃん?ってなるかもしれません。ではクラス定数のメリットはどこにあるかです。

普通に、

define('MAN', 1);
define('WOMAN', 2);

ではなくてクラスにするメリットはどこにあるのでしょうか。
実際どちらでもかまいませんが、クラス定数なら定数を「分類化」する事ができます。「Sex::MAN」「Sex::WOMAN」とすると、Sexという性別を表す定数の中の男性、という感じで定数が階層的に分類できます。

これにより単にわかりやすいだけでなく、例えば違う分類で同じ定数名を使う事が可能になります。

例えば次の例を見てください。

<?php

// 処理ステータス
class ProcStatus
{
    const OK = 1; // 正常終了
    const NG = 2; // 異常終了
    const ONPROC = 3; // 処理中
}

// 処理準備状態
class ProcReadyStatus
{
    const OK = 0; // 準備OK
    const NG = 1; // 準備中
}

?>

あるシステムで処理状態の管理が必要だとします。
で、現在の処理状態を表す区分が3種類あるためにそれぞれ1、2、3という数値を割り当て、定数化するとします。

次に処理開始可能か同かを表す区分が2種類のために0、1という数値を割り当て、定数化するとします。

この異なる二つの定数クラスは、定数名の「OK」がかぶっています。
しかし実際は「ProcStatus::OK」と「ProcReadyStatus::OK」となり、別物です。

これをdefineでやるとしたら、

<?php

define('PROC_STATUS_OK', 1);
define('PROC_STATUS_NG', 2);
define('PROC_STATUS_ONPROC', 3);
define('PROC_READY_STATUS_OK', 0);
define('PROC_READY_STATUS_NG', 1);

?>

という感じになります。
名前を見ればわかるものの、明確な分類分けがなされておらず、わかりにくいと思いませんか?
実際どちらでもかまいませんが、オブジェクト指向ではオブジェクト指向らしくクラス定数を使用するとよいでしょう。