目次

PHPでEnumを使う

VBではおなじみのEnumをPHPで使えるように、クラスを作ってみる。

まずは定数としてEnumを定義できるクラス。

定数としてEnumを定義できるクラス

やはりEnumは定数っぽく使いたい。

<?php
class Enum {
	const IS_NOT_MEMBER = -1;
	private static $_values = array(null);	// 0番目は間違いの元になるので使わない
 
	public static function define($group, array $members) {
		// グループ名をstatic変数に追加&定数として定義
		$num = array_push(self::$_values, self::IS_NOT_MEMBER) - 1;
		define($group, $num);
 
		// グループ内の要素をstatic変数に追加&定数として定義
		foreach ($members as $member) {
			define($group . '_' . $member, array_push(self::$_values, $num) - 1);
		}
	}
 
	public static function typeof($memberNum) {
		return self::$_values[$memberNum];
	}
}

動作確認

// Enumを定義
Enum::define('ANIMAL', array('DOG', 'CAT'));
Enum::define('FOOD', array('BANANA'));
 
// 引数の種類を判定する
function test($var) {
	if (Enum::typeof($var) === ANIMAL) {	// このグループに含まれるか?
		if ($var === ANIMAL_DOG) {
			echo 'dog.';
		} else {
			echo 'not dog.';
		}
	} else {
		echo 'not animal.';
	}
}
 
// 実行結果
test(ANIMAL_DOG);	// => 'dog.'
test(ANIMAL_CAT);	// => 'not dog.'
test(FOOD_BANANA);	// => 'not animal.'
test(ANIMAL_DAG);	// 未定義の場合 => 'not animal.'

Type Safeでないのが残念。

Enumなら引数で型チェックしたい。

Type SafeなEnumを定義するクラス

<?php
class Enum {
	public static function define($group, array $members) {
		// グループ名をinterfaceとして宣言
		if (!interface_exists($group, false)) {
			eval("interface $group {}");
		}
 
		// グループ内の要素をclassとして宣言
		foreach ($members as $member) {
			if (!class_exists($member, false)) {
				eval("class $member extends Enum implements $group {}");
			}
		}
	}
 
	public function is($type) {
		if (!class_exists($type, false) && !interface_exists($type, false)) {
			throw new Exception("$type is not defined");
		}
 
		return $this instanceof $type;
	}
}

動作確認

// Enumを定義
Enum::define('Animal', array('Dog', 'Cat'));
Enum::define('Food', array('Banana'));
 
// 引数がDogか判定する
function test(ANIMAL $var) {
	if ($var->is('Dog')) {	// switchの場合はis()は使えないので get_class($var) で
		echo 'dog.';
	} else {
		echo 'not dog.';
	}
}
 
// 実行結果
test(new Dog());	// => 'dog.'
test(new Cat());	// => 'not dog.'
//test(new Banana());	// ANIMALでない(引数の型不一致エラー) => Catchable fatal error
//test(new Dag());	// 未定義の場合(class不明エラー) => Fatal error
 
// グループに属するか判定する
$banana = new Banana();
if ($banana->is('Food')) {	// グループに属していればtrueになる
	echo 'food.';
} else {
	echo 'not food.';
}

引数の型チェックができる。

でもEnumを定数として使えないのが残念。