2012年2月25日土曜日

Simple@高円寺(2012/02/25)

高円寺でSimple勉強会。
今回は人数少なめ。おいしいカキフライ弁当で腹ごしらえして
黙々と作業開始。

◆AmidaMVCという名前について。
多分、MVCは使っていない。

だいたいMVCが分かっていない。


◆最初は1月で開発をやめるつもりだったが、面白いので2月も続けてしまった。で、フレームワークからCMSになりつつある。FWとCMSの違いって何だろう?

◆今はMarkdownを使ったPukiwikiみたい。
これでdiffも出来るようになったし、ほぼ機能はそろった?
自動で登録したファイルを一覧するのが足りないか。

twitterのbootstrap使ってみた。
最初は戸惑ったが、ちょっと触れば簡単。jQuery Mobileのようにマークアップだけでボタンの色・形、そしてモーダルの動きを指定できる。というかクラスを指定すればOK。クラス名だけ調べれば簡単。
ただ途中でbootstrap使い始めたので、競合が起きて面倒だった。

simplediffというPHPのテキストdiffツールを使ってみた。
Pearを試したが、ウォーニングばかりで動かなかったので、こちらを試したら一発で動いた。遅いらしいが、ま速度を気にする段階ではないので。動く方が大事でしょう。

◆アミダ式チェーンは、
開発はしやすい。
ただ、次のチェーンを理解する必要がある。
ので、あまり長くアクションを使い回すのはよくないかも。

◆コンポーネントは汎用性を高くするのは難しい。
CMSごとのコンポーネント群を作って管理するがあるかも。
例:ルーター・管理者機能なし、ファイルの閲覧のみのCMS。

◆自分のコーディングスキルに絶望、まではしないが、残念な気持ちになった。

2012年2月3日金曜日

AmidaMVC:設計方針を決める

さてコントローラーのアミダ式チェーンが出来たので、
今度は中身のフレームワーク。

HTTPのリクエストはコントローラーが受け取り、
コンポーネントを次々と実行する。

で、コンポーネントで何をするか?
まずは、
  1. リクエストから読み込むファイルを探して、
  2. ファイルを読み込んで、
  3. テンプレートに流し込んで、出力。
これが最低必要な流れでしょう。
それぞれ、Router、Loader、そしてRenderという名前のコンポーネントを開発することにしました。
試してませんが、今でもこの3つのコンポーネントだけで動くんじゃないかな?

Router

コントローラーからpath_infoを読み込む。
単純に最後のパスがファイル名で、残りがフォルダー名。
で、探し出せればLoaderにファイル情報を渡す。 
今はルートマップから探索できるようになりました。どちらの方法でもファイルが見つから無ければPageNotFoundにアクションを変更。 

Loader

ファイル情報からファイルの中身を読み込む。
拡張子から中身の種別を判断(HTML、markdown、テキスト、PHPのソースコード)したうえで読み込む。
PageNotFoundの場合は、前もって決めておいたファイルを読み込む。最初、ファイルはLoader内に直書きしてたが、今は設定コンポーネント(Config)で設定するよう変更済み。ファイルが正常に読み込めれば、アクションをデフォルトに戻してRenderに処理を渡す。

Render

中身の種別により、マークダウンならHTMLに、テキストならnl2brとhtmlspecialcharsをかけて、PHPならソースコードハイライトして、と前処理を行う。

それから中身のデータをテンプレートに流し込む。
とにかく簡単に。単なるPHPをインクルードするだけ。

◆$siteObj

コンポーネントで色んなデータを扱う必要があるので、必要な情報は全部このオブジェクトにぶち込んで、コンポーネント間で持ち回します。デザインパターンで言えばData Transfer Objectですな。

◆Toolsフォルダー

コンポーネントが行う実際の処理はここに書く。
コンポーネントは、処理を行うルーチンを呼び出すのが仕事。

こういう汎用的なルーチンは再発明しても仕方ないので、出来るだけ自分で書かないのが方針。実際、『パーフェクトPHP』のフレームワークの章から、ほとんどのコードを書き写しました。とても参考になったいい本だと思います。

理想はsymfonyなどのフレームワークのライブラリを再利用できるといいな。

◆以上、簡潔に方針をまとめると
・コントローラーは単純に、
・コンポーネントは薄く、
・ツールは汎用的かつコピペで書く、
のが開発方針です。

2012年2月2日木曜日

AmidaMVC:Amida式チェーンを思いついた流れ

何故Amidaなんてチェーンを思いついたか?

昔作った簡単なページコントローラーがあったけど、
コードが汚くて使いづらくて。

つい書き直し始めたのが始まりだった・・・

最初は入力の値によって、決まった関数を呼び出すだけ。
次に、ひとつのクラス内のメソードを呼び出すようにしてみた。

ふむ。簡単すぎるぐらいの改造。

それではと、複数のクラスを登録して、
次のクラスを呼び出せるようにしてみた。

何か面白いぞ。

じゃぁ、デフォルトで次のクラスに進んでみよう。
お~、いい感じ。

おや?MVCフレームワークにちょうどいいんじゃない

じゃ作ってみようか。

作ってみた ← いまここ。

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として表示する予定です。