PHPで簡易Enumを実装する

同じようなネタはいっぱい有りそうなんだけど脊髄反射で書いてしまったので被っても御愛嬌で。探すより自分の目的に特化したのを脊髄反射で書いた方が早いだろうと思って始めたら、意外と手間がかかったので、ここにメモしておく。

```php
<?php
/**
 * JavaっぽいEnumを実装するためのベース
 */
abstract class EnumBase {
    // Enumを放り込んでおく場所です
    private static $enumArray = array ();

    // 値の名前
    private $name  = null;

    // 値
    private $value = null;

    // public には new させません
    private function __construct () {
    }
    private function setName ($name) {
        $this->name = $name;
    }
    private function setValue ($value) {
        $this->value = $value;
    }

    /**
     * 名前を直に指定してアクセスしたい時に利用します
     * @param $name Enumの名前です
     * @param $args 今回は使いません
     * @return 対象のインスタンスです
     */
    final public static function __callStatic($name,$args){
        if ($args) throw new Exception('undefined method');
        return self::valueOf($name);
    }

    /**
     * 全てのEnumを返却
     * @return 該当Enumの配列
     */
    final public static function values () {
        $className = get_called_class();
        if (!isset(self::$enumArray[$className])) {
            $class = new ReflectionClass($className);
            $history = array();
            foreach ($class->getConstants() as $key => $val) {
                if (in_array($val, $history)) throw new Exception('値の重複っす');
                $enum = new $className;
                $enum->setName($key);
                $enum->setValue($val);
                self::$enumArray[$className][] = $enum;
                $history[] = $val;
            }
        }
        return self::$enumArray[$className];
    }

    /**
     * 名前から該当Enumを探して返却
     * @param $name 名前
     * @return Enumのオブジェクト
     */
    final public static function valueOf ($name) {
        $result = null;
        foreach (self::values() as $enum) {
            if ($enum->name() === $name) {
                $result = $enum;
                break;
            }
        }
        return $result;
    }

    /**
     * 値から該当Enumを探して返却
     * @param $value 値
     * @return Enumのオブジェクト
     */
    final public static function nameOf ($value) {
        $result = null;
        foreach (self::values() as $enum) {
            if ($enum->value() === $value) {
                $result = $enum;
                break;
            }
        }
        return $result;
    }


    /**
     * 名前を返却
     * @return 値の名前を文字列で
     */
    public function name() {
        return $this->name;
    }

    /**
     * 値を返却
     * @return 値
     */
    public function value(){
        return $this->value;
    }

    /**
     * 比較する
     * @param $enum 比べる対象あ
     * @return boolean 一致ならtrue
     */
    public function equals (EnumBase $enum) {
        return $this->name() === $enum->name() &&
            $this->value() === $enum->value() &&
            get_class($this) === get_class($enum);
    }
}

///
/// 使い方
///


// こうやって継承してconstを作って
class Enum1 extends EnumBase {
    const HOGE = 1;
    const FUGA = 2;

    // しょうもないメソッドを作ったり
    public function getNickName () {
        return 'Nick:' . $this->name();
    }
}
class Enum2 extends EnumBase {
    const PIYO = 1;
    const FIZZ = 3;
    const BUZZ = 4;

    public function dqndqn () {
        return $this->name() . '|' . $this->value();
    }
}

// constとしてアクセスしたり
var_dump(ENUM1::HOGE);

// constの名前でメソッドアクセスしたり
var_dump(ENUM1::HOGE()->getNickName());
var_dump(Enum1::HOGE()->equals(Enum2::PIYO()));
var_dump(Enum1::HOGE()->equals(Enum1::HOGE()));

// foreachで回したりする
foreach (Enum1::values() as $enum) {
    var_dump($enum->getNickName());
}

foreach (Enum2::values() as $enum) {
    var_dump($enum->dqndqn());
}
```