2012年2月2日木曜日

Windows 7 買ってVirtualPCでXPを立ち上げてみる

もう10年前のデスクトップが未だに必要なのはXPで納入したお客さんのサポートのため。今回こそは廃棄に回せるようWindows 7 Professionalの入ったノートブックを購入した。

◆まずは作業開始

ノートブックが来て最初にしたのは
avastというフリーのウィルス対策ソフト。
気が利いてることに一緒にChromeをインストールをしてくれる。

それからOSのアップデート。
半日近くかかったがダウンロードして再起動。
それを2~3回繰り返したら最新の状態になったようだ。

◆Virtual PC/XP Modeのインストール

スタート→全てのプログラム→Windows Virtual PCの
Windwos XP ModeをクリックするとMicrosoftのページが立ち上がる。

そこからXP Modeをダウンロードするんだけど、
RunしてOpenして・・・
どうにも動かない。

そうだ、Chromeだからだ。
IEで同じURLにアクセスしたら、なるほど、こういう意味だったのか。

Windows XP Modeをダウンロードして実行して、
しばらくするとXPが立ち上がった。パスワードを設定して・・・

あれ?Virtual PCはダウンロードしなくても動くようだ。

◆ファイルの共有のセットアップ

XPのコントロールパネルから
ネットワークセットアップウィザードを立ち上げて設定。
懐かしいねぇ。最初に見たときはすごいと思ったけど、今見ると面倒なだけ。OSも進歩している。

が、ファイルを共有してくれない。
XPの設定を色々いじくったけど、駄目だ。

これはホスト側のネットワークの設定を変更することで解決。
デフォルトでは「共有ネットワーク(NAT)」になっているので、
ここにホストPCのLANカードを指定して動きました。

◆ホストPCとのファイル共有が遅い

ホストPCからネットワークを開いて、VirtualXPの共有フォルダーへ、30MBほどのファイルをコピー。時間が30分とでた。何これ。遅い。

原因と対策はここに一緒に書いてありました。

しかし。
面倒だ。

別のPCからVirtualXPの共有フォルダーへコピーしたら、
早い。問題なし。

これなら使えそうだ。

何とかなりそうです。

AmidaMVC:アミダ式チェーンとChain of Responsibilityパターン

Amida式Chainのコードです。


function dispatch( $action, &$data=NULL ) {
    // set current action.
    $return = NULL;
    $this->_dispatchAct = $action;
    $this->setAction( $action );
    $this->fireStart();
    // -----------------------------
    // chain of responsibility loop.
    while( $this->moreModels() )     {
        $this->fireDispatch();
        $action = $this->getAction();
        $return = $this->execAction( $action, $data, $return );
        if( $this->useNextComponent() ) {
            // go to next component. 
            $this->nextComponent();
        }
        else {
            $this->useNextComponent( TRUE ); // reset to TRUE. 
        }
    }
    // -----------------------------
    return $return;
}


内部に$this->_componentsという配列にコンポーネントを登録しておいてwhile文でループしているだけです。

コンポーネントの扱い

nextComponent()でarray_sliceをして、
次のコンポーネントに進みます。つまり、後へは戻れません。

ただしコンポーネント内で別のコンポーネントを登録できます。
例:prependComponent( 'appHello', 'app' );

これで自分自身の次に実行するコンポーネントを追加します。
その他、チェーンの最後に追加する、チェーンを終了するする、指定したコンポーネントまでスキップする、などが出来ます(まだAPIで全てに対応はしてませんが)。

アクションの扱い

$actionはコンポーネント内で必要に応じて変更されます。それ以外は変わらず同じアクション(つまり同じ名前のメソード)を呼び続けます。

Chain of Responsibilityパターン

Amida式チェーンは、Chain of Responsibility (CoR)パターンの一種だと思ってます。WikipediaでのCoRのページを見てもよく分かりません。

自分の理解では、何かを達成するのに複数のオブジェクトを使う、複数のオブジェクトを使うためにAPIを共通にする、そして実行するオブジェクトの順番を適宜変更できるようにする、というパターンだと思ってます。

