Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

typesafeな定数

typesafeな定数については、型に安全な定数あたりを参照。Java5以前だと

public final class Card {
    private String name;
    public static final Card CLUBS = new Card('clubs');
    public static final Card DIAMONDS = new Card('diamonds');
    public static final Card HEARTS = new Card('hearts');
    public static final Card SPADES = new Card('spades');

    private Card(String name) {
        this.name = name;
    }
    public function toString() {
        return this.name;
    }
}

な感じのコードでtypesafeな定数を実現できます。
以前からずっと「PHPでtypesafeな定数を簡単に書けないか」と思っていたのですが、PHPだと定数(const、define)にオブジェクトを入れること自体が不可能なので、Javaのようなコードで実現するのはムリです。
そこで、定数を返す静的メソッドを用意することで、これに近いことが(一応)できるので晒しておきます。

<?php
class Card
{
    private $name;
    private function __construct($name)
    {
       $this->name = $name;
    }
    public static function __callStatic($name, $args)
    {
        static $objects = null;
        if (is_null($objects)) {
            echo "create\n";
            $objects = array();
            $objects['CLUBS'] = new Card('clubs');
            $objects['DIAMONDS'] = new Card('diamonds');
            $objects['HEARTS'] = new Card('hearts');
            $objects['SPADES'] = new Card('spades');
        }
        if (isset($objects[$name])) {
            return $objects[$name];
        }
        throw new BadMethodCallException('method not exist');
    }
    public function __toString()
    {
        return $this->name;
    }
}

__callStaticを使っているのは、使う側で

Card::CLUBS()

としたいのと、定数ごとのメソッドを用意したくないのが理由です。
実際の使い方は次のような感じ。

<?php
function deal(Card $card) {
    echo $card . "\n";
}

deal(Card::CLUBS());
deal(Card::DIAMONDS());
deal(Card::HEARTS());
deal(Card::SPADES());

// エラー
// deal(new stdClass());