<?php echo "Hello PHP"; ?>

PHPとJava中心のウェブ技術うんちくブログ

<< 前の記事  

innoframe の同期トークン処理

進捗は緩やかですが少しずつ改良されている PHP のフレームワークの innoframe ですが今回は Struts にもある同期トークンと同等のものを実装しました。

Webアプリ開発時によくある2度押し対策に有効なのがフレームワークの同期トークンの仕組みを利用する方法。Struts でも画面をまたぐトランザクションの仕組みとして同期トークンの仕組みを提供しています。

この仕組みと同等品を innoframe にも実装しました。実装と言ってもとっても簡単なものなので自前で実装してもできてしまうものなんです、中身は。

使い方もほぼ同じでアクション内で


// 同期トークン保存
$this->saveToken();


でトークンを保存して


// 同期トークンチェック
if (!$this->isTokenValid($request, true)) {
    // エラー処理
}


で同期トークンの妥当性をチェックします。

ビュー側の ViewScript にもトークン値を埋めて置かなければいけないのですがこの辺はフレームワークチックに


<?php echo $html->form("/regist") ?>


と記述しておくとクライアントには


<form action="/regist" method="post"><input type="hidden" name="TRANSACTION_TOKEN_VALUE" value="9a2241c2015fb98771d97a9828db1d73" />


が出力されます。

ブラウザの戻るや二重送信対策にはこの同期トークンの仕組みは便利ですよね。

InnoFrame Website
http://innoframe-php.net/

PHPで簡単Zip展開

数年前、Windows アプリケーションを作っていた頃に初めて Zip アーカイブを作成したり展開したときのあの感動を最近また味わうことができました。あるサイトの作成で Zip アーカイブを展開する仕様があり PHP でも出来るのか調べたらすぐに出来ることが判明。



$zip = new ZipArchive();
$result = $zip->open("example.zip");
if ($result === true) {
    $zip->extractTo("/tmp/example/");
    $zip->close();
}


展開のメインになっている部分はこんな感じです。
これだと example.zip の内容を /tmp/example/ にすべて展開します。
ZipArchive をラップしてもう少し機能的なクラスは作りましたが、やっぱり PHP。
この手軽さは良いですね。

StrutsライクなPHPフレームワーク

とうとう作成中だった PHP のフレームワークがそれなりに動作してきているので公開することになりました。まだ開発中ですのでアルファ版扱いで公開しています。

InnoFrame Website
http://innoframe-php.net/

公式サイトも同時に作成していて、現在は簡単なドキュメントを紹介する程度ですが読めるものになっています。ドキュメントに関してはサンプルソースを含め沢山掲載していく予定ですので今後の更新をお持ちください。ちなみにこのサイトも作成していたフレームワーク上で動作しています。

フレームワークとサイト共に完成版ではないため短いスパンで修正してくと思いますので、お時間がありましたらまたいらしてください。またサイトの方には連絡機能がないので連絡等ありましたらこのブログでお願いします。

JavaとDTOとVOとPHPフレームワーク

Java の仕事では当たり前のように使っている DTO (DataTransferObject)、現場によっては VO (ValueObject)とも呼ばれていますが(個人的にはDTOが好き)、現在作成中の PHP フレームワークでもこのオブジェクトの利用を推奨しています。どの辺がこれらのオブジェクト(DTO)に対応しているのかと言うと、画面からのデータ(リクエストパラメータ)を受け取るところでフレームワークが DTO の該当するプロパティに直接データをセットしてくれます。

Java のプロジェクトでよくリクエストパラメータを一つずつ DTO のプロパティにセットし直したりしているのを見ますがこのフレームワークではそんな面倒な作業をする必要はありません。例えばユーザの名前を入力するためのテキストボックスがあるならば name 属性に userDto::name と記述しておけばフォームで定義してあるUserDto型オブジェクトの name プロパティに入力値がセットされて渡されます。

そんなフレームワークですが現在公式サイトを作成しています。完成次第このブログでも紹介しますので是非いらしてください。


PHPフレームワーク 〜 設定ファイル フォーム編2 〜

今回は実際のソースを使ってフォームクラスを説明しようと思います。

フォームクラス (UpdateForm.php)

class UpdateForm extends BeanForm
{
    /**
     * フォームを初期化します
     *
     * @return array
     */

    public function init()
    {
        return array(

            "id"     => 0,
            "title"  => null,
            "author" => null,
            "isbn"   => null
        );
    }


