◆Underscore.phpという面白いクラスが紹介されてました。
なんでもはunderscoreというjavaScriptをPHPにポートしたものだそうです。何が面白いって、
jQueryみたいにPHPコードが書ける。
早速ソースコードを追いかけてみたのですが、すぐ理解できなかったので自分で書き直してみました。なるほど、分かってしまえば、単純なことを丁寧に積み重ねていたんですね。
理解のために書いたコードですが、せっかくなので、衆目の下に晒すことにしました。クラス名は_ww。アンダースコア・ワラワラと読んでください。
ちなみにWalla Wallaはアメリカ合衆国はワシントン州にある小さな町です。昔、あの近くのもっと辺鄙な場所で学生してたことがあったので、名前に使ってみました。
◆簡単な使い方。
まずは一番簡単なスタティック・メソードで呼ぶ方法。
// use as static method. $ww = _ww::each( array( 1,2,3 ), function($n) { echo $n . "\n"; } );実際のeachの中身は、_wwmというクラス内で定義されています。
class _wwm { static public function each( $collection, $iterator ) { if( !empty( $collection ) ) foreach($collection as $k=>$v) { call_user_func($iterator, $v, $k, $collection); } } }$collectionが配列で、foreach文を使って各要素について$iterator関数を呼んでいます。
これだと、普通なPHPといった感じです。
次はメソードチェーンをしてみます。
◆チェーンで次々と処理する
eachとかをつなぎ合わせてチェーンのように使えます。
// use as a chain of methods. _ww::chain( array( 1,2,3 ) ) ->each( function($n) { echo "in chain $n" . "\n"; } ) ->get( $mid ) ->map( function($n) { return $n * 2; } ) ->value( $result ); var_dump( $mid ); var_dump( $result );ポイントは、
- chainメソードで始めること。
- valueメソードでチェーンを終了して、結果を受け取ること。
- チェーンの途中で値を使いたい場合はgetメソードが使うこと。
といった所でしょうか。
◆チェーンの仕方を理解する
一体どうなっているか、_wwクラスの中を少し見てみます。
class _ww { private $_wrapped = NULL; // holds the data public function __construct( $collection ) { $this->_wrapped = (array) $collection; } public static function chain( $collection ) { return new _ww( $collection ); } }chainメソード内で、自分をnewしてます。いや、自分自身のインスタンスを作成してます。入力された配列はオブジェクト内の$_wrappedという変数に保存されます。
で、作ったオブジェクトをすぐにreturnしてます。
これでチェーンが始まるというわけです。
◆メソードの呼び出し方は?
次はeachを呼ぶのですが、_ww自体にはeachなどのメソードは実装されてません。その代わりに「magicメソード」と呼ばれる__callメソードが呼ばれます。
class _ww { public function value( &$result=NULL ) { $result = $this->_wrapped; return $this->_wrapped; } public function __call( $method, $args ) { $arg = array_merge( array( $this->_wrapped ), $input ); $return = call_user_func_array( "_wwm::$method", $arg ); if( !is_null( $return ) ) { $this->_wrapped = $return; } return $this; } }この中で、最初に$_wrappedに保存しておいた配列を呼び出して、残りの引数と一緒にして、実際のeachメソードを呼び出しています(call_user_func_array( "_wwm::$method", $arg )の部分)。
ここらへんがShinさんのarryクラスと実装が違うところです。あちらはeachとかが最初からチェーン内で使われることを想定して書かれてます。一方、元ネタのUnderscore.phpは書くメソードがチェーン内にいるかどうかを判定して、引数と返値を制御しています。ちなみに、_wwはスタティックはチェーン外、インスタンスの場合はチェーン内と割り切って、__callマジックメソードで引数などを作り直しています。メソードの最後にreturn $thisをして次のチェーンへつなぎます。
この後のmapメソードも、同じように処理されます。ただmapだと返り値があるので、$_wrappedの値が変わることになります。
こうして次から次にチェーンにしたがって、$_wrappedに入っている配列に対して処理を行ってゆくことが出来ます。
◆チェーン部分の出力です
in chain 1 // each内で出力 in chain 2 in chain 3 array(3) { // $midのvar_dump [0]=> int(1) [1]=> int(2) [2]=> int(3) } array(3) { // $resultのvar_dump [0]=> int(2) [1]=> int(4) [2]=> int(6) }
◆最後に
_wwのソースコードはgithubでホスティングしています。
ライセンスはMITライセンスです。
コードをちゃんと見ると、テストコードが入り込んでる、クラスが二つに分かれた、とか問題はたくさんあるのですが、ひとまず動いたので今回はここで完了としました。
よく「車輪の再発明」とか言われますが、こういう小さなクラスを書くのはとても楽しいです。実際に使うとか考えると微妙ですが、細かいことは気にしないで作ってみると楽しいと思います。
0 件のコメント:
コメントを投稿