2011年12月14日水曜日

コンポーネント指向について調べてみた

イベント駆動から始まって、次はコンポーネント指向について調べてみました。

いつものようにWikipediaでコンポーネント指向を見ると、まったくわけが分かりません。いや、部品から組み立てようということらしいんですが、それって当たり前だし、ほとんど全てのフレームワークはコンポーネント指向でしょう。

要はGUIのあれと同じ・・・

困って別のサイトを見てたら、いい解説がありました。
全然別のフレームワークの紹介なのですが、上手に説明されていたので、そのまま抜き出します。
コンポーネント指向のフレームワークでは,リクエストごとではなく,コンポーネントが発火するイベントに対するハンドラとして処理を記述します。これは既存のGUIアプリケーションと同様のモデルです。
http://itpro.nikkeibp.co.jp/article/COLUMN/20070305/263875/
既存のGUIアプリと同じモデルだそうです。
あれ、イベント駆動についての最初の例と同じだ。

つまりコンポーネント指向とは、
・ウェブの画面上の各要素に対して、
・サーバー上のクラスを一対一に対応付けて処理する、
という設計方針という意味だと理解しました。

一方、イベント駆動とは、コンポーネント指向を実現するための実装技術なのでしょう。

Yiiのコンポーネントについて想像をめぐらす

YiiのウェブサイトにComponentについての説明があります。コンポーネントとは、パブリックなプロパティを持って、イベントを発生・処理できるとあります。

いまひとつよく分かりませんが、Yii Blog Demoを見ていて、面白いフォーム構造をしているのに気がつきました。

新規ポストの登録で、タイトルの名称は
<input type="text" name="Post[title]" />
となってます。

残念ながらデモのソースコードが見つからなかったので、想像ですが、おそらくPostというコンポーネントクラスが存在するのでしょう。新規投稿をすると、何らかのイベントが発生してPostコンポーネントを呼び出して、titleというプロパティに値を設定して、DBに保存する、という流れになっているのではないでしょうか。

ちなみにコメントの追加では、名前のところは
<input type="text" name="Comment[author]" />
です。

ポスト先を見ると、
http://www.yiiframework.com/demos/blog/index.php/post/1/Welcome%21
Commentという文字がURI内に見当たりません。
どうやってCommentコンポーネントに処理を渡しているのか・・・
単にURIを解析して対応するコンポーネントを決定しているわけではなさそうです。

バインディング

最後に・・・

このように、フォームや画面上に表示された要素と、バックエンド側での処理クラスを関係付けることをデータバインディングと呼ぶようです。

最初にこの言葉を聞いたのがJavaServer Faces(JSF)でした。
調べてみるとWindowsなど様々な場所で使われてる技術・言葉みたいですね。

リクエストを処理→テンプレートで表示、というウェブの作りとはちょっと違っていて面白いですね。

Underscore.phpとarryと_wwの速度比較してみた

PHP Advent jp 2011で書いた_ww(Underscore-Walla-Walla)ですが、せっかくなので他の実装と速度比較してみました。

比較対象は、実装の元ネタの__と、同じくPHP Adventで発表されたarryです。

また比較用のコードとデータはarryで使われていた「悲しき今シーズンの結果」を利用させてもらいました。ありがとうございました。


まずはリファクタリングから

が、利用するにはsortがないし、クラスは二つに分かれてるし。
最初は_wwのリファクタリングから始めました。

まずはgitでブランチを切って、開発開始です。
次にテストクラスを作成してから、さくっとクラスをマージ。
簡単に出来ました。テストがあると、変更するのに気が楽ですね。

速度比較について予測してみる

測定する前に予測を立てます。

まずは、自分の_wwが一番遅いと予想。
理由はマジックメソードを使っているので。

一番早いのはShinさんのarryクラス。
最初からチェーンするようにメソードが組み立てられているので、一番早いはず。

すると中間は、Underscore.php。
これもマジックメソード使ってないけれど、各メソード内での処理があるので、チェーンすると少し処理が増えると予想。

速度比較の結果

ソースコードはgistで公開してあります。
インストールですが、どこかのフォルダーでgit bashを開始して、
git clone git://gist.github.com/1460352.git gist-1460352
git clone git://github.com/shin1x1/arry.git
git clone git://github.com/brianhaveri/Underscore.php.git
git clone git://github.com/asaokamei/_ww.git
と打ち込んで、からgist-1460352内のcompare.phpを走らせてください。
測定ですが、Core2Duo 2.13GHzのWindows Vistaでの結果です。

2.2504210472107  // 素のPHP
5.0997409820557  // arry
9.1629009246826  // Underscore.php
7.959762096405  // _ww

予想通りarryが一番早かったです。
素のPHPに比べて2倍強になりました。結構、速い気がします。

意外だったのがUnderscore.phpより_wwのほうが速かったこと。
何度か走らせると、値が結構変わるので「ほんの少し速い」だけですが、各メソード内での処理が意外と重いのでしょう。

PHPのプロファイリング