    /**
     * バリデートチェック
     *
     * @param HttpRequest $request
     * @return string
     */

    public function validate($request)
    {
        if (
$this->getTitle() == "") {
            $this->addMessage("error.empty_title");
            return "error.tpl";
        }
    }
}



このフレームワークのフォームクラスはこんな感じになります。画面からのデータのチェック処理はフォームのvalidate()で行えます。

通常フォームと言うとプロパティのセッター・ゲッターを長々と書くことになりますがこのフレームワークではBeanFormクラスを継承したサブクラスにすると上記のようにセッター・ゲッターを書いていなくてもセッター・ゲッターが使えます。プロパティはinit()の連想配列でセットします。連想配列のキーがプロパティ名で値がプロパティの設定値(初期値)になります。フレームワークはフォーム作成後にこのinit()をコールしてプロパティを初期化します。


メッセージリソース (messages.conf)

error.empty_title = タイトルは必須項目です。


上記のようなフォームクラスとメッセージリソースを用意すると、画面からのid、title、author、isbnという名前でリクエストパラメータを取得できさらにアクションが呼ばれる前にパラメータtitleの入力チェックを行うことができます。ここでエラーが無ければアクションクラスに処理が移りますがエラーがあった場合は指定している error.tpl テンプレートに「タイトルは必須項目です。」というメッセージを表示して処理が終わります。エラーで遷移する画面はreturnで返す値をパターン分けすればどこにでも飛ばせます。

Ajaxでいこう

お仕事の話しですが前々回のJavaの業務アプリが入力項目の多い画面がたくさんあるプロジェクトで、プルダウンで入力項目が変化していく、Webアプリだとちょっとやりたくない大変なものでした。最初の一画面はプルダウンのonChangeでsubmitさせて項目を変えていく普通のやり方でコーディングしていましたが、プルダウンを選択するたびに画面の再表示が行われてユーザビリティの悪い画面になってしまいました。

まあ想定されていた動きでしたが実際使う側まわるともうちょっと何とかーと。そこで今度は非同期通信を使って二画面目にチャレンジ。以前からJavaScriptはそれなりに使っていたので時間もかからず実装が終わり、画面の出来栄えもなかなか。もっと早く取り入れていれば良かった、というのが印象でした。

そんなこんなで今回作っているPHPのフレームワークではこの辺の仕組みも取り入れようと思っています。とは言ってもprototype.jsさえあれば(無くてもそれほど面倒な気はしませんが)フレームワークに仕組みがなくてもいけるのかなとも思ってます。

フレームワークにAjax対応と銘打つには何があればいいんでしょうかね。

PHPフレームワーク 〜 設定ファイル フォーム編 〜

設定ファイルの続き。

設定ファイル (webfront.conf)

[action-mapping]
/new = /src/action/CreateAction
/update = /src/action/UpdateAction
/delete = /src/action/UpdateAction::delete
/welcome = /src/pages/welcome.tpl
/read = /src/action/ReadAction#command

[form-mapping]
/new = /src/form/CreateForm
/update = /src/form/UpdateForm
/delete = /src/form/UpdateForm

[filter-mapping]
/* = CFilter
/update = AFilter, BFilter

[filter]
AFilter = /src/filter/AFilter
BFilter = /src/filter/BFilter
CFilter = /src/filter/CFilter



前回まではアクションの設定でしたが今回はフォームの設定です。

このフレームワークもStrutsと同じように画面ごとにフォームを用意し、データをフォームクラスに保持することが可能です。設定次第では画面ごとではなく複数の画面で一つのフォームを使い回ししたり、フォームを一切使わない画面という設定も可能になっています。簡単な画面などはフォームを使わないのも手でしょう。フォームの設定は[form-mapping]に記述します。 記述の仕方は簡単でアクションパス = フォームのクラスパス、という感じになります。これはアクションの記述の仕方と同じです。

上記の設定ファイルだと、/new に対してはCreateFormクラスをフォームのクラス、/update にはUpdateFormクラス、/delete にはUpdateFormクラスということになります。ここでカンのいい方は気付いたと思いますがフォームは複数のアクションパスで同一のフォームを記述することができます。/update と /delete は同じUpdateFormクラスをフォームとして使うということです。

また[action-mapping]でアクションパス /read を定義していますが、[form-mapping]では /read を定義していません。この場合アクションにReadActionクラス、フォームは無し、という設定になります。この設定も有りです。この設定だとアクションクラス内のフォーム変数の値はnullとなり、リクエストパラメータを取得する場合はHttpRequestオブジェクトのget()を使って取得することになります。


アクションクラス (ReadAction.php)

class ReadAction extends DispatchAction
{
    /**
     * ボタンとアクションをマッピングする
     *
     * @return array
     */

    protected function getKeyMethodMap()
    {
        return array(

            "button.list" => "list",
            "button.back" => "back"
        );
    }


    /**
     * 一覧表示ボタンのアクション
     *
     * @param HttpRequest $request
     * @param Form $form
     * @return string
     */

    public function list($request, $form)
    {

        // リクエストパラメータの値を取得
        // このメソッドの引数$formはnullです
        $name = $request->get("name");

        // 通常は下記のようにフォームから取得します
        //$name = $form->getName();

        return "read.tpl"
;
    }

    /**
     * 戻るボタンのアクション
     *
     * @param HttpRequest $request
     * @param Form $form
     * @return string
     */

    public function back($request, $form)
    {

        // 戻る処理

        return "read.tpl"
;
    }
}



