へろへろもへじ

(ブログタイトル募集中)

【PHP】PHPでタイプセーフなプログラミングを『タイプヒンティング』 + 『Enum』で実現してみた

どうも、PHP歴5ヶ月の僕です。

世間的にどのくらい使われているのかわかりませんが、皆様『タイプヒンティング』、使っていますか?
今回はタイプヒンティングと、Javaでお世話になっていたEnumを利用してPHPでタイプセーフなプログラミングにチャレンジしてみようと思います。ただ残念ながら、現状PHP(最新版の5.5でも)ではEnumはサポートされておらず、独自で実装する必要があったので、自前で用意してみました。(既に公開されていた方のソースを参考に、traitにしてみました。)

Enum.php

<?php

namespace core;

/**
 * Enumを実現するためのtrai
- 
+ t
 */
trait Enum
{
    /** 生成したEnumを保持する配列 */
    public static $values = array();

    /** 名称 */
    public $name;

    /** 実値 */
    public $value;

    /**
     * コンストラクタ
     * 
     * @param string $name  名称
     * @param mixed  $value 実値
     */
    protected function __construct($name, $value)
    {
        $this->name = $name;
        $this->value = $value;
        self::$values[] = $this;
    }

    /**
     * 実値と等価のEnumオブジェクトを返します。見つからない場合、nullを返します。
     * 
     * @return mixed Enumオブジェクト
     */
    public static function ofSafe($value)
    {
        foreach (self::$values as $enum) {
            if ($value === $enum->value) {
                return $enum;
            }
        }

        return null;
    }

    /**
     * 実値と等価のEnumオブジェクトを返します。見つからない場合、Exceptionをthrowします。
     * 
     * @return mixed Enumオブジェクト
     */
    public static function of($value)
    {
       $result = self::ofSafe($value);
       if (is_null($result)) {
            throw new \Exception("IleegalArgument. value=$value");
       }

       return $result;
    }

    /**
     * オブジェクトの情報を文字列として返します。
     * 
     * @return string オブジェクトの情報を文字列
     */
    public function __toString()
    {
        $result = get_class($this) . ':';
        $result .= 'name=';
        $result .= $this->name . ',';
        $result .= 'value=';
        $result .= $this->value;

        return $result;
    }
}

上記のtraitを組み込んで、Enumを実現します。
例として、属性を表す列挙型を作ってみます。

■ AttrType.php

<?php

namespace constant;

/**
 *  属性を表す列挙型です。
 */
class AttrType
{
    // traitを組み込む
    use \core\Enum;

    /** 火 */
    public static $FIRE;

    /** 水 */
    public static $WATER;

    /** 木 */
    public static $WOOD;

    /** 光 */
    public static $SHINE;

    /** 闇 */
    public static $DARK;

    /**
     * 初期化処理
     */
    public static function _initialize()
    {
        static::$FIRE = new AttrType('FIRE', 1);
        static::$WATER = new AttrType('WATER', 2);
        static::$WOOD = new AttrType('WOOD', 3);
        static::$SHINE = new AttrType('SHINE', 4);
        static::$DARK = new AttrType('DARK', 5);
    }
}

// 初期化実行
AttrType::_initialize();

では、AttrTypeクラスを使ってみます。

<?php 

require 'Enum.php';
require 'AttrType.php';

use constant\AttrType as AttrType;

// プロパティの参照
var_dump(AttrType::$FIRE);
// プロパティ名称の参照
var_dump(AttrType::$FIRE->name);
// プロパティの実値参照
var_dump(AttrType::$WATER->value);
// 全プロパティの参照
var_dump(AttrType::values());
// Enumへの変換(該当なしでnull)
var_dump(AttrType::ofSafe(10));
// Enumへの変換
var_dump(AttrType::of(3));
// __toString()
echo AttrType::$DARK . PHP_EOL;
// Enumへの変換(該当なしでException)
var_dump(AttrType::of(10));

これでタイプセーフなプログラミングをする準備ができました。
例えば、下記のようなセッターメソッドの引数に「AttrType」を指定してみます。

<?php 

namespace entity;

use constant\AttrType as AttrType;

class MonsterMaster
{
    public function setAttrType(AttrType $attrType)
    {
        $this->attr_type = $attrType;

        return $this;
    }

これでPHPでタイプセーフなプログラミングが可能になったどー!

※冒頭にも書いてますが、traitを使っているので5.4以降での実装となります。
5.3の場合はtraitをabstractなクラスにして継承する + リフレクションを利用することで実現できます。

参考サイト