MarkupHelperの紹介
および
PHPによるDSL実装の
可能性について
自己紹介
ミワ タカユキ
京都大学工学部情報学科中退
現在の会社に入社
以後京都に住み着く
プログラミング
Linuxサーバ管理
上流設計や
ネットワーク管理の
まねごとまで
@tkykmw
へびにっき
Markup
Helper
の紹介
HTML
どう書いて
いますか?
<div>
<?php
echo h($var)
?>
</div>
printf('<div>%s</div>',
h($var));
echo <<<HTML
<div>${var}</div>
HTML;
HTML
の中に
PHP
PHP
の中に
HTML
2つの
言語が
混在
醜い
発想を
変えましょう
PHP
で
HTML
Markup
Helper
基本的な
使い方
echo $markup
->div
->text('Hello world')
->end;
<div>
Hello world
</div>
以上!
特長
特長(1)
自動
自動で
タグを
閉じる
echo $markup
->div->p->end->end;
//<div><p></p></div>
自動で
HTML
エスケープ
echo $markup
->p->text('<br>')->end;
//<p><br></p>
特長(2)
完全
=どんなに
複雑な
HTMLでも
書ける
属性
echo $markup
->div('class1')->end;
//<div class="class1"></div>
echo $markup
->div(array('id' => 'abc',
'title' => 'ABC'))->end;
//<div id="abc" title="ABC"></div>
内容
echo $markup
->p->text('<textメソッド>')->end;
->p_('<省略形>');
//<p><textメソッド></p>
//<p><省略形></p>
echo $markup
->p
->html('<em>htmlメソッド</em>')
->end;
//<p><em>htmlメソッド</em></p>
ループと
組み合わせて
echo $markup->ul;
foreach($vars as $var) {
echo $markup->li_($var['key']);
}
echo $markup->end;
「このタグ/属性は書けるけど
このタグ/属性は書けない」
「開始タグは書けるけど
閉じタグは書けない」
そういう半端な
制約はありません
特長(3)
このHelperの
目玉機能!
他の
Helperも
連結できる
echo $markup
->f_create('User')
->fieldset->legend_('ログイン')
->f_input('username')
->f_input('password')
->p
->h_link('/forget', 'パスワード忘れた')
->end
->endfieldset
->f_end('ログイン');
f_method
Formヘルパー
h_method
HTMLヘルパー
その他は
HelperName_method
他にもelementや
ヘルパー内での使用に
特化した機能あり
詳しくは
github/tkyk/cakephp-markup-helper
ぜひ使ってみて
ください
...次の話題...
PHPによる
DSL実装の
可能性
DSL:ドメイン特化言語
(Domain Specific Language)
DSLについての
説明は省略
(参考URLなど
参照してください)
PHPによる
内部DSL
例
Markup
Helper
echo $markup
->div
->text('Hello world')
->end;
二通りの
解釈が可能
解釈(1)
PHP言語
としての
解釈
1. divプロパティ参照
2. textメソッド呼び出し
3. endプロパティ参照
解釈(2)
HTML生成言語
としての解釈
1. div開始タグ
2. textコンテンツ
3. 閉じタグ
DSL=一段高い
抽象レベルでの
解釈を可能にする
書きやすく
読みやすく
理解しやすい
インターフェイス
になる
設計/実装の
テクニック
4つ
テクニック(1)
流れるような
インターフェイス
(fluent interface)
$obj->method1('a');
$obj->method2('b');
$obj->method3('c');
$obj
->method1('a')
->method2('b')
->method3('c');
DSL設計に
おける役割
「メソッド名の並び」
に意味を持たせる
ことができる
例
Zend_Db_Select
$stmt = $db
->select()
->from('table1')
->where('price > ?', $minPrice)
->group('gid');
実装
function method1($arg) {
//...副作用...
return $this;
}
テクニック(2)
__call
メソッドの
オーバーロード
DSL設計に
おける役割
メソッド名を
引数として
利用できる
= “メソッド名”を
省略できる
省略時の動作が
推測できるように
設計することが重要
例
MarkupHelper
= HTMLマークアップの生成
→タグ名を与えれば
開始タグが生成される
$markup
->div('class1')
->p()
->strong();
テクニック(3)
__get
プロパティの
オーバーロード
DSL設計に
おける役割
2つ
役割(1)
引数なしの
メソッド呼び出しで
括弧()を省略可能に
$markup
->div()
->p()
->end()
->end();
$markup
->div
->p
->end
->end;
役割(2)
プロパティ参照と
メソッド呼び出しを
同一視させる
例
$markup->div;
$markup->div('class1');
PHP言語の解釈では
「プロパティ参照」と
「メソッド呼び出し」は
別物
しかし
HTML生成言語の解釈では
両方とも
「divタグの開始」
実装
function __get($name) {
return $this->{$name}();
}
テクニック(4)
__toString
文字列のようで
文字列でない
オブジェクト
echo $markup->div;
//<div>
echo $markup->div->end;
//<div></div>
echo $markup
->div;
echo $markup
->div->__toString();
PHP>=5.2 なら
文字列コンテキストで
自動的に__toString
が呼ばれる
DSL設計に
おける役割
“流れるような
インターフェイス”
の終端
結果が文字列なら
__toString
がベスト
一般には
DSLの機能に応じて
終端メソッドを用意
終端は複数
あっても良い
例
もう一度
Zend_Db_Select
//文字列としてSQLを得る場合
$sql = $db
->select()
->from('table1')
->where('price > ?', $minPrice)
->group('gid')
->__toString();
//そのまま実行する場合
$stmt = $db
->select()
->from('table1')
->where('price > ?', $minPrice)
->group('gid')
->query();
まとめ
PHPでも工夫次第で
“DSLらしい”
インターフェイスを
設計することは可能
書きやすく
読みやすく
理解しやすい
インターフェイスを
設計しよう!
arrayで
妥協しないで!
参考URL:
Martin Fowler's Bliki in Japanese - ドメイン特化言語
ご清聴ありがとうございました。