フォームクラスはこの他にバリデートチェックをするメソッドもありますがこれはまた次回に紹介します。一連の設定の説明が紹介しきれた時にこのフレームワークをダウンロードできるようにしたいと思いますので是非使って頂いて感想等をお聞かせ頂ければと思っています。

PHPフレームワーク 〜 設定ファイル アクション編2 〜

先日の続き。

設定ファイル (webfront.conf)

[action-mapping]
/new = /src/action/CreateAction
/update = /src/action/UpdateAction
/delete = /src/action/UpdateAction::delete
/welcome = /src/pages/welcome.tpl
/read = /src/action/ReadAction#command

[form-mapping]
/new = /src/form/CreateForm
/update = /src/form/UpdateForm
/delete = /src/form/UpdateForm

[filter-mapping]
/* = CFilter
/update = AFilter, BFilter

[filter]
AFilter = /src/filter/AFilter
BFilter = /src/filter/BFilter
CFilter = /src/filter/CFilter



[action-mapping] の4番目のようにアクションクラスパスではなくファイル名を記述するとフレームワークはそのファイルを直接表示します。HTMLなどのファイルを表示するだけのリクエストのときはこのように記述します。[action-mapping] の5番目はStrutsのLookupDispatchActionと同じような動作が可能で、フォーム画面内にある複数のsubmitボタンごとにそれぞれ処理するメソッドを指定するとこができます。5番目の場合の例でソースを使って説明します。


テンプレートファイル (read.tpl)

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>
<body>
<form name="read_form" action="/read" method="post">
<input type="submit" value="<?= MessageResources::get('button.list') ?>" name="command" /><br /><br />
<input type="submit" value="<?= MessageResources::get('button.back') ?>" name="command" />
</form>
</body>
</html>



submitのname属性には設定ファイルで#のあとに記述した値と同一の値を設定します。5番目の場合だと /src/action/ReadAction#command なので command です。


アクションクラス (ReadAction.php)

class ReadAction extends DispatchAction
{
    /**
     * ボタンとアクションをマッピングする
     *
     * @return array
     */

    protected function getKeyMethodMap()
    {
        return array(

            "button.list" => "list",
            "button.back" => "back"
        );
    }


    /**
     * 一覧表示ボタンのアクション
     *
     * @param HttpRequest $request
     * @param Form $form
     * @return string
     */

    public function list($request, $form)
    {

        // 一覧表示処理

        return "read.tpl";
    }

    /**
     * 戻るボタンのアクション
     *
     * @param HttpRequest $request
     * @param Form $form
     * @return string
     */

    public function back($request, $form)
    {

        // 戻る処理

        return "read.tpl"
;
    }
}




この動作をさせるアクションクラスはDispatchActionクラスを継承して書き、submitボタンとコールするメソッドをマッピングさせるためにDispatchActionクラスの抽象メソッドgetKeyMethodMap()を実装してメッセージリソースのキーとメソッド名を連想配列でセットしていきます。


メッセージリソース (messages.conf)

button.list = 一覧表示
button.back = 戻る



submitボタンのボタン名を記述したメッセージリソースファイルは上記のようになります。

これでフォーム画面の一覧表示ボタンを押下するとReadActionクラスのlist()がコールされ、戻るボタン押下でReadActionクラスのback()がコールされるようになります。フレームワークの設定ファイルのアクションに関する項目は以上です。次回はフォームについて説明したいと思います。

PHPフレームワーク 〜 設定ファイル アクション編 〜