なのでCoRの実装方法はいろいろあると思います。
多分、LithiumというPHPのフレームワークは、各オブジェクト内で次に呼ぶオブジェクトを指定しているようです。リンクトリストみたいですね。

それに比べるとAmida式チェーンは比較的単純だと思います。実行するオブジェクト(あるいはクラス)を最初に配列で持っています。一方、途中でオブジェクトの順番を変えたり、呼び出すメソード名を変更できるのが特徴です。


2012年2月1日水曜日

AmidaMVC:appHelloを書いてみよう

今度はちゃんと俗に言うModelみたいな書き方を。

「demo/hello」フォルダーを作ります。
その下に「_App.php」ファイルを作って、



<?php
$_ctrl->prependComponent( 'appHello',  'app' );
class appHello extends \AmidaMVC\Component\Model
{
    static function actionDefault(
        \AmidaMVC\Framework\Controller $ctrl,
        \AmidaMVC\Component\SiteObj &$siteObj  )
    {
        $html = '<h1>Hello Application</h1>' .
            '<p>Hello World from Application</p>';
        $siteObj->setContents( $html );
    }
}

と書き込みます。

次のURLにアクセス。
http://your.host.here/pathto/AmidaMVC/demo/hello/

your.host.herepathtoは適宜環境に合わせてください。
すると、


と表示されます。

今度はたくさんのルールがあります。

_App.php

アプリケーションは_App.phpファイルにあるという規約になってます。

自分自身をコンポーネントとして登録

Componentとして自分自身をチェーンに登録する必要があります。
AmidaMVCのコントローラーが$_ctrlで取得できるので、prependComponentメソードを使って登録しています。

$_ctrl->prependComponent( 'appHello',  'app' );

ここにクラス名(appHello)を入れます。
この時点では、Loaderコンポーネント内で動いています。次に実行するコンポーネントとしてクラスを登録することになります。
正直、これは格好悪いと思いってます。クラス名を固定にしてコントローラー側で自動で読み込ませようかと思ったのですが、規約を増やすより、自分でコード書いた方がいいかな、と思って。というのは嘘で、ここでViewも読み込ませたいのですが、二つのクラスを上手に読み込ませるよい規約を思いつかなかったからです。

actionDefaultメソード

Loaderがファイルを読み込んだ後、appHelloクラスを実行します。
このURLだとアクションはデフォルトになります。なのでactionDefaultメソードが呼ばれることになります。

変数は3つあります。
  • $ctrl:AmidaMVCのコントローラーオブジェクト。
  • $siteObj:ウェブサイトに関するデータを収集するオブジェクト。
  • $data:前のコンポーネントから受け渡されるデータ(必要なければ無視)。

HTMLを返す

結果のHTMLは$siteObjに登録します。
これがRenderに運ばれて、テンプレートなどの処理を行います。

$siteObj->setContents( $html );


個人的な感想・・・

ちょっと書いてますが、もっと規約を増やして自動で処理する項目を増やしたほうがいいかもしれません。

  • たとえばRenderが受け取る$data(3番目の変数)は常にコンテンツにするとか。するとappあるいはviewメソードの最後にHTMLをreturnすればRenderが受け取ってコンテンツとして処理してくれるとかです。
  • それから、コンポーネントの登録を自動で行うとか。
  • _initメソードを前もって呼び出すとか。

フレームワークは作っていて楽しい理由は、ここら辺でしょうね。
やりすぎて使いにくくならないようにするのが難しいのかもしれません。

AmidaMVC:Hello Worldを書いてみよう

何はともあれ「Hello World」
一番簡単な方法からです。

マークダウンでHello World

AmidaMVCを落としてくるとdemoフォルダーがあります。
この下に「hello.md」というファイルを作ります。

ここに
#Hello World
Hello to AmidaMVC.  
と書きます。

そして次のURLにアクセス。
http://your.host.here/pathto/AmidaMVC/demo/hello.md

