PHP5.4でコンストラクタを呼ばずにインスタンスが作れる

こんにちは。PHP5.4 ADVENTカレンダー13日目PHP5.4の新機能の一つ: アップロード進捗の取得に続いて14日目の担当のPaulです。

PHPはReflectionを使うことにより、privateとかprotectedとかのルールを無視して色々出来ます。
下はReflectionを使ってprivateメソッドを呼び出す例です。

class LockedDoor
{
    private function open()
    {
        return 'privateだけどドア開いちゃったよ!!';
    }
}

$refClass = new ReflectionClass('LockedDoor');
$refMethod = $refClass->getMethod('open');
$refMethod->setAccessible(true); //無理やりprivate解除
 //privateだけどobjectの外から実行できる
echo $refMethod->invoke($refClass->newInstance());

PHP5.4(Beta1)でReflectionClassにnewInstanceWithoutConstructorというメソッドが加わりました。
そうです!名前通りコンストラクタ使わずにインスタンスが作れるのです。

PHP5から__constructでコンストラクタが書ける様になりました。あれはあれでクラス名に依存しないので便利なんですけど、それのお陰でコピペのコンストラクタが増えたと思うのは僕だけでしょうか?
そういう問題には本来「親クラスを作って必要な処理はそこにまとめて、、、」やるべきなのでしょうが、「それが面倒だけど、コンストラクタの余分な処理をなんとかしたい!」って時に使えます。

例えば下の様なクラス

class MyClass
{
    private $lang;

    public function __construct() 
    {
        $this->lang = $_SERVER[HTTP_ACCEPT_LANGUAGE];
    }

    public function getLang() 
    {
        return $this->lang;
    }

    //意味ねーよ!
    public function setLang($lang) 
    {
        if ($this->lang == null) {
            $this->lang = $lang;
        }
    }
}

気持ちは分かるけどsetLangの意味がねーよ!と。

それもReflectionClass::newInstanceWithoutConstructorでなんとか出来ます。

$_SERVER[HTTP_ACCEPT_LANGUAGE] = 'ja';
$obj = new MyClass();
$obj->setLang('en');
echo $obj->getLang(), PHP_EOL; //jaのまま

$obj = (new ReflectionClass('MyClass'))->newInstanceWithoutConstructor();
$obj->setLang('en');
echo $obj->getLang(), PHP_EOL; //enと表示

個人的にはPHPは[スクリプト=コードが書き換えられる]ので、あまりReflectionに頼るのは如何なものかと思いますが、(Javaみたいにバイナリしかない場合はReflectionも必要だけど)
応急処置として頼りになるので、覚えておいて損はないと思います。

明日15日目は@srea2431さんです!お楽しみに!
あと、PHP5.4 ADVENTカレンダーまだまだ参加者募集中です!

traitでデザインパターン再考

こんにちは。php5.4adventカレンダーの10日目「PHP5.4でのhtmlspecialcharsの仕様変更とセキュリティ」を引き継いで11日目担当のPaulです。

PHP5.4で出てくるtraitこれのメリットとして、

  • mix-inができる。
  • あとでメソッド名再定義できるから、名前当たってもOK

みたいな事がRFCに書いてありましたが、

僕がRFCを眺めて感じたメリットは
親クラス作らなくても、実装の再利用が出来る!って事でした。

いや、ちゃんと親クラス作って、それを実装を追加したいクラスでextendして…
てやればタイプヒントの恩恵に預かれるのは分かってますが、、、
それをする余裕が無いことってありますヤン。
もちろんデリゲートも知ってますけど、あれはあれで追加のコード量が半端ないですヤン。
traitやったら、1個trait作っておけば、あとから実装追加したいクラスにuseなんちゃらって1行書くだけで済みますヤン。
それにもともと、タイプヒントでやってるクラスと、そうでないクラス両方に共通の実装ってなると何かと面倒でっしゃろ。

で、、、あとから実装を追加したくなるのってどのデザインパターンかなと思って
このサイトを眺めてたら

  • Decorator
  • Visitor
  • Observer

ぐらい?

この中で一番簡単そうなObserverパターンをtraitで書いて見ました。

まずはSubject trait

trait Subject {
    /**
     * Observer追加
     *
     * @param Observer $observer
     */
    public function attach(Observer $observer)
    {
        array_push($this->observers, $observer);
    }