そういえばプロファイリング(速度の測定)なんて何年振りです。
PHPだとxdebugを使えば出来るようですが、今回は簡単な関数を自作して計ることにしました。最近使い始めたNetBeansから直接測定結果が見られると便利なんですけどね。

2011年12月13日火曜日

Pradoのイベント駆動を想像してみる

PradoというPHPフレームワークのイベント駆動がどうなっているのか、Pradoのホームページを眺めているうちに、何となく想像がついてきました。

ただし、ソースコードは一切読まずの想像ですので、間違ってるかもですが。

QuickStartサンプルから

単純なラジオボタンのデモですが、このページからソースコードを眺めると、

  • Home.pageの97行目:
    <com:TButton Text="Submit" OnClick="selectRadioButton" />
  • Home.phpの10行目:
    public function selectRadioButton($sender,$param)

とあって、サブミットボタンがクリックされると「selectRadioButton」というイベントが発生して、HomeクラスのselectRadioButtonメソードに処理を渡しているようです。

なるほど、Pradoのいうイベント駆動は、まさGUIでのイベント駆動に近いようです。JavaScriptを使ってHtml上のボタンなどにイベントを発生させて、サーバー側のPHPクラス・メソードに結び付けているのでしょう。

Yiiはイベント駆動ではないのか?

さて、Pradoは「終了?」したのか、
新しい「Yii」というフレームワークの開発を始めているということです。

Yiiの特徴は「コンポーネント指向」

これまた新しい言葉が・・・
いや、よく見たらPradoでも同じ言葉を使ってました。

ともあれ、Yiiはイベント駆動をやめたのでしょうかね?

これまた想像ですが、Restが優勢になってイベント駆動というパラダイムが分かりにくくなったからではないでしょうか?

要するにリクエストがあった場合に、どのクラスのメソードに処理をさせるのかを決定できればいいわけで、RestならURIだけで決定できます。ボタンクリックでわざわざイベントを発生させるのは無駄な気がします。

さ、次は「コンポーネント指向」について調べてみよう。

2011年12月8日木曜日

PHPでイベント駆動(event driven)って何だろう?

PHPのPradoというフレームワークがイベント駆動で、すごいらしい。
で、イベント駆動って何だろう?と思って、ちょっと調べてみた。

オブザーバーパターン

イベント駆動というと、jQueryを使って、あるDOM要素にクリックしたら~~する、というのが、まさによい例だそうです。
$( '#button' ).click( function() { alert( 'hi' );});

が、一般的にオブザーバーパターンというらしい。

しかし、イベント駆動のPHPフレームワークと言われても、サーバー側でどうイベントを利用するのかぴんと来ません。

こういうときは、自分で書いてみる。


Stackoverflowで見つけた「Event-driven architecture and hooks in PHP」を参考に、自分で書いてみました。

イベント駆動というよりは、「dumb hook」あるいは「単なるdispatcher」と書いてあるように、非常に簡単なものです。

ともかく、

eventHookというクラス(github)が出来ました。。

最初にイベント名に対して実行する関数(クロージャーでも何でも)を登録します。
eventHook::hook( 'some_event', 
  function() {  echo 'do something'; });
イベント発行は、イベントメソードを叩きます。
eventHook::event( 'some_event' );
簡単だなぁ。

でも、イベント実行時に必要な情報はどうやって渡すんだろう?

そのとき、例に挙げられてた通信バッファーを思い出した。
通信を受け取ると、(OSが?)受け取った内容をメモリ上のバッファーに書き込み始めます。オブザーバーはバッファーを監視していて、バッファーの状態が変わるとイベントを発行、関連付けられてた何かを実行します。当然バッファーに必要な情報が入っている「はず」です。

つまりイベントとは観察している対象が変わることであり、対象自体に情報があるはずです。

eventHookクラスの場合は、eventメソードのイベント名はあくまでバッファーの場所(の代わり)であり、バッファー内の情報はeventメソードと一緒に渡せばいいわけです。
説明すると難しいですが、コードにすると簡単。
eventHook::hook( 'more_event', 
  function( $arg1=NULL, $args=NULL ) {
    echo "doSomething: '$arg1', '$args'\n";
});
eventHook::event( 'more_event', 'arg#1', 'arg#2' );
// 出力:doSomething: 'arg#1', 'arg#2'
単にeventメソードに必要そうなデータを渡せば、実行関数にも同じものを渡します。
うん?実行関数で生成された何か(プライマリキーとか)受け取りたい場合はどうするんだろう?別のイベントを発行してもらうのかな???やばそうな匂いがするなぁ。

で、イベント駆動って何?

未だに肝心の「イベント駆動」がよく分かりません。
多分、イベントを連続して発行することでフレームワークの動きを実装しているのでしょう。MVCの代わりみたいなイメージなのかな。

そういえばワードプレスにもフックがたくさんあると聞いたことが・・・
あぁ、こういうことか。

先は長いなぁ。

2011年12月6日火曜日

PHPからlocalStorageに書き込み・読み込み(PHP Advent JP 2011)

PHP Advent Calendar jp 2011」6日目(12月6日)のエントリーです。