your.host.herepathtoは適宜環境に合わせてください。
すると、最初のHello World画面が出るはず。


demoフォルダーの下に置いたのは、AmidaMVC直下だとルータマップが効いてしまって直接ファイルを指定してもアクセスできないためです。
中身のマークダウンファイルをHTMLに変換しています。

PHPでHello World?

次は「hello.php」を作ってみましょう。
「demo/hello.php」ファイルを作って、

<?php
echo 'Hello World from AmidaMVC';
?>
と入力して、アクセス。すると、どうでしょう。
PHPのソースコードが出てきました。


面白いでしょう?

現状でPHPを走らせるには・・・

「demo/hello.html」ファイルを作って、こう書きます。
<h1>Hello World</h1>
<?php echo '<p>Hello World from Html</p>'; ?>
と入力して、アクセスすると・・・


ちゃんとPHPとして動きました。

スキャンモードで各種ファイルを表示

AmidaMVCは要求されたURLを元にファイルシステムをスキャンできます。
ファイルを見つけたら、拡張子から中身を推測、HTMLに変換して、テンプレートに放り込んだ上で表示します。

ルートマップもあります。ルートマップに引っかかったファイルを優先して読み込みます。

対応している拡張子は:
  • マークダウン(md、markdown)
  • HTML(html)
  • テキスト(text,txt)
  • PHP ソースコード (php)
があります。
ソースコードを表示する機能は、ちょっとgitHubみたいで気に入っているのですが、さすがに変すぎるので、PHPファイルなら実行して、HTMLとして表示する予定です。

2012年1月31日火曜日

AmidaMVC:ComponentとAction

AmidaMVCが何をしているのか?

一言で言えば、コンポーネント(部品)を次々と実行するだけ。
格好良くコンポーネントと言ってますが、要するにクラスです。

コンポーネントの登録

たとえば、こんなコードです。


// Controllerを作成、
$ctrl = new \AmidaMVC\Framework\Controller();
// コンポーネントを追加。
$ctrl
    ->addComponent( 'Config', 'config' )
    ->addComponent( 'Router', 'router' )
    ->addComponent( 'Loader', 'loader' )
    ->addComponent( 'Render', 'render' )
;
// フレームワーク実行
$data = new \AmidaMVC\Component\SiteObj();
$ctrl->start( $data );

最初にSiteObjとControllerのオブジェクトを作成してから、
コントローラーにコンポーネントを追加してゆきます。

これらのコンポーネントを順番に実行してゆきます。

クラスなので、実際に走らせるにはメソード名が必要です。
最初は「actionDefault」というメソードを呼び続けます。つまり正常に動いている場合の処理をこのメソード内に書きます。

上の例だと、
・Config:設定用クラス。必要そうな値を設定。
・Router:URLから読み込むファイルを決定する。
・Loader:見つけたファイルを読み込む。
・Render:テンプレートを呼び出してHTMLを出力する。
という処理を順番に行います。

アクション

実行するメソード名は、アクションと読んでいます。
実際はactionAction、最初に「action」をつけて呼び出します。
例えばactionHelloとかです。

もしアクションが存在しない場合。
デフォルトのアクション「Default」を呼び出します。
つまり「actionDefault」のメソードが呼ばれます。

エラー処理の例

途中でエラーが起きたとします。
たとえばRouterでファイルがみつからなかった場合について考えてみます。
  • Routerでファイルがみつからない。
    この場合、Routerはアクションを「_PageNotFound」に変えて、次のコンポーネントに処理を渡します。
  • これ以降、「_PageNotFound」が呼び出されます。無ければデフォルトのアクションを実行します。
  • さて、Loaderの「_PageNotFound」のアクションでは、設定で指定していたファイル(PageNotFound.md)を読み込みます。
    この時点で、表示に必要なデータはそろったことになるので、正常系に戻します。つまり「actionDefault」を呼ぶようにコントローラーに命令します。
  • Renderはファイルが読み込まれているので、正常系の処理と同じになります。
