読者です 読者をやめる 読者になる 読者になる

PHP5.2 で Enum の実装

PHPEnumが欲しかったので試しに作ってみました。
作ってるうちに色々不満点が出てきたので、半端な状態だけどアイデア乞食目的で晒します。

PHPのバージョンは5.2以上を対象にしています。

Gist: https://gist.github.com/1874771
(参考URL: http://d.hatena.ne.jp/kamekoopa/20101202/1291274615)

Enumの基底クラス

<?php

/**
 * Enum のベースクラス
 * 
 */
abstract class Enum {

    public $code;
    public $name;
    private static $instances = array();

    protected static function setUp() {
        $addingEnums = func_get_args();
        // StackTraceからCall元のEnumのサブクラス名を取得する
        // PHP5.3以上ならget_called_class()が使える
        $stack = debug_backtrace();
        $className = $stack[1]['class'];

        // 取得したクラス名をもとにリフレクションでインスタンスを生成する
        $class = new ReflectionClass($className);
        // アクセス修飾子を問わずすべての静的メンバを動的に取得する
        $types = $class->getStaticProperties();
        // プロパティ名の配列を取得する
        $keys = array_keys($types);

        for ($i = 0; $i < count($keys); $i++) {
            /**
             * プロパティごとにインスタンスを生成しnameをセットしたあと
             * $instances にそのインスタンスを保持
             */
            $obj = new $className();
            $obj->code = $className . '@' . $i;
            $obj->name = $keys[$i];
            if ($class->hasProperty($keys[$i])) {
                $class->setStaticPropertyValue($keys[$i], $obj);
            }
            self::$instances[$keys[$i]] = $obj;
        }
    }

    public function equals(Enum $obj) {
        return $this->code === $obj->code;
    }

    public function __toString() {
       return $this->name;
    }

}
?>

独自のEnumの定義

<?php

require_once 'Enum.php';

class Fruits extends Enum {

    // 列挙子を静的プロパティとして定義
    public static $APPLE, $BANANA, $ORANGE;

    public static function setUp() {
        parent::setUp();
    }

}

// 静的プロパティの動的初期化(必須)
Fruits::setUp();
?>

定義したEnumを使ってみる

<?php

require_once 'Fruits.php';

// タイプヒンティング活用
function eat(Fruits $fruits) {
    // 等価性はEnumの$codeの値で比較される
    if ($fruits->equals(Fruits::$APPLE)) {
         // __toStringをオーバーライドしてるので
         // $nameの比較であれば===でもOK
         echo $fruits === Fruits::$APPLE;
    } else {

    }
}
?>

不満なところ

        // StackTraceからCall元のEnumのサブクラス名を取得する
        // PHP5.3以上ならget_called_class()が使える
        $stack = debug_backtrace();
        $className = $stack[1]['class'];

これのためにEnumのサブクラスで親のメソッドをcallしてあげないといけない。


あとここ。

// 静的プロパティの動的初期化(必須)
Fruits::setUp();

スタティックイニシャライザがあれば…というところなのだけど、マジックメソッドとかを駆使してうまいこと出来ないものか。


指摘等あればお気軽にじゃんじゃんください!