先日の続きでフレームワーク用の設定ファイルの記述です。

このファイルにはアクションパスに紐付くアクションとフォーム、フィルターのクラスパスを記述します。



[action-mapping]
/new = /src/action/CreateAction
/update = /src/action/UpdateAction
/delete = /src/action/UpdateAction::delete
/welcome = /src/pages/welcome.tpl
/read = /src/action/ReadAction#command

[form-mapping]
/new = /src/form/CreateForm
/update = /src/form/UpdateForm
/delete = /src/form/UpdateForm

[filter-mapping]
/* = CFilter
/update = AFilter, BFilter

[filter]
AFilter = /src/filter/AFilter
BFilter = /src/filter/BFilter
CFilter = /src/filter/CFilter



[action-mapping] にはアクションの設定を書きます。記述の仕方は見ての通りでアクションパス(= の左側でURLで表すと http://hogehoge.com/foo/new の /new の部分)に対するアクションクラスのパスを記述します。具体的な処理の流れで言うと /new でアクセスがあるとCreateActionクラスのexecute()がコールされる流れです。[action-mapping] の3番目のように記述すると /delete に対してUpdateActionクラスのdelete()をコールするようになります。

このフレームワークでは通常抽象ActionのBaseActionクラスを継承したアクションクラスを書きBaseActionクラスのexecute()をオーバーライドして処理を書いていきますが、3番目のようにクラス名のあとにコロン(:)を2回続けてからコールしたいメソッド名を記述することでコールするメソッドを指定するとこが出来ます。動き的にStrutsのMappingDispatchActionと似たような動きが可能になります。

/update と /delete は同じUpdateActionクラスですが /update はexecute()、/delete はdelete()がコールされます。クラスファイルはこんな感じになります。



class UpdateAction extends BaseAction
{
    /**
     * /update のアクション
     *
     * @param HttpRequest $request
     * @param Form $form
     * @return string
     */

    public function execute($request, $form)
    {

        // 更新処理

        return "update.tpl";
    }


    /**
     * /deleteのアクション
     *
     * @param HttpRequest $request
     * @param Form $form
     * @return string
     */

    public function delete($request, $form)
    {

        // 削除処理

        return "delete.tpl";
    }
}




複数の似たようなリクエストを一つのアクションクラスに書けるようになります。

続きはまた明日に。

フレームワークのコンフファイル

大抵フレームワークを使おうとするとXMLで設定情報を書いていきますがXMLの特性と言うのか毎回決まった文言を書くことになるのでいつもまとまりでコピペして一部を変更するお決まりのパターンで書いています。コピペすれば済むことなのでそう大変ではありませんが、それは慣れたフレームワークの話しで新しいフレームワークを使おうとすると、どこに何を設定するのか分らず私のような人にはとても嫌な面倒くさい作業になります。

フレームワークの中には決まったルールに従えばさほど設定を書かなくても良いというお行儀の良いフレームワークもいますが、私はちょっと変なのか書かないのは駄目で複雑なのはイヤでもある程度ピシッと設定は書いて自分で制御しているのを視覚的に確認したい!という願望があるようです。

そんな願望から自分でフレームワークを書くなら設定ファイルは間違いなくXMLではなく簡単に記述ができて視覚的にも一目瞭然なINI形式でいこうと決めていました。複雑な設定を書こうとするとXMLほど知的な書き方はできないのですが「項目 = 値」というシンプルさが気に入ってます。

話しを今書いているフレームワークに戻して、このフレームワークではフレームワーク用の設定ファイルが1つ、アプリケーション用に設定ファイルが1つ、メッセージリソース用に1つと計3ファイルあります。この辺はStrutsと同じです。

フレームワーク用の設定ファイルはアクションパスとクラスのマッピング情報が主で

アクションパス = クラスパス

の一行で書けます。


フレームワークの設定ファイルの例

[action-mapping]
/new = /src/action/CreateAction
/read = /src/action/ReadAction

[form-mapping]
/new = /src/form/CreateForm
/read = /src/form/ReadForm


と記述します。

アクションとフォームがあるところが良くも悪くもStrutsライクですがフォームは必須ではないのでアクションのみでも書けます。例だと /new のリクエストに対してアクションはCreateActionクラスのexecute()が実行されリクエストパラメータの値はCreateFormに入ります。

フレームワーク用の設定ファイルにはアクションとフォームの他にフィルターの設定も記述しますが続きは明日に。
<< 前の記事