    /**
     * SplSubjectと同じ名前で通知を定義
     */
    public function notify($funcName)
    {
        foreach ($this->observers as $key => $item) {
            $item->update([$this, $funcName]);
        }
    }
}

次はObserver

class Observer
{
    public function update(Callable $f)
    {
        $f();
    }
}
</code>

<p>最後にSubjectの実装</p>
<code>
class AKB 
{

    use Subject;                       //この2行を付け足すだけで
    private $observers = array(); //ObserverのSubjectになる。

    private $members = array();
    private $latest;

   
     /**
      * メンバーが追加される度に通知
      */
    public function addMember($member)
    {
        array_push($this-&gt;members, $member);
        $this-&gt;notify('getLatest');
    }
    
    /**
     * Observerで呼び出される。
     */
    public function getLatest()
    {
        echo $this-&gt;members[count($this-&gt;members) -1] . "が加わりました!" . PHP_EOL;
    }

}

実行すると

$o = new Observer();

$akb = new AKB();
$akb-&gt;attach($o);

$akb-&gt;addMember("前田敦子");
$akb-&gt;addMember("大島優子");
$akb-&gt;addMember("柏木由紀");
</code>
<p><br /></p>
<code>
"前田敦子が加わりました!"
"大島優子が加わりました!"
"柏木由紀が加わりました!"

僕的には作ったあと、「これ本当に便利になったかな?」って思ってしまいましたが、
既存のクラスに少し手を加えるだけ良いし、
例えばAKBクラスが親クラスを持っていたら今までは親クラスに変更を入れるかinterfaceをインプリメントするコードを書かなければいけなかったので、楽になったと思います!

次回12日目は@co3kさんです!お楽しみに

追記:12月12日 class AKBのところにバグが有ったので修正しました。

PHP5.4の新機能(その3)

1回目2回目だけではまだまだ書ききれないPHP5.4の新機能を紹介いたします。

1.配列のシンタックスシュガー

すっかり紹介を忘れてました。
他の言語のように配列の簡単な初期化がサポートされます。
(これを名前空間とか、無名関数の前にやってほしかったなぁ、、、)

$a = ['AKB', 'SKE', 'MNB'];
$b = ['チームA' => '倉持明日香', 'チームK' => '秋元才加', 'チームB' => '柏木由紀'];

そう、arrayとか書かなくて良いのですよ!  ”[]“だけで良いのですよ!!!

2.staticメソッドの可変呼び出し

波括弧を使いstaticメソッドの呼び出しを可変に出来ます。

class StaticClass {
public static $name = '峯岸みなみ';
public static function getName() {
return '峯岸みなみ';
}
}

$property = 'name';
echo StaticClass::${$property},PHP_EOL; //「峯岸みなみ」と表示される。(5.3でもOK)

$method = 'getName';
echo StaticClass::{$method}() ,PHP_EOL; //「峯岸みなみ」と表示される。(5.4からOK)

プロパティの呼び出しは5.3から{}が使えていたのですが、5.4からメソッドでも使える用になりました。

3.callableが型ヒントに使える

β1からタイプヒントにcallable型が使える様になりました。

//callable タイプヒント
function hi(callable $f) {
echo $f(), PHP_EOL;
}

class Human
{
public function hello() {
return "上からマリコ";
}
}

hi([new Human(), 'hello']); //「上からマリコ」が表示される。

$songName = function() {
return "Everyday カチューシャ";
};

hi($songName); //「Everyday カチューシャ」が表示される。

現在の実装として無名関数の親クラスはClojureでそれを型ヒントに使えるのですが、
将来変更される可能性があるのでより便利な型が追加されたようです。

4.コンストラクタからメソッドチェーンが実行可能

β2から追加された新機能です。

class Human
{
function __construct($name) {
$this->name = $name;
}

public function hello() {
return "Hi " . $this->name;
}
}

// 古い方法
$human = new Human("まゆゆ");
echo $human->hello();

// 新しい方法
echo (new Human("まゆゆ"))->hello();

phpは変数のスコープが他の言語と比べても広いので、
コンストラクタからメソッドチェーンや、配列をメソッドチェーンの様に利用
は変数の宣言を抑えて、名前の衝突を避けることができるので地味に嬉しいですね。