とまぁ、こんな感じで、エラーがあった場合に異常系の処理をして、処理が終われば正常系の処理に戻したりが出来ます。これでフレームワークの設計が簡単になるんじゃないかと思った訳です。

2012年1月30日月曜日

AmidaMVC:今更PHPマイクロフレームワーク作ってみた

ちょこちょこ書いてますが、PHPのmicroframework作ってみました。
今更の感覚が強いですが、
  • 勉強のため、
と思って作ってみました。

でも勉強のためだけで作ることは無かったですね。
実は
  • アミダ式Dispatcher(と勝手に命名)
というロジックを思いついてしまって。
それをMVCフレームワークに応用したら面白そうだなと思ったら、作ってみたくて仕方がなくなり作ったという次第です。

ウェブサイトを作っていて、面倒なのがエラー処理。
ページが見つからないなどの処理をきれいに行いたいと思ってました。

アミダ式の説明は後で行いますが、
標準の処理が簡単なだけでなく、エラーなどの異常系の処理も簡単にできるのではないかと思っています。
まぁ自分が初めて考えついたロジックとは思いませんし、誰かがMVCに応用していてもおかしくはない、というか実はこれがMVCの標準的な処理だった、なんてオチはないといいのですが。こういうのは広い知識を持ってないのがつらいところです。

◆特徴は?

せっかく作ったので、何か特徴があるはず…
  • モデルやビューが無くても動く。
    一つのファイルにPHPとHTMLが混在していても、ちゃんとテンプレート内に入るようになります。もちろんクラスを作っても大丈夫。
  • ルートを設定してもしなくても動く。
  • ファイルビューワーとして、マークダウンファイルやテキストファイルをHTMLとして表示することが出来ます。PHPファイルもソースコードとして表示できたりします。
  • ファイルシステムがベース
    特徴なのかどうか分かりませんがデータベースは必要ありません。遅いと思いがちなファイルですが、今後SSDが普及するにつれ十分なスピードが出るようになるのではないでしょうか?すると簡単にファイルで作れるフレームワークも便利かなぁと。
その他、ファイルを直接修正したりすることも(多分簡単に)出来るはずなのですが、こうなるとフレームワークなのかCMSなのか分からなくなるので、適当なところで切り上げないと。

2012年1月17日火曜日

クロージャーを使ったPSR-0準拠のクラスローダー

PSR-0をベースにしたクラスローダーを探したのですが、全てクラスベースのクラスローダーしか見つかりませんでした。でコピーするのが面倒だったので、クロージャーを使ったクラスローダー作ってみました。
こういうのにクロージャーって便利なんですね。
初めてクロージャーをちゃんと使った気がする。
ソースコードはgitHubのGistにて公開しました。
https://gist.github.com/1620960#file_class_loader_ns.php


特徴は、パラメータ無しで動きます。

◆使い方は、たとえば次のようなクラスをロードする場合。
new AmidaMVC\Folder\SomeClass();

対応するクラスのが次の場所だったとします。
pathto/src/AmidaMVC/Folder/SomeClass.class

こういう場合は、pathto/src/AmidaMVC/bootstrap.php
などに上のGistコードを貼り付けてください。

spl_autoload_register( ClassLoader() );
普通、多分、他のコードを見る感じだとクラスをロードするには、ライブラリで使っているネームスペースとパスを指定する必要があります。
この関数は、呼ばれた場所がライブラリの一番上と仮定して、現在のフォルダーをネームスペース(例の場合はAmidaMVC)、そして現在のフォルダーをのぞいたパス(例の場合はpathto/src/)を設定します。

◆ただし、関数の定義が別ファイルにある場合は、先のbootstrap.phpで

spl_autoload_register( ClassLoader( __DIR__ ) );

とすれば動く「はず」です(動作は未確認)。

◆注意

今コードを見たら、バグがありますね。
少なくとも自分の環境では動いているので、ちょっと修正すれば動くと思います。また時間を見つけたら修正します。