PHP5はimmutable不可能?
元々マニュアルで「PHP 5はオブジェクト指向言語ではありません。」と宣言しているのだから、仕方ないといえば仕方ないんですが。
- privateで宣言したメンバを守ってくれる。
- finalで宣言したクラスは拡張できない。
- privateコンストラクタ宣言可能
こういう実装なのにimmutableなクラスを作ることができない。
次のようなことをすると簡単にimmutableを破れたので開いた口がふさがらなくなってしまった。
例えば、こういうクラス宣言を書いたとすると。
<?php final class Immutable { private static $instance; private $value; private function __construct($value) { $this->value = $value; } public function getValue() { return $this->value; } public function equals(Immutable $obj) { return($obj->getValue() == $this->getValue()); } public static function getInstance() { if (Immutable::$instance === null) { Immutable::$instance = new Immutable('hoge'); } return Immutable::$instance; } } ?>
以下のようなスクリプトを実行すれば、
<?php $immutable = Immutable::getInstance(); print("生成直後の状態\r\n"); var_dump($immutable); /** 下のコメント文を実行すればエラーを起こす */ // $immutable->value = 'foo'; print("\r\n"); /** しかし下の文はエラーを起こさない */ $immutable->foo = 'bar'; print("未宣言メンバに値を入れ込んだ後の状態\r\n"); var_dump($immutable); ?>
このような結果になってしまった。*1
生成直後の状態 object(Immutable)#1 (1) { ["value:private"]=> string(4) "hoge" } 未宣言メンバに値を入れ込んだ後の状態 object(Immutable)#1 (2) { ["value:private"]=> string(4) "hoge" ["foo"]=> string(3) "bar" }
こういう事を実装されると、当然 == による同値性の保障は失われるわけで。
<?php $immutable1 = Immutable::getInstance(); $immutable2 = Immutable::getInstance(); $serialize = serialize($immutable1); $immutable3 = unserialize($serialize); var_dump($immutable1 == $immutable2); // return true; var_dump($immutable1 == $immutable3); // return true; var_dump($immutable1 === $immutable2); // return true; var_dump($immutable1 === $immutable3); // return false; var_dump($immutable1->equals($immutable2)); // return true; var_dump($immutable1->equals($immutable3)); // return true; print("\r\n"); $immutable1->foo = 'bar'; var_dump($immutable1 == $immutable2); // return true; var_dump($immutable1 == $immutable3); // return false; var_dump($immutable1 === $immutable2); // return true; var_dump($immutable1 === $immutable3); // return false; var_dump($immutable1->equals($immutable2)); // return true; var_dump($immutable1->equals($immutable3)); // return true; ?>
当たり前ですが、上にも書いたように == での同値性確認ではなく、常にequals()を実装するように心がけておけば、こんなことにはならないんですけれど、だからといってこういう実装もどうかと。*2
当然、PHP4からの移植性とかで残したものと思われるんですが、せめてfinal宣言のクラスでは不可能にしてほしかったな、と思うわけです。
まぁ、それはそれで言語としてややこしくなるので、どう考えてもいいことではないんですけど。