phpで無理やりEnum

タイトルのとおり。
phpでもjavaEnumっぽいの使いたい、何とかできないかしら…。
と思っていろいろ考えてみました。

目標

<?php
ItemType::$NORMAL;  //アイテムタイプ : 通常
ItemType::$SPECIAL; //アイテムタイプ : 特別
ItemType::$LIMITED; //アイテムタイプ : 限定品 

foreach(ItemType::values() as $itemType){
  echo "コード値 : {$itemType->getCode()} 表記名 : {$itemType->getDescription()}\n";
}

こんな雰囲気で使いたい。
(タイプにどんな属性を与えられるかは自由に決められる。)

という訳で書いてみたのが以下のコード


Enum基底クラス
<?php
class Enum {

  /** 項目を記録しとくプロパティ */
  protected static $enums = array();
  
  /** $enumsに項目を追加するメソッド */
  protected static function add(){

    // 追加される項目げっと (可変長引数)
    $addingEnums = func_get_args();

    // このメソッドが呼び出されたクラスの名前をげっと
    $calledClass = get_called_class();

    // Enumを継承したクラスごとに項目データを保存
    self::$enums[$calledClass] = $addingEnums;
  }
  
  /** このメソッドを呼び出したクラスの項目として設定されているものを取得 */
  public static function values(){
    $calledClass = get_called_class();
    return self::$enums[$calledClass];
  }
}
ItemTypeクラス
<?php
class Item extends Enum {

  /** 商品タイプ : 通常商品 */
  public static $NORMAL;

  /** 商品タイプ : 特別商品 */
  public static $SPECIAL;

  /** 商品タイプ : 限定商品 */
  public static $LIMITED;

  /** コード値 */
  private $code;

  /** 記述 */
  private $description;

  /** コンストラクタ : 商品タイプの初期化 */
  private function __construct($code, $description){

    $this->code = $code;
    $this->description = $description;
  }

  public static function setup(){

    //各クラスプロパティに商品タイプのインスタンスをセットアップして
    //valuesで使えるように基底クラスに保存する
    parent::add(
      self::$NORMAL  = new ItemType("1","通常商品"),
      self::$SPECIAL = new ItemType("2","特別商品"),
      self::$LIMITED = new ItemType("3","限定商品")
    );
  }

  /** コード値getter */
  public function getCode(){
    return $this->code;
  }

  /** 記述文字列getter */
  public function getDescription(){
    return $this->description;
  }
}
ItemType::setup(); //←これを実行しないと動かない
実行
<?php
foreach(ItemType::values() as $itemType){
  echo "コード値 : {$itemType->getCode()} 表記名 : {$itemType->getDescription()}\n";
}

echo ItemType::$NORMAL->getDescription() . "\n";
問題点
  • 値変えられるよ!
    • (結局ただのpublic static プロパティなので)
  • ::setup()を必ず実行する必要があるよ!
    • (静的プロパティにどうにかして自動で自身のインスタンスを設定できないかしら…。)


なかなかうまくいかないもんですよね。
ちなみに PHP 5.3 以上じゃないと動きません。

2011-07-12 追記

結局業務ではこんな感じで落ち着きました。

enumクラス
<?php
class ItemType {

  /** @var int コード値 */
  private $code;

  /** @var string 記述 */
  private $description;

  /**
   * コンストラクタ
   * @param int $code コード値
   * @param string $description 記述
   */
  private function __construct($code, $description){
    $this->code = $code;
    $this->description = $description;
  }

  /**
   * @return int コード値
   */
  public function getCode(){
    return $this->code;
  }

  /**
   * @return string 記述
   */
  public function getDescription(){
    return $this->description;
  }

  /**
   * @return boolean 等価かどうか
   */
  public function equals(ItemType $itemType){
     return $this->getCode() === $itemType->getCode();
  }

  /**
   * 通常商品
   */
  public static function NORMAL(){
    return new self(1, "通常商品");
  }

  /**
   * 特別商品
   */
  public static function SPECIAL(){
    return new self(2, "特別商品");
  }

  /**
   * 限定商品
   */
  public static function LIMITED(){
    return new self(3, "限定商品");
  }
}
実行
<?php

$normal = ItemType::NORMAL();
$special = ItemType::SPECIAL();

if($normal->equals($special)){
  echo "同じだよ";
}else{
  echo "違うよ";
}

値を変えられる点と::setup()を実行しなきゃいけない点はクリアになったけれど
バリエーションを増やすごとにメソッド増やさなきゃいけないのが面倒くさい…ぐぬぬ