Ethna_Renderer_Php.phpを作ってみた
CakePHPも、昨日勉強会に行ってきたsymfonyも、Viewはphpを素で使う形式だ。
一方、EthnaはデフォルトでSmartyを使用し、Rendererを拡張することで、他のテンプレートエンジンを使うことも出来るようになっている。
ということで、EthnaでViewのテンプレートエンジンの代わりに素のphpを使用するRenderer拡張を作ってみました。
元にしたのは、Ethna 2.3.2です。
で、コードが長いので、その前にまとめを書きます。
先にまとめ
phpのコードがhtmlにまざるのが気持ち悪いです。
Smartyでなくてもいいが、なんらかのテンプレートエンジンを使いたいと、個人的には思いました。
それと、Smartyの froeachelse とか、変数の修飾子のdefaultとかは便利で、無いとちょっとめんどくさい。
http://www.smarty.net/manual/ja/language.function.foreach.php
http://www.smarty.net/manual/ja/language.modifier.default.php
他のフレームワークがどうかはわかりませんが、たぶん、うちが採用するとしたら、使い捨てになる短期のバッチやページ以外は、テンプレートエンジンを使うだろうと思います。
ついでに
info.tplを、これで動くように修正していて、いくつか気づいたところがありました。
まず、Controllerの var $ext にて、tplの拡張子をphpにしたとしても、info.tplが呼ばれているようです。
また、2.3.2のinfo.tplにhtmlのタグミスがありました。
ライン91,96で iタグの閉じがliになってます。
「<i>(未定義)</li>」ってなってる。
<tr> <td class="e">アクションクラス</td> <td class="v" colspan="2"> {$action.action_class}{if $action.action_class_info.undef}<i>(未定義)</li>{/if} </td> </tr> <tr> <td class="e">アクションフォーム</td> <td class="v" colspan="2">{$action.action_form|default:"<i>(未定義)</i>"}{if $action.action_form_info.undef}<i>(未定義)</li>{/if}</td> </tr>
きっと、次期リリース版では修正されていると思うので、今度、開発中のバージョンとか落として見てみようと思います。どこで落とすんだろう?
使い方
で、肝心の使い方です。
Ethna_Renderer_Php.phpは、libの下か、Ethna/class/Renderer/ あたりに置いてください。
app以下の、{appId}_Controller.phpの中で使用するクラス定義をします。
142行目あたり
/** * @var array クラス定義 */ var $class = array( /* * TODO: 設定クラス、ログクラス、SQLクラスをオーバーライド * した場合は下記のクラス名を忘れずに変更してください */ 'class' => 'Ethna_ClassFactory', (略) 'view' => 'Mm_ViewClass', 'renderer' => 'Ethna_Renderer_Php', // <-ここを変更 'url_handler' => 'Mm_UrlHandler',
テンプレートを .phpの拡張子にする場合には、もう一箇所変更します。
119行目あたり
/** * @var array 拡張子設定 */ var $ext = array( 'php' => 'php', 'tpl' => 'php', // <-ここを変更 );
テンプレート内では、app、app_ne、formは普通に変数としてアクセスできます。
それぞれ、配列になっているので、そのままアクセスします。
例)
<?php echo $app['name'] ?> <?php echo $app_ne['name'] ?> <?php echo $form['name'] ?>
フォーム関連は、例によって未作成。
実際に、これを使ってみた例の一つとして、info.tplをphpに書き直してみました。
foreachelseのところとか実装が変かもしれませんが、とりあえず。
以下、実際のファイルです。
Ethna_Renderer_Php.php
<?php // vim: foldmethod=marker /** * Ethna_Renderer_Php.php * * @author maru_cc <tomoyuki [dot] maruta [at] gmail [dot] com> * @license http://www.opensource.org/licenses/bsd-license.php The BSD License * @package Ethna * @version $Id: $ */ // {{{ Ethna_Renderer_Php /** * Phpレンダラクラスって(笑 * * @author Kazuhiro Hosoi <hosoi@gree.co.jp> * @access public * @package Ethna */ class Ethna_Renderer_Php extends Ethna_Renderer { /** @var string compile directory */ var $compile_dir; /** @var array template data */ var $_data=array(); /** * Ethna_Renderer_Phpクラスのコンストラクタ * * @access public */ function Ethna_Renderer_Php(&$controller) { parent::Ethna_Renderer($controller); $template_dir = $controller->getTemplatedir(); $this->setTemplateDir($template_dir); ///TODO $this->_setDefaultPlugin(); } /** * デフォルトの設定. * * @access public */ function _setDefaultPlugin() { ///TODO } /** * ビューを出力する * * @param string $template テンプレート名 * @param bool $capture true ならば出力を表示せずに返す * * @access public */ function perform($template = null, $capture = false) { if ($template === null && $this->template === null) { return Ethna::raiseWarning('template is not defined'); } if ($template !== null) { $this->template = $template; } $config = $this->controller->config->config; $app = $this->_data['app']; $app_ne = $this->_data['app_ne']; $form = $this->_data['form']; $data = $this->_data; if ((is_absolute_path($this->template) && is_readable($this->template)) || is_readable($this->template_dir . $this->template)) { $template_path = $this->template; if (!is_file($template_path)) { $template_path = $this->template_dir . '/' . $this->template; } if ($capture === true) { ob_start(); include $template_path; $captured = ob_get_contents(); return $captured; } else { include $template_path; } } else { return Ethna::raiseWarning('template not found ' . $this->template); } } /** * テンプレート変数を取得する * * @param string $name 変数名 * * @return mixed 変数 * * @access public */ function &getProp($name = null) { if (is_set($this->_data[$name])) { return $this->_data[$name]; } return null; } /** * テンプレート変数を削除する * * @param name 変数名 * * @access public */ function removeProp($name) { unset($this->_data[$name]); } /** * テンプレート変数に配列を割り当てる * * @param array $array * * @access public */ function setPropArray($array) { if (is_array($array)) { foreach ($array as $key=>$val) { $this->_data[$key] = $val; } } } /** * テンプレート変数に配列を参照として割り当てる * * @param array $array * * @access public */ function setPropArrayByRef(&$array) { if (is_array($array)) { foreach ($array as $key=>$val) { $this->_data[$key] =& $array[$key]; } } } /** * テンプレート変数を割り当てる * * @param string $name 変数名 * @param mixed $value 値 * * @access public */ function setProp($name, $value) { $this->_data[$name] = $value; } /** * テンプレート変数に参照を割り当てる * * @param string $name 変数名 * @param mixed $value 値 * * @access public */ function setPropByRef($name, &$value) { $this->_data[$name] =& $value; } /** * プラグインをセットする * * @param string $name プラグイン名 * @param string $type プラグインタイプ * @param mixed $plugin プラグイン本体 * * @access public */ function setPlugin($name, $type, $plugin) { ///TODO } } // }}} ?>
Ethna/tpl/info.tpl
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html;charset=EUC-JP" /> <style type="text/css"> <!-- body { margin: auto; background-color: #ffffff; color: #000000; } body, td, th, h1, h2 {font-family: sans-serif;} pre {margin: 0px; font-family: monospace;} a:link {color: #000099; text-decoration: none; background-color: #ffffff;} a:hover {text-decoration: underline;} table { margin: auto; border-collapse: collapse;} .center {text-align: center;} .center table { text-align: left;} .center th { text-align: center !important; } td, th { border: 1px solid #000000; font-size: 75%; vertical-align: top;} h1 {font-size: 150%;} h2 {font-size: 125%;} .p {text-align: left;} .e {background-color: #ccccff; font-weight: bold; color: #000000;} .h {background-color: #9999cc; font-weight: bold; color: #000000;} .v {background-color: #cccccc; color: #000000;} i {color: #666666; background-color: #cccccc;} img {float: right; border: 0px;} hr {width: 600px; background-color: #cccccc; border: 0px; height: 1px; color: #000000;} .header { margin: auto; width:600px; background-color: #9999cc; padding:0.2em; border:solid 1px black; } //--> </style> <title><?php echo $app['app_id'] ?> - Ethna Info</title> </head> <body> <div class="center"> <div class="header"> <h1><?php echo $app['app_id'] ?></h1> </div> <br /> <hr /> Contents <hr /> <table border="0" cellpadding="3" width="600"> <tr> <td style="border:0px"> <ul> <li><a href="#actions">Actions</a></li> <ol> <?php foreach ($app['action_list'] as $action_name=>$action): ?> <li><a href="#action_<?php echo $action_name ?>"><?php echo $action_name?></a></li> <?php endforeach; ?> </ol> <li><a href="#forwards">Forwards</a></li> <ol> <?php foreach ($app['forward_list'] as $forward_name=>$forward): ?> <li><a href="#action_<?php echo $forward_name ?>"><?php echo $forward_name?></a></li> <?php endforeach; ?> </ol> <li><a href="#configuration">Configuration</a></li> <li><a href="#plugins">Plugins</a></li> <ol> <?php foreach ($app['plugin_list'] as $plugin_type=>$plugin): ?> <li><a href="#action_<?php echo $plugin_type ?>"><?php echo $plugin_type?></a></li> <?php endforeach; ?> </ol> </ul> </td> </tr> </table> <br /> <a name="actions"></a> <h1>Actions</h1> <table border="0" cellpadding="3" width="600"> <?php foreach ($app['action_list'] as $action_name=>$action): ?> <tr class="h"> <th colspan="3"><a name="action_<?php echo $action_name ?>"></a><?php echo $action_name ?></th> </tr> <tr> <td class="e">アクションクラス</td> <td class="v" colspan="2"> <?php echo $action['action_class'] ?><?php if ($action['action_class_info']['undef']): ?><i>(未定義)</i><?php endif; ?> </td> </tr> <tr> <td class="e">アクションフォーム</td> <td class="v" colspan="2"><?php echo isset($action['action_form'])?$action['action_form']:'<i>(未定義)</i>' ?><?php if ($action['action_form_info']['undef']): ?><i>(未定義)</i><?php endif; ?></td> </tr> <tr> <?php if (count($action['action_form_info']['form']) == 0): ?> <td class="e">フォーム値</td> <?php else: ?> <td class="e" rowspan="<?php echo count($action['action_form_info']['form']) ?>">フォーム値</td> <?php endif; ?> <?php $cnt=0; $loopcnt=count($action['action_form_info']['form']); if ($loopcnt): foreach ($action['action_form_info']['form'] as $form_name=>$form): $cnt++; ?> <?php if ($cnt!=1): ?><tr><?php endif; ?> <td class="v"><?php echo $form_name ?></td> <td class="v"> <table cellpadding="1"> <tr> <td style="border:0px; font-size:100%;">表示名</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['name'])?$form['name']:"<i>未定義</i>" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">必須</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['required'])?$form['required']:"false" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">最大値</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['max'])?$form['max']:"<i>未定義</i>" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">最小値</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['min'])?$form['min']:"<i>未定義</i>" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">正規表現</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['regexp'])?$form['regexp']:"<i>未定義</i>" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">チェックメソッド</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['custom'])?$form['custom']:"<i>未定義</i>" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">変換フィルタ</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['filter'])?$form['filter']:"<i>未定義</i>"|nl2br ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">フォームタイプ</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['form_type'])?$form['form_type']:"<i>未定義</i>" ?></td> </tr> <tr> <td style="border:0px; font-size:100%;">タイプ</td> <td style="border:0px; font-size:100%;"><?php echo isset($form['type'])?$form['type']:"<i>未定義</i>" ?></td> </tr> </table> </td> <?php if ($cnt != $loopcnt): ?></tr><?php endif; ?> <?php endforeach; else: ?> <td class="v" colspan="2"></td> <?php endif; ?> </tr> <tr> <?php if (count($action['action_class_info']['return']['prepare']) == 0): ?> <td class="e">遷移先(prepare)</td> <?php else: ?> <td class="e" rowspan="<?php echo count($action['action_class_info']['return']['prepare']) ?>">遷移先(prepare)</td> <?php endif ?> <?php $cnt=0; $loopcnt=count($action['action_class_info']['return']['prepare']); if ($loopcnt): foreach ($action['action_class_info']['return']['prepare'] as $forward): $cnt++ ?> <?php if ($cnt!=1): ?><tr><?php endif; ?> <td class="v" colspan="2"><?php echo $forward ?></td> <?php if ($cnt!=$loopcnt): ?></tr><?php endif; ?> <?php endforeach; else: ?> <td class="v" colspan="2"></td> <?php endif; ?> </tr> <tr> <?php if (count($action['action_class_info']['return']['perform']) == 0): ?> <td class="e" >遷移先(perform)</td> <?php else: ?> <td class="e" rowspan="<?php echo count($action['action_class_info']['return']['perform']) ?>">遷移先(perform)</td> <?php endif ?> <?php $cnt=0; $loopcnt=count($action['action_class_info']['return']['perform']); if ($loopcnt): foreach ($action['action_class_info']['return']['perform'] as $forward): $cnt++ ?> <?php if ($cnt!=1): ?><tr><?php endif; ?> <td class="v" colspan="2"><?php echo $forward ?></td> <?php if ($cnt!=$loopcnt): ?></tr><?php endif; ?> <?php endforeach; else: ?> <td class="v" colspan="2"></td> <?php endif; ?> </tr> <?php endforeach; ?> </table> <br /> <a name="forwards"></a> <h1>Forwards</h1> <table border="0" cellpadding="3" width="600"> <?php foreach ($app['forward_list'] as $forward_name=>$forward): ?> <tr class="h"> <th colspan="2"> <a name="forward_<?php echo $forward_name ?>"></a> <?php echo $forward_name ?> </th> </tr> <tr> <td class="e">ビュークラス</td> <td class="v"><?php echo isset($forward['view_class'])?$forward['view_class']:"<i>未定義</i>" ?></td> </tr> <tr> <td class="e">テンプレートファイル</td> <td class="v"><?php echo $forward['template_file'] ?></td> </tr> <?php endforeach; ?> </table> <br /> <a name="configuration"></a> <h1>Configuration</h1> <table border="0" cellpadding="3" width="600"> <?php foreach ($app['configuration'] as $section_name=>$section): ?> <?php if ($section_name): ?> <tr class="h"> <th class="h" colspan="2"><?php echo $section_name ?></th> </tr> <?php endif ?> <?php if (count($section) == 0): ?> <tr> <td class="v" colspan="2">N/A</td> </tr> <?php else: ?> <?php foreach ($section as $entry_name=>$entry): ?> <tr> <td class="e"><?php echo $entry_name ?></td> <td class="v"><?php echo nl2br($entry) ?></td> </tr> <?php endforeach; ?> <?php endif; ?> <?php endforeach; ?> </table> <br /> <a name="plugins"></a> <h1>Plugins</h1> <table border="0" cellpadding="3" width="600"> <?php foreach ($app['plugin_list'] as $plugin_type=>$plugin): ?> <tr class="h"> <th colspan="2"> <a name="plugin_<?php echo $plugin_type ?>"></a> <?php echo $plugin_type ?> </th> </tr> <?php foreach ($plugin as $plugin_name=>$plugin_class): ?> <tr> <td class="e"><?php echo $plugin_name ?></td> <td class="v"><?php echo $plugin_class ?></td> </tr> <?php endforeach; ?> <?php endforeach; ?> </table> <br /> <hr /> powered by <a href="http://ethna.jp/">Ethna <?php echo $app['ethna_version'] ?></a> (experimental) <hr /> </div> </body> </html>