http://coderepos.org/share/browser/events/phpframework/piece_framework/trunk
機能をすべて実装したものの、まだまだチューニングや見えざる敵(bug)との戦いが控えています。
俺たちの戦いはこれからだ!
r15761 号をもってコミットメントは終了です。
ご愛読ありがとうございました。
http://coderepos.org/share/browser/events/phpframework/piece_framework/trunk
機能をすべて実装したものの、まだまだチューニングや見えざる敵(bug)との戦いが控えています。
俺たちの戦いはこれからだ!
r15761 号をもってコミットメントは終了です。
ご愛読ありがとうございました。
できました。
Runemaster (http://hatotech.org/runemaster/)
これまで「PHP DOMパーサを使ってテンプレートエンジンを作ってみる」としてエントリをあげてきたライブラリが、このたびめでたく(まだベータ的なものですが)初めてのリリースと相成りました。
ライブラリが今の形に落ち着いてからドキュメントをカリカリと書いてたんですがこれが地味に時間がかかってしまっていたので、思い切って 2/3 ほど書いた現在の状態でひとまずリリースしちゃうことにしました。
このまま継続してドキュメントは書き進めていきます。数日単位でポツポツといった感じになるでしょうが、上記サイトに掲載していきますのでどうぞよろしくお願いします。
ちなみにこいつの名前については、はてブにて小山さんより「カッコイイ名前を付けるように」というお達しがありましたので、このようになりました。
Runemasterは、多彩なSpellを組み合わせることで (x)html の力を Runeストーンに刻み込み、埋め込まれた秘められし力を引き出すことで様々な能力を発揮させることができるのだ!(ドギャァーン)
この辺までくると、機能的なところでそんなに大きく進展はしてない感じではありますが、継続的に報告もせんといかんよね!
新しく追加した機能は、フォームに対する値やエレメントの操作。この辺はPEARのHTML_Template_Flexyが良い感じだったので、それと出来る限り同じような使い方が出来るように。
Template
<form name="example">
<input type="text" name="email" />
<select name="person">
<option value="1">foo</option>
<option value="2">bar</option>
<option value="3">baz</option>
</select>
<input name="item" type="radio" value="1" />foo
<input name="item" type="radio" value="2" />bar
<input name="item" type="radio" value="3" />baz
</form>
PHP
<?php
$template = new Template();
$values = new stdClass();
$values->email = 'foo@example.com';
$values->person = 1;
$values->item = 2;
$template->setFormValues('example', $values);
$template->renderer('Example');
?>
Result
<form name="example">
<input type="text" name="email" value="foo@example.com" />
<select name="person">
<option value="1" selected="selected">foo</option>
<option value="2">bar</option>
<option value="3">baz</option>
</select>
<input name="item" type="radio" value="1" />foo
<input name="item" type="radio" value="2" checked="checked" />bar
<input name="item" type="radio" value="3" />baz
</form>
地味に「PHPSpec を使った開発のケーススタディ」的な感じでテンプレートよりも好評な、Specによる仕様確認の現在はこちら。
テンプレート変数割り当て - オブジェクトを割り当てると、プロパティがテンプレート変数として利用される - 連想配列を割り当てると、配列キーがテンプレート変数として利用される - テンプレート変数として配列が利用できる - テンプレート変数としてオブジェクトが利用できる - テンプレート変数は配列とオブジェクトが混合で利用できる - テンプレート変数として割り当てたクラスオブジェクトのメソッドが利用できる - テンプレート変数に対して関数が利用できる - テンプレート変数として指定する属性の名称が変更できる - 置換されるテンプレート変数は何もせずともhtmlspecialchars済み - 置換されるテンプレート変数のhtmlspecialcharsは個別で無効にできる - テンプレート内で同じ変数は、利用タグに関係なく何度でも使える - 値がassignされていないテンプレート変数は無視される - 値を割り当てるのに利用したタグは明示的に消去することができる - エレメント属性の値に対してのみ{}で囲んだテンプレート変数を利用できる エレメント操作 - セレクタによる指定ノードのエレメントが追加、変更できる - セレクタによる複数対象のエレメントが追加、変更できる - エレメントの各要素のうち、今のところタグ名は変えれない フォーム操作 - テキストラインへ値をセットすることができる - テキストエリアへ値をセットすることができる - hiddenへ値をセットすることができる - セレクトボックスを選択状態にできる - ラジオボタンをチェック状態にできる - チェックボックスをチェック状態にできる - ボタンやサブミットへ値がセットすることができる - 指定するフォームに値をhiddenとしてセットすることができる - リストをセレクトオプションとして登録することができる 条件による表示切り替え処理 - テンプレート変数値がtrueであれば表示、偽であれば非表示にできる - テンプレート変数値の条件を反転評価して表示非表示を切り替えることができる - オブジェクトのテンプレート変数を使って表示非表示を切り替えることができる - 配列のテンプレート変数を使って表示非表示を切り替えることができる - クラスメソッドの評価によって表示非表示を切り替えることができる - 関数の評価によって表示非表示を切り替えることができる 挿入処理 - セレクタによる指定ノード内の末尾にコンテンツが追加できる - セレクタによる指定ノード内の末尾にノードが追加できる - セレクタによる指定ノード内の先頭にコンテンツが追加できる - セレクタによる指定ノード内の先頭にノードが追加できる 繰り返し処理 - phpのforeach_as_valueのような処理ができる - phpのforeach_as_key_valueのような処理ができる - 繰り返し処理エレメント内でも、トップレベルのテンプレート変数が使える - 通常の変数と繰り返し対象の展開変数の変数名が重複した場合は、展開変数が優先さ れる - 繰り返し対象の展開変数がオブジェクトや配列も利用できる - 繰り返し処理はネストできる - foreach属性と同じエレメントにkey属性を設定して変数展開できる - リスト変数が空もしくはnullの場合はinnertextが消える 機能の複合利用 - foreach構文とif構文を組み合わせて利用できるが、同エレメント内にてリスト変数と同時に利用できる条件は展開変数になる - foreach構文で繰り返されるコンテンツ内のエレメント要素が{}変数で変更できる - foreach構文で繰り返されるコンテンツ内でチェックボックスの生成およびチェック状態が実現できる 47 examples, 0 failures
かなり充実してきた感じで、今回から若干コードの内容や構成に手を入れ始めています。もうすぐ本当にベータリリースできそうな気もしますが、ここからは引き続きコードのリファクタリングを続けながら、あとは仕様の若干の追加や見直しなども進めます。条件分岐として <span if="foo > 10"> みたいな記述もできるようにしたい。
鈍足ながら少しずつ機能を増やしていっています。
前回から追加された機能は、if 記法によって表示箇所を切り替えできるようにしてみたのと、指定したノード内にコンテンツを挿入するという機構。
if 記法の切り替えとは、次のようにエレメントに if 属性を準備しておき
Template
<div> <span if="foo">Message A</span> <span if="bar">Message B</span> </div>
変数の値が「真」判定されればそのまま表示、「偽」判定されればエレメントが消滅します。foo = 1, bar = 0 とした場合なら、次のようになります。
Result
<div> <span if="foo">Message A</span> </div>
一方のノード内へのコンテンツ挿入は、セレクタで指定するノードへ append で末尾に、prepend で先頭に挿入します。
Template
<p id="foo">Content</p> <p id="bar">Content</p>
PHP
<?php
$template = new Template();
$text = 'Insertion';
$node = '<span>Insertion</span>';
/* id="foo", id="bar" にそれぞれ $text, $node を挿入 */
$template->append('#foo', $text);
$template->prepend('#bar', $node);
$template->renderer('Example');
?>
Result
<p id="foo">ContentInsertion</p> <p id="bar"><span>Insertion</span>Content</p>
恒例の PHPSpec による仕様確認はこちら。大分充実してきました。
テンプレート変数割り当て - オブジェクトを割り当てると、プロパティがテンプレート変数として利用される - 連想配列を割り当てると、配列キーがテンプレート変数として利用される - テンプレート変数として配列が利用できる - テンプレート変数としてオブジェクトが利用できる - テンプレート変数は配列とオブジェクトが混合で利用できる - テンプレート変数として割り当てたクラスオブジェクトのメソッドが利用できる - テンプレート変数に対して関数が利用できる - テンプレート変数として指定する属性の名称が変更できる - 置換されるテンプレート変数は何もせずともhtmlspecialchars済み - 置換されるテンプレート変数のhtmlspecialcharsは個別で無効にできる - テンプレート内で同じ変数は、利用タグに関係なく何度でも使える - 値がassignされていないテンプレート変数は無視される - 値を割り当てるのに利用したタグは明示的に消去することができる エレメント操作 - セレクタによる指定ノードのエレメントが追加、変更できる - セレクタによる複数対象のエレメントが追加、変更できる - エレメントの各要素のうち、今のところタグ名は変えれない 条件による表示切り替え処理 - テンプレート変数値がtrueであれば表示、偽であれば非表示にできる - テンプレート変数値の条件を反転評価して表示非表示を切り替えることができる - オブジェクトのテンプレート変数を使って表示非表示を切り替えることができる - 配列のテンプレート変数を使って表示非表示を切り替えることができる - クラスメソッドの評価によって表示非表示を切り替えることができる - 関数の評価によって表示非表示を切り替えることができる - foreach構文と組み合わせて表示非表示を切り替えることができるが、同エレメント内にてリスト変数と同時に利用できる条件は展開変数 挿入処理 - セレクタによる指定ノード内の末尾にコンテンツが追加できる - セレクタによる指定ノード内の末尾にノードが追加できる - セレクタによる指定ノード内の先頭にコンテンツが追加できる - セレクタによる指定ノード内の先頭にノードが追加できる 繰り返し処理 - phpのforeach_as_valueのような処理ができる - phpのforeach_as_key_valueのような処理ができる - 通常の変数と繰り返し対象の展開変数の変数名が重複した場合は、展開変数が優先さ れる - 繰り返し対象の展開変数がオブジェクトや配列も利用できる - 繰り返し処理はネストできる - foreach属性と同じエレメントにkey属性を設定して変数展開できる - リスト変数が空もしくはnullの場合はinnertextが消える 34 examples, 0 failures
前回のエントリの続きです。
前回からの進展としては、時間切れで出来なかった関数実行、指定する DOM Element の要素書き換えや、繰り返し処理の実装。そしてここまで来るとコードがさらにゴチャゴチャとしてくるので、若干のリファクタリング作業をば。
繰り返し処理は PHP の foreach ~ as みたいなのができるように、例えば次のようにテンプレート側で foreach , as 要素を準備しといて、
<table>
<tr>
<th>id</th>
<th>name</th>
<th>email</th>
</tr>
<tr foreach="persons" as="person">
<td key="person.id"></td>
<td key="person.name"></td>
<td key="person.email.pc"></td>
</tr>
</table>
persons 変数に配列を assign すれば、こんな感じに置換される。
<table>
<tr>
<th>id</th>
<th>name</th>
<th>email</th>
</tr>
<tr >
<td key="person.id">1</td>
<td key="person.name">foo</td>
<td key="person.email.pc">foo@example.com</td>
</tr><tr >
<td key="person.id">2</td>
<td key="person.name">bar</td>
<td key="person.email.pc">bar@example.com</td>
</tr>
</table>
一方、DOM Element の要素書き換えはテンプレート側の準備一切なしで PHP 側から Element指定+内容適応。なお Element の指定方法はひとまず DOM パーサ仕様で。jquery like なものなのでスムーズに使えそう。
<?php $template = new Template(); $element = new stdClass(); $element->class = 'example'; $element->aaa = 'AAA'; /* id="foo" の Elememt に class="example", aaa="AAA" を追加 */$template->setElement('#foo', $element);$template->setAttribute('#foo', $element); /* メソッド名変更予定 */ $template->renderer('Example'); ?>
なお現在の仕様はこちら。
テンプレート変数割り当て - オブジェクトを割り当てると、プロパティがテンプレート変数として利用される - 連想配列を割り当てると、配列キーがテンプレート変数として利用される - テンプレート変数として配列が利用できる - テンプレート変数としてオブジェクトが利用できる - テンプレート変数は配列とオブジェクトが混合で利用できる - テンプレート変数として割り当てたクラスオブジェクトのメソッドが利用できる - テンプレート変数に対して関数が利用できる - タグ内のテンプレート変数の要素名が変更できる - 置換されるテンプレート変数は何もせずともhtmlspecialchars済み - 置換されるテンプレート変数のhtmlspecialcharsは個別で無効にできる - テンプレート内で同じ変数は、利用タグに関係なく何度でも使える - 値がassignされていないテンプレート変数は無視される エレメント操作 - セレクタによる指定ノードのエレメントが追加、変更できる - セレクタによる複数対象のエレメントが追加、変更できる - エレメントの各要素のうち、今のところタグ名は変えれない 繰り返し処理 - phpのforeach_as_valueのような処理ができる - phpのforeach_as_key_valueのような処理ができる - 通常の変数と繰り返し対象の展開変数の変数名が重複した場合は、展開変数が優先さ れる - 繰り返し対象の展開変数がオブジェクトや配列も利用できる - 繰り返し処理はネストできる 20 examples, 0 failures
実は前回の仕様ではテンプレートはキャッシュするようになってたんですが、その機構はひとまずカットしました。毎度テンプレートを DOM パースする必要はないなと思って準備してたんですが、PHPコード側から動的に値が適応される「Element 書き換え」を行う場合は結局毎回パースしなおさなければならず、その辺非常にややこしい処理になりそうだったので、現時点ではあまり深く考えなくてよいよう機構そのものをなくしました。
まあ Element 書き換えが発生しない場合も当然あるでしょうから、後々キャッシュできる機能とそうでない機能をきちんと分けた上で改めて再考すればいいかな。
あと、ちゃんとカッコイイ名前をつけたほうがよいとのことで、どうしたもんかと思っているわけです。ネタでいくならKumartyだな!
男なら一度はテンプレートエンジンの作成に挑戦してみるよね!
ということで PHP Simple HTML DOM Parser を使って、DOMパーサ利用ならではのテンプレートエンジンを作ってみました。
ひとまずはテンプレート変数の利用だけ。クラス名とかは考えるのが面倒だったのでひとまず Template で。
<?php
require_once 'Template.php';
$variables = new stdClass();
$variables->foo = 'AAA';
$variables->bar = array(1, 2);
$variables->baz->quux = 'BBB';
$template = new Template('./templates', './caches');
$template->assign($variables);
$template->renderer('Example');
?>
<html> <head> </head> <body> <span key="foo">hoge</span> <div key="bar.0">hoge</div> <p key="bar.1">hoge</p> <p key="bar.2">hoge</p> <font color="#FF0000" key="baz.quux">hoge</font> </body> </html>
<html> <head> </head> <body> <span key="foo">AAA</span> <div key="bar.0">1</div> <p key="bar.1">2</p> <p key="bar.2">hoge</p> <font color="#FF0000" key="baz.quux">BBB</font> </body> </html>
割り当てたオブジェクト(もしくは連想配列)のプロパティが、タグ内の「key=***」という属性で指定されている箇所をテンプレート変数として置換。変数としても配列、オブジェクトともに利用可能。あと「key」という属性名も好きな名称に指定可能。
PHPTALってよりmayaaに近い?(使ったことないのでよくわからない。中間層の XML もないけど。)
まだまだ途中段階だし、コードもベタに書いて一切見直ししていないのでアレな内容ですが。
ちなみにアーカイブ内の specs ディレクトリ内は、PHPSpecによるファイル群(スペック、テスト)で、ここを見れば&動かせば、現在の仕様や利用方法などが分かります。
なお現在の状態はこんな感じ。
テンプレート変数割り当て - オブジェクトを割り当てると、プロパティがテンプレート変数として利用される - 連想配列を割り当てると、配列キーがテンプレート変数として利用される - テンプレート変数として配列が利用できる - テンプレート変数としてオブジェクトが利用できる - テンプレート変数は配列とオブジェクトが混合で利用できる - テンプレート変数として割り当てたクラスオブジェクトのメソッドが利用できる - テンプレート変数に対して関数が利用できる (ERROR) - タグ内のテンプレート変数の要素名が変更できる - 置換されるテンプレート変数は何もせずともhtmlspecialchars済み - 置換されるテンプレート変数のhtmlspecialcharsは個別で無効にできる - 置換されるテンプレート変数に対して個別にurlencodeできる - 置換されるテンプレート変数に対して個別にnl2brできる - テンプレート内で同じ変数は、利用タグに関係なく何度でも使える テンプレートエンジンの利用 - 利用するとキャッシュができる - キャッシュデータがあるとそれが描写で再利用される - キャッシュデータ作成後にテンプレートが更新されると、キャッシュを作り直して描写する - テンプレートファイルの拡張子が変更できる 17 examples, 0 failures, 1 error
関数利用を作ってる最中でタイムアップ。また後日にでも。
これ面白い。
require_once 'html_dom_parser.php';
$dom = file_get_dom('http://www.google.co.jp/search?q=%E3%83%94%E3%82%AB%E3%83%81%E3%83%A5%E3%82%A6&lr=lang_ja&ie=utf-8&oe=utf-8');
foreach ($dom->find('a') as $node) {
$node->innertext = str_replace('ピカチュウ', 'オプーナ', $node->innertext);
$node->href = 'http://www.opoona.com/index.htm';
}
foreach ($dom->find('b') as $node) {
$node->innertext = str_replace('ピカチュウ', 'オプーナ', $node->innertext);
}
print $dom->save();
なんか色々思いついたので、暫らくの間これで遊びます。
Piece_ORM を使えば、だれでも簡単にデータベースを使ったPHPプログラムを書くことができます。(任天堂のCM風に。)
1. フォルダの作成
Piece_ORM を動作させるには、プログラムのファイルとは別に3つのフォルダを使います。
プログラムファイルを作成するフォルダ内に、次の3つのフォルダを作成します。
・config
・cache
・mappers
2. データベース情報を準備する
次に Piece_ORM から接続するデータベースサーバの情報を設定します。
仮にデータベースの情報が
データベース: PostgreSQL
ホスト名: localhost
database : example
username : example_user
password : example_password
以上のようなものなら、先ほど作ったフォルダのうちの1つ、config フォルダの中に「piece-orm-config.yaml」という名前のファイルを作成し、次のように記述します。
- name: database1 dsn: pgsql://example_user:example_password@localhost/example
3. テーブルを準備する
続いて、実際にプログラムから読み込みや書き込みを行おうとするテーブルを準備します。
データベース内の実際のテーブルは予め作成しておきます。ここでは「person」という名前のテーブルを作成したとします。
先ほど作ったフォルダのうちの1つ、mapper フォルダの中に「Person.yaml」という名前のファイルを作成します。中身は何も必要ありません。
4. プログラムを書く
最後にプログラムを書きます。書くプログラムは非常に短くて簡単です。
SELECT 文の SQL は次のようなコードを準備します。SELECT した結果のレコードがオブジェクトとして簡単に参照が可能です。
<?php
// Piece_ORM を読み込む
require_once 'Piece/ORM.php';
// Piece_ORM を設定する
Piece_ORM::configure('config', 'cache', 'mappers');
// person テーブルを対象にする
$mapper = Piece_ORM::getMapper('Person');
// select * from peron where id = 1;
$person1 = $mapper->findById(1);
echo $person1->name; // name カラムを表示
// select * from peron where name = 'foo';
$person2 = $mapper->findByName('foo');
echo $person2->name;
?>
INSERT, UPDATE, DELETE も簡単です。
<?php
// Piece_ORM を読み込みと設定
require_once 'Piece/ORM.php';
Piece_ORM::configure('config', 'cache', 'mappers');
// person テーブルを対象にする
$mapper = Piece_ORM::getMapper('Person');
// 新しいレコードとして INSERT
$person = new stdClass();
$person->name = 'foo';
$mapper->insert($person);
// name カラムを変更して UPDATE
$person->name = 'bar';
$mapper->update($person);
// DELETE
$mapper->delete($person);
?>
1/31 に開催された勉強会in大阪でも一部の人には話をしていたんですが、その後少しだけ動きがあったので。
勉強会の前日である 1/30 に HTML_Template_Flexy のバージョン 1.3.0 がリリースされ、いくつかの小さな新機能が実装されたということになってます。
このパッケージ、実は DB_DataObject と同じ作者が開発、管理しておりまして。(実際、同日に DB_DataObject のバグフィックスとしてマイナーバージョンがリリース)
で、去年の9月東京で開催されたPHPカンファレンスで私の「ネタ」をご覧になった方はお分かりかと思いますが、この方のパッケージをリリースする際に「ちょっとした落とし穴」みたいなミスをすることがたまにあったりして、あるバージョンのリリース後にも細かなFixバージョンUPを短時間で積み重ねてしまうということがあります。
さて今回の Flexy 1.3.0 にて、少し魅力的な機能を試してみたく Ver.UP して動かしてみたのですが、何やらエラーが発生。どういうことだとメッセージをチェックしたりファイル構成や中身を確認してみると、なんと新機能を利用するため新規に追加された(であろう)クラスファイルが存在しないため require に失敗し、結果 new class できず Fatal が発生している。
「AKまたやらかしやがった!」(rsky へ送ったメールより)
どうやら CVS には新クラスファイルが存在しているものの、PEAR パッケージを作成するための package.xml 内にそのクラスファイルを記述していなかったため、結果 1.3.0 パッケージ内にファイルが含まれない状況になっていたのです。
多分、CVS にコミット済みのものやらローカルに存在しているソースやら何やらが分散したり、時間が経ったことで混在しているオープン・クローズなソースの状況を忘れてしまっていたりしてるんじゃないかと思います。(今回ファイルを忘れていた分に関しても、機能的な部分の実装は数ヶ月前でした。それでも一部ミスがあったようですが。)
なお、現在出ている 1.3.2 では、1.3.0 リリース時に提示した機能の一部をカットしているようです。現在リリース分のものと CVS 上のソースの内容が異なっているのを見る限り、恐らく再実装しなおして次バージョンがリリースするのではないかと思います。
いまのところ 1.3.2 では問題は特に出ていません。(PHP5 で動かすと「is_a 使うのやめろ」という要求が出るものの、エラーではないです。) flexy:include 内の src 指定に変数が使えるのは地味に便利だし(ちょうど目前の案件で必要そうなシチュエーションだったのでワロタ)、あと radio エレメント内で id 要素を指定しないと怒られてしまうというのもこのバージョンからなくなります。ただし、いくつかの radio で同 name 要素を持つエレメントに対し 同 id 要素を持たせるというのは相変わらず NG。なので今後も変わらずマークアップ担当者には別 id を使うか class を使ってもらうようにしよう!
そしてがんばれ AK!!
去年行われたPiece Network 1の懇親会でも話が出ていたんですが、ついに「PHP勉強会 at 大阪」として開催されるようです。しかも予定どおり Apple Store 内の施設!発表者募集中とのことで、ここはひとつ昨年の PHP カンファレンスのLTでだだスベリしたAlan Knowles漫談を関西初披r
そういうことはありませんが、参加したいと思います。