初めてアドベントカレンダーに参加したのですが、いきなり内容が被ってしまいました。そのまま公開しようと思ってたのですが、急遽ネタを思いついたので、早速作ってみました。

ちなみに最初のネタは_ww(アンダースコア・ワラワラ)というクラスです。

PHPからローカルストレージを使う

HTML5とスマートフォンが登場してから、クライアント(ブラウザー側)の技術が急速に進化していると感じます。今までのPHPでサーバー側でHTMLを構築するとは違ったウェブの構築になるのではないでしょうか。

という前振りで、HTML5で登場したlocalStorageをPHPからを操作するクラスを作ってみました。

ソースコードはgithubのphpadventjp2011で公開してあります。
localStorageクラスとサンプルが入っています。

一応、妙なスクリプトを入れられても大丈夫なはずですが・・・
セキュリティに関しては自己責任でお願いします。

基本的な使い方

基本的な考え方は
  1. PHPの変数をlocalStorageに保存するJavaScriptを自動生成。これをHTMLのどこかに貼り付けると、ブラウザーにデータを保存。
  2. ブラウザーのlocalStorageから値を読み出て隠しタグに変換するJavaScriptを自動生成。これをHTMLのフォーム内に貼り付けて、サブミットしてもらう。
  3. フォームデータを受け取るときに隠しタグから値を取得。
です。

ローカルストレージに保存してみる

PHPコードです。

require_once './localStorage.php';
$data = array( 'test'=>'test', 'more'=>'more' );
$js = localStorage::saveStorage( $data, 'testID' );
$js2 = localStorage::saveStorage( 'just value', 'valID' );
この$jsや$js2をHTML内で出力すると、$dataの中身をブラウザーに保存します。

ちなみに生成されたJavaScriptです。
PHP変数をJSONに変換して、localStorageに保存しています。
<script>localStorage.setItem( 
'saveID_testID', '{"test":"test","more":"more"}' );
</script>
この後、クライアント側で保存した値を使えるようになります。

PHPで受け取ってみる

面倒ですが、まずはフォームが必要です。そこで次のPHPコードを走らせてJavaScriptを生成します。
require_once './localStorage.php'; 
$js = localStorage::loadStorage( 'testID' );
$js2 = localStorage::loadStorage( 'valID' );
保存する際につけた名前(「testID」や「valID」)を指定します。

生成されたJavaScriptです。
これをフォームのどこかに出力しておきます。
<input type="hidden" name="saveID_testID" id="saveID_testID">
<input type="hidden" name="saveID_testID_EncType_" value="json">
<script>
 document.getElementById( 'saveID_testID' ).value =
 localStorage.getItem( 'saveID_testID' );
</script>

<input type="hidden" name="saveID_valID" id="saveID_valID">
<input type="hidden" name="saveID_valID_EncType_" value="json">
<script>
document.getElementById( 'saveID_valID' ).value =
localStorage.getItem( 'saveID_valID' );
</script>

PHPで受け取ってみる(パート2)

こんなPHPコードで受け取れます。
require_once './localStorage.php'; 
$data = localStorage::loadPost( 'testID' ); 
$val = localStorage::loadPost( 'valID' );
保存する際につけた名前(「testID」や「valID」)を指定してください。

保存したままの値が入ってくる(はず)です。

使い方を考えてみる

勢いで作ったクラスですが、どう使うか考えてみました。
意外と使いどころがないw

フォームに一旦出力するので使い勝手が悪い気がします。サーバーだけで使うデータならセッション使ったほうが楽でしょう。ただクライアント側のJavaScriptと変数を共有したい場合に便利かもと思ってます。

Underscore-Walla-WallaというPHPクラスを作ってみた

初めてアドベントカレンダーに参加したのですが、いきなり内容が被ってしまいました。別のトピックを考えたのですが、思いつかず。まぁ実装が違うので、このままで参加することにしました。別ネタを思いついたので、そちらを書きました。

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ライセンスです。

コードをちゃんと見ると、テストコードが入り込んでる、クラスが二つに分かれた、とか問題はたくさんあるのですが、ひとまず動いたので今回はここで完了としました。

よく「車輪の再発明」とか言われますが、こういう小さなクラスを書くのはとても楽しいです。実際に使うとか考えると微妙ですが、細かいことは気にしないで作ってみると楽しいと思います。

2011年12月1日木曜日

Synergyで「failed to save autostart configuration」エラーが出る

Windows VistaでSynergyを1.3.1から1.3.8に
アップグレードして、「Start」ボタンを押すと

failed to save autostart configuration

というエラーメッセージが出て動きませんでした。

◆解決方法:


このページにて発見。
http://code.google.com/p/synergy-plus/issues/detail?id=256
まぁグーグルの一番最初のエントリですが。

まず、synergy.exeを「管理者として」走らせます。
自分のPCだと以下のフォルダーにありました。
C:\Program Files\Synergy\synergy.exe

次に、Optionsの中の「AutoStart...」ボタンを押す。

「Uninstall」を押して、一旦オートスタートをリセット。
OKを何度か押す。

これで解決しました。

例によって、管理者権限の問題のようですね。