2011年3月の英語版を元に作成しています。
翻訳ミスなどありましたらtetsuya@wardish.jpまでお願いします。
PHP Template Attribute Language
Laurent Bédubourg
Kornel Lesiński
Dan Sheppard
Anton Andriyevskyy
- 1. イントロダクション
- 2. なぜPHPTALを使うのか
- 3. インストール
- 4. 最初の例
- 5. Template Attribute Language
- 6. PHP による開発
- 7. システム管理者のために
- 8. Useful links
- 9. 謝辞
1. イントロダクション
PHPTALは Zope Page Template (ZPT)のPHPによる実装です。PHPTALはTAL、METAL、I18N namespaces をサポートしています。
PHPTALESはTALES(Template Attribute Language Expression Syntax)に相当しており、XMLのattributeが処理される方法を定義しています。
PHPTALESはTALESと相似しているため、pythonで書かれたTALテンプレートを容易にPHPへ(逆もまた同様に)移植できます。
TALに対応するために、PHPTALはデータに対するXPath的なアクセスを実装しています。
PHPTALはLaurent Bedubourg <lbedubourg@motion-twin.com>によって開発され、by Kornel Lesinski によってメンテナンスされており、LGPLライセンスの元で自由に配布することができます。
2. なぜPHPTALを使うのか
XML/HTMLテンプレートはウェブサービスにおけるロジックとプレゼンテーション(デザイン)を分離するために存在しています。
この分離によって、以下のようなメリットを得ることができます。
- よりよいアプリケーションのデザイン
- 作業分割の容易さ
- 優れたメインテナンス性
- easy web skins
ほとんどのテンプレートシステムがセクション検知のために<? ?>、<% %> もしくは <xxx:yyy></xxx:yyy> のようなタグを使います。 これらはテンプレートシステムの開発を容易にしますが、テンプレートのデザイナーにとっては助けになりません。
TALは、そのほとんどのロジックをXMLの属性として隠蔽するので、XHTMLの構造と文法をくずすことはありません。 なので、TALのテンプレートはWebブラウザ(WYSIWYGエディタ等のライブプレビューでも)でプレビューできますし、プログラマーのエディタによるハイライト表示も可能です。 もしあなたが一般的なテンプレートシステムを使っているなら、以下のようなコードとなるでしょう。
<table>
<%loop myarray as myitem %>
<tr>
<td><% myitem %></td>
</tr> <%/loop%>
</table>
これをPHPTALで書くとこうなります。
<table>
<tr tal:repeat="myitem myarray">
<td tal:content="myitem"> text replaced by the item value
</td>
<td tal:replace="">sample 1</td>
<td tal:replace="">sample 2</td>
<td tal:replace="">sample 3</td>
</tr>
</table>
上記のコードはWebブラウザー上で正しくサンプルテキストを表示できますから、もし変数’myarray’がまだ存在していなくてもあなたのクライアントに対してプレゼンテーションを行うことができます。
それ以外のPHPTALの大きなアドバンテージとしては、PHPTALはZopeコミュニティにより3年以上もの間、経験、ドキュメント、例、およびヘルプが蓄積されていることが挙げられます。 PHPTALは、このコミュニティによる豊富な提供物を享受することができます。
PHPTALは開発者やパフォーマンスを要求するシステムのためにカスタマイズできるように設計されています。 が、快適さとシンプルな振る舞いで初心者でも簡単に使えるままにしておいてください。(私はそう努めています:)
3. インストール
PHPTALはPEARパッケージ(http://pear.php.netを参照)によってリリースされています。 PHPTALのライブラリーはPHPTALのウェブサイト(http://phptal.org)からダウンロードすることができます。
PEARのユーティリティを使う場合は以下のようにインストールします
pear install http://phptal.org/latest.tar.gz
一度インストールしてしまえば、PEARを使ってPHPTALをアップグレードすることができます
pear upgrade http://phptal.org/latest.tar.gz
もしあなたがPEARを使いたくない場合や、あなたのシステムにインストールされていない場合は、アーカイブをダウンロードした後に解凍することでもインストールできます。
tar zxvf PHPTAL-X.X.X.tar.gz cp -r PHPTAL-X.X.X/PHPTAL* /path/to/your/lib/folder
これによりPHPTAL.php と PHPTALフォルダが /path/to/your/lib/folder にインストールされます。
4. 最初の例
PHPTALの使い勝手を体験するためには、多くの言葉よりも簡単な例のほうが良いでしょう。
以下のような妥当なxml/htmlドキュメント(ルートエレメントを持った)を用意し、’my_template_file.xhtml’と名前を付けてください。
<?xml version="1.0"?>
<html>
<head>
<title tal:content="title">
Place for the page title
</title>
</head>
<body>
<h1 tal:content="title">sample title</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr tal:repeat="person people">
<td tal:content="person/name">person's name</td>
<td tal:content="person/phone">person's phone</td>
</tr>
<tr tal:replace="">
<td>sample name</td>
<td>sample phone</td>
</tr>
<tr tal:replace="">
<td>sample name</td>
<td>sample phone</td>
</tr>
</tbody>
</table>
</body>
</html>
php側では、PHPTALライブラリをインクルードし、テンプレートシステムのいくつかの項目を設定してください。
<?php
require_once 'PHPTAL.php';
// 新しいテンプレートオブジェクトを生成
$template = new PHPTAL('my_template_file.html');
// Person class
class Person {
public $name;
public $phone;
function Person($name, $phone) {
$this->name = $name;
$this->phone = $phone;
}
}
// テスト用の配列オブジェクトを作成
$people = array();
$people[] = new Person("foo", "01-344-121-021");
$people[] = new Person("bar", "05-999-165-541");
$people[] = new Person("baz", "01-389-321-024");
$people[] = new Person("quz", "05-321-378-654");
// テンプレートコンテキスト内にいくつかの値をセット
$template->title = 'The title value';
$template->people = $people;
// テンプレート実行
try {
echo $template->execute();
}
catch (Exception $e){
echo $e;
}
?>
PHPスクリプトを実行すれば、以下のような結果が得られます。
<?xml version="1.0"?><html>
<head>
<title>The title value</title>
</head>
<body>
<h1>The title value</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr>
<td>foo</td>
<td>01-344-121-021</td>
</tr>
<tr>
<td>bar</td>
<td>05-999-165-541</td>
</tr><tr> <td>baz</td>
<td>01-389-321-024</td>
</tr>
<tr>
<td>quz</td>
<td>05-321-378-654</td>
</tr>
</tbody>
</table>
</body>
</html>
PHPTALは出力の際にファイル中の改行やインデントをあまり意識しません。 もしあなたが出力されるHTMLがきれいである(改行とインデントが施されている)ことを望むのであれば、HTML Tidyによって後処理を行ってください。
5. Template Attribute Language
目次
- 5.1. 属性の優先度
- 5.2. TAL namespace
- 5.3. METAL namespace
- 5.4. I18N namespace
- 5.5. PHPTAL namespace
- 5.6.
tal:block - 5.7. PHPTALES
このセクションではTALとその拡張について説明します。これは主にテンプレートのデザイナーを対象としていますが、PHPの開発者も同様に読んでおいてください。
5.1. Attribute の優先度
属性の宣言の順序は無関係であることに注意してください。
<span tal:define="usersList application/listUsers"
tal:condition="somecondition"
tal:repeat="user usersList"
>...</span>
これは、以下と全く同一です。
<span tal:repeat="user usersList"
tal:condition="somecondition"
tal:define="usersList application/listUsers"
>...</span>
優先度はTALの仕様と同等です。
- define
- condition
- repeat
- content or replace
- attributes
- omit-tag
5.2. TAL namespace
- 5.2.1.
tal:define - 5.2.2.
tal:condition - 5.2.3.
tal:repeat - 5.2.4.
tal:omit-tag - 5.2.5.
tal:replace - 5.2.6.
tal:content - 5.2.7.
tal:attributes - 5.2.7.1. 付加的な属性
- 5.2.8.
tal:on-error
TALの名前空間URIはhttp://xml.zope.org/namespaces/talです。XML中でtal:のattributeを使うのであれば、以下の記述が必要になります。
<html xmlns:tal="http://xml.zope.org/namespaces/tal">
5.2.1. tal:define
このattributeは、このテンプレート上における変数を定義します。
一度に一つ、ないしはセミコロンで区切ることで一つ以上を定義することができます。
これは長いパスへのショートカットの例です。
<span tal:define="destname path/to/existing/variable" />
複数の変数を同時に定義します。
<span tal:define="fname string:Paul; lname string:Dupond">...</div>
globalキーワードと共に変数を定義すると、その変数はグローバル変数としてテンプレートやマクロのどこからでも参照できるようになります。
グローバル変数は再定義することができます。
<span tal:define="global hello string:hello world" /> <p tal:content="hello" />
一方、ローカル変数はその変数が定義されたエレメントの中(およびその中で呼ばれたマクロ内)でのみ有効となります。
<span tal:define="hello" string:hello world" /> <p tal:content="hello" /> <-- will preduce an undefined variable error -->
Tips
tal:defineを他の属性と共に使用した場合、同一エレメント内では他の属性よりも先に実行されます。
文字列をテンプレート中で定義することができます。
<span tal:define="destname string:some string" />
他の変数を含んだ変数を定義します。
<span tal:define="fname hello string:Paul; hello string:Hello $fname! Welcome on this page" >...</div>
エレメントのコンテントを利用した小技です。(複雑な値を定義したいときに便利です)
<span tal:define="global hello">hello ${fname} welcome on this page</span>
上記の例では、spanタグは表示されません。なぜなら、出力対象となるコンテントでもattributesでも無いからです。 (コンテントはhello変数によって保持されます)
5.2.2. tal:condition
評価した結果が真の場合のみ、このエレメントとコンテントを出力します。
<span tal:condition="identified"> Welcome member ... </span>
<span tal:condition="not: identified"> このページにアクセスする前にログインしてください。 </span>
もしPHP側でメソッドが用意されていない場合、特定の状態のためにテンプレート側でPHPによる処理を記述する必要があります。
<span tal:comment="五つ以上の商品がカートにある場合のみ表示します"
tal:condition="php: cart.countItems() GT 5">...</span>
これではテンプレート内に余計な処理が入ってしまいます。多くの場合、論理属性やアクセスしやすいメソッドを提供することが望ましいでしょう。
<span tal:condition="cart/hasEnoughItems">...</span>
5.2.3. tal:repeat
このattributeは配列や連想配列、PHP5のIteratorクラスを実装したオブジェクトといった反復可能なオブジェクトを扱います。
このrepeatattributeはそのエレメントとそのコンテントを、指定されたリソースの終わりが来るまで繰り返します。
<tr tal:repeat="item some/result"> <td tal:content="item">この文字はitemで置き換えられます</td> </tr>
ループの間、repeat/*パスを使うことで、現時点のループの(およびその親のループの)情報を参照することができます。
repeat/item/key: some/resultが連想配列であればキー(でなければインデックス)が返されますrepeat/item/index: itemのインデックスが返されます(0からcount -1まで)repeat/item/number: 何番目のitemであるかが返されます(1からcountまで)repeat/item/even: itemのインデックスが偶数であれば真が返されますrepeat/item/odd: itemのインデックスが奇数であれば真が返されますrepeat/item/start: 最初のitemであれば真が返されますrepeat/item/end: 最後のitemであれば真が返されますrepeat/item/length: some/resultの総数が返されます
“item“はtal:repeat式の中で戻り値として定義された変数です。
多くの場合tal:repeatは何かしらのSQLの結果に対して利用されます。 以下のコードはplayersRankingがPHPのIteratorを実装したオブジェクトである場合に動作します。
<table>
<thead>
<tr>
<th>Position</th>
<th>Player</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr tal:repeat="ranking playersRanking">
<td tal:content="ranking/position"/>
<td tal:content="ranking/player"/>
<td tal:content="ranking/score"/>
</tr>
</tbody>
</table>
5.2.4. tal:omit-tag
このattributeはPHPTALのパーサーに対して、開始タグと終了タグを無視するように指示します。 ただし、内容は評価されます。
<span tal:omit-tag="condition"> もしconditionが真の場合、</span>このテキストのみが表示され、spanの開始タグと終了タグは消去されます。
出力は以下のようになります。
このテキストのみが表示され、spanの開始タグと終了タグは消去されます。
このattributeは任意のエレメントを生成したいとき、たとえば特定の条件下でリンクを隠したい場合などに役に立ちます。
もしエレメントも出力したくないならtal:blockを使ってください。
<tal:block tal:repeat="x php:range(1,10)">このテキストは10回しか表示されません</tal:block>
5.2.5. tal:replace
このattributeはタグ全体を値で置き換えます。 値が無い場合は何も表示しません。
<span tal:replace="string:美しい文字列"> 見苦しい文字列とspan </span>
出力は以下のようになります。
美しい文字列
tal:replace はテンプレート中のサンプルとして記載したいが、最終的な成果物からは取り除きたい場合に利用できます。
<table>
<tr tal:repeat="item myresult">
<td tal:content="item">item value</td>
</tr>
<tr tal:replace="">
<td>sample 1</td>
</tr>
<tr tal:replace="">
<td>sample 2</td>
</tr>
</table>
5.2.6. tal:content
このattributeはタグのコンテントを式の実行結果で置き換えます。
<span tal:define="myvar string:オレの文字列"/> <span tal:content="myvar">この文字は置き換えられます</span>
出力は以下のようになります。
<span>オレの文字列</span>
5.2.7. tal:attributes
このattributeはタグ属性の値を置き換えます。(複数指定可)
<a href="http://www.foo.com" title="some foo link" tal:attributes="href somelink/href; title somelink/title" tal:content="somelink/text" >sample link</a>
‘somelink‘ が以下であった場合
$somelink->href = "http://www.google.com"; $somelink->title = "google search engine"; $somelink->text = "the google search engine";
出力は以下のようになります。
<a href="http://www.google.com" title="google search engine">the google search engine</a>
セミコロン(;)で属性を区切ることができます。 もし、セミコロン自体を属性に出力したいときは、二つ重ねてください(;;)。
以下はtal:repeatと組み合わされた、若干複雑な例です。
<tr tal:repeat="ranking playerRankings"
tal:attributes="class php: repeat.ranking.odd ? 'odd' : NULL">
...
</tr>
この php: モディファイアは後ほど詳しく説明しますが、ここでは行が偶数ならtrのclass属性として値に”odd”を設定し、そうでなければclass属性そのものを設定しません。
この”condition ? then : else” は慎重に利用しなければならないPHPの書式ですが、今回はメリットが大きいと判断して利用しています。
同じ結果を得るためのより良い方法は、ロジックの担当者に対して以下のようなニーズに合ったカスタムモディファイア(PHP による開発 / カスタムモディファイア を参照)を作成してもらうことです。
<tr tal:repeat="ranking playerRankings"
tal:attributes="class css-odd:repeat/ranking/odd">
...
</tr>
このモディファイアはrepeat/ranking/oddがtrueの場合に”odd”を返し、falseの場合はNULLを返します。
OPTIONAL ATTRIBUTES
もしあなたが代替手段としてtal:attributesでnothing(もしくはPHPにおけるNULL)を使うのであれば、属性は追加されません。(これは空の属性が追加されることを避けるためです。)
... tal:attributes="title object/tooltip | nothing">
selectedやcheckedのようなXHTML属性は自動的に扱われます。
<input type="checkbox" tal:attributes="checked object/isChecked"/>
注意
XHMLは大文字と小文字を区別します。
SELECTED属性はXHTMLでは認められませんので、 selectedと指定してください。
5.2.8. tal:on-error
このattributeはtal:on-error式の評価において、パスの誤りを検出した場合やPHPのexception(例外)が投げられた場合に、タグのコンテントを置き換えます。
<span tal:on-error="string:ユーザー名が定義されていません"
tal:content="user/name">ユーザー名です</span>
もし’name’や’user’へのアクセスにエラーが発生した場合、エラーメッセージがタグの場所に表示されます。
定義されたタグのみでなく、エレメント中にまで作用します。
<span tal:on-error="string:error occurred somewhere"> <span tal:content="user/firstname"/> <span tal:content="user/lastname"/> <span metal:use-macro="userMenu" /> </span>
5.3. METAL namespace
METALのnamespaceはhttp://xml.zope.org/namespaces/metalです。metal: attributeを利用する際には以下の定義が必要となります。
<html xmlns:metal="http://xml.zope.org/namespaces/metal" ...>
Note
PHPTALにおいては必須ではありません。
METALは’Macro extension for TAL’という意味です。 このnamespaceによってデザイナーがXML/XHTMLのマクロを定義し、呼び出せるようになります。
マクロによって再帰的に出力を行ったり、他のテンプレートからコードを取り込むことができます。
5.3.1. metal:define-macro
このattributeはマクロを定義します。 マクロとは、小さなテンプレートを他の場所でも再利用できるライブラリのようなものと考えてください。
<div metal:define-macro="main_menu">
<ul>
<li><a href="/">home</a></li>
<li><a href="/products">products</a></li>
<li><a href="/contact">contact</a></li>
</ul>
<div>
Last modified:
<span tal:content="mdate">page modification date</span>
</div>
</div>
マクロは呼び出し元から全ての変数を引き継ぎます。上記の例では、変数’mdate’はマクロの呼び出し元のテンプレートに依存します。
5.3.2. metal:use-macro
このattributeはマクロを呼び出し、テンプレート内に展開します。
<span tal:comment="main_menu template requires 'mdate' variable" tal:define="mdate page/last_modified" metal:use-macro="main_menu" />
外部のテンプレートで宣言されたマクロも参照できます。
<span metal:use-macro="site_macros.xhtml/main_menu"/>
use-macro attribute内では、PHPTALの変数展開を使うことができます。
<span metal:use-macro="${design}/site_macros.html/main_menu"/>
マクロは自分自身を呼び出すこともできますので、配列を再帰的に出力することができます。
<ul metal:define-macro="show-list">
<li tal:repeat="item list">
<tal:block tal:condition="php:is_array(item)" tal:define="list item" metal:use-macro="show-list" />
<tal:block tal:condition="php:!is_array(item)" tal:content="item" />
</li>
</ul>
コールバック
マクロの名称を設定した変数を利用して、あるマクロを他のマクロにコールバックさせることができます。これはslotでは十分でない場合に有用です。
<-- このコードでは"macroname"変数をコールバック用に利用しています -->
<ul metal:define-macro="macro-with-callback">
<li tal:repeat="item list">
<tal:block metal:use-macro="${macroname}"/>
</li>
</ul>
<-- コールバック定義 -->
<div metal:define-macro="my-callback">
いつでも呼び出せます
</div>
<-- 最初に定義したマクロで利用します -->
<div tal:define="macroname 'my-callback'" metal:use-macro="macro-with-callback" />
5.3.3. metal:define-slot
このattributeはmetal:define-macroタグの中に現れなければなりません。
スロットは呼び出し元のテンプレート側で置き換える事ができます。
スロットはincludeの逆と考えることができ、マクロはページ全体やスロットがこのページをURLに応じて改変することができます。
たとえば、スロットにはホームページの最近のニュースやログインユーザーのアクション一覧を表示することができます。
<span metal:define-slot="news_place">
<table>
<tr tal:repeat="item php:latestNews()">
<td tal:content="item/value">news description</td>
</tr>
</table>
</span>
上記の例では、’news_place‘という名前で定義された場所は、呼び出し元のテンプレートで上書きすることが可能です。 引き続き次のセクションを見てください。
注意
スロットは変数の用に振る舞います。コールバックではありません。
tal:repeat内のmetal:define-slogは同じ値で何度も繰り返されます。
5.3.4. metal:fill-slot
このattributeはmetal:use-macro内でのみ使うことができます。
この宣言はPHPTALに対し、定義されたスロットをmetal:fill-slotattribute内のコンテンツで置き換えるよう指示します。
<span tal:condition="logged" metal:fill-slot="news_place">
<h2>user menu</h2> <ul>
<li><a href="/user/action/inbox">inbox</a></li>
<li><a href="/user/action/new">new mail</a></li>
<li><a href="/user/action/disconnect">disconnect</a></li>
</ul>
</span>
スロットは、プッシュ方式によってカスタマイズできるテンプレートの定義を可能にします。
5.4. I18N namespace
namespaceはhttp://xml.zope.org/namespaces/i18nです。i18n: attributeを利用する際には以下の定義が必要となります。
<html xmlns:metal="http://xml.zope.org/namespaces/i18n" ...>
Note
PHPTALにおいては必須ではありません。
Note
‘i18n’ は’internationalization’の略です。
このnamespaceによって、デザイナーはテンプレートの実行時に翻訳しなければならないテキストゾーンを指定することができます。
5.4.1. i18n:translate
このattributeはPHPTALの翻訳システムを利用して翻訳しなければならない文を定義します。
<div i18n:translate="string:welcome_message">Welcome here</div>
上記の例では、PHPTALは’welcome_message’という名前のキーを探し、タグの内容を要求された言語で置き換えます。
<div i18n:translate="">Welcome here</div>
この用法は少し異なっています。 translationキーが指定されていないので、PHPTALはタグの内容である’Welcome here’をキーとして利用します。
翻訳システムがキー’Welcome here’を理解できるのであれば、正常に翻訳されます。
もし翻訳が見つからなければ、キーが翻訳結果として利用されますので、キーの代わりに文章を使用するのが良い選択であるかもしれません。
覚えておいていただきたいのは、このキーは、動的なキー選択のために変数を含むことができるということです。
<div tal:define="welcome random_welcome_message"/> <div i18n:translate="welcome">...</div>
5.4.2. i18n:attributes
どの属性が翻訳されるべきか定義します。 i18n:translateにはセミコロンで分割された、属性とキーのリストで指定します。
<img i18n:attributes="alt 'picture alternative text';title thetitle" alt="Picture" title="${thetitle}" />
5.4.3. i18n:name
このattributeは翻訳キー内の置換変数に値を設定します。
翻訳キーは、しばしば${xxx}(置換変数)を含み、”xxx“という名前の変数によって必要に応じて動的に補間されます。
この変数の値は、タグとその内容で有効となります。 もし値にタグが必要でないなら、tal:contentの代わりにtal:replaceを使ってください。 tal:omit-tagは、翻訳キーが長い文字列である場合に便利です。
<span i18n:name="myVar" tal:content="some/path"/>
<!-- <span>${some/path}</span> --><span i18n:name="myVar" tal:replace="some/path"/>
<!-- ${some/path} -->
<span i18n:name="myVar">foo</span>
<!-- <span>foo</span> -->
<span i18n:name="myVar" tal:omit-tag="">foo</span>
<!-- foo -->
i18nの利用例です。
<div i18n:translate=""> Welcome <span i18n:name="user" tal:replace="user/name"/>, you have <span i18n:name="mails" tal:replace="user/nbrMails"/> unread mails. </div>
この例における翻訳キーは、このような値が考えられます。
"Welcome ${user}, you have ${mails} unread mails."
PHPTALは翻訳時に、${user}を${user/name}に、${mails}を${user/nbrMails}に置換します。
PHPTALのi18nに関するこれ以上の情報は、このサイトのPHPセクションを参照してください。
5.4.4. XHTML in translations
通常、翻訳はテキストだけと思われます。 ですので、PHPTALはすべての”<”をエスケープします。 バージョン1.1.14からはi18n:translate内でstructureを使うことでエスケープを無効化できます。 以下の例では、
<div i18n:translate="structure '<b>bold text</b>'" />
以下を得ます
<div><b>bold text</b></div>
注意
これは単純な場合のみ使用できます。
翻訳された文字内のTAL属性は無視されます。翻訳内のIll-formedなXHTMLはwell-formedなページを壊してしまいます。
5.5. PHPTAL namespace
PHPTALのnamespaceはhttp://phptal.org/ns/phptalです。phptal: attributeを利用する際には以下の定義が必要となります。
<html xmlns:phptal="http://phptal.org/ns/phptal" ...>
Note
PHPTALにおいては必須ではありません。
この属性はTALの仕様によって定義されているわけではありません。 しかしPHPTALで作業する場合には有用です。
5.5.1. phptal:debug
この属性は、それが定義されたタグの内容に対して、PHPTALのデバッグ指定を切り替えます
Note
テンプレート内で呼ばれたマクロのエラーをデバッグするためには、マクロを定義するテンプレートにもphptal:debugを指定する必要があります。
デバッグモードでは、ファイル名やテンプレートの行番号を格納しますので、不正なパスにより例外が投げられた場合に、より多くの情報を得ることができます。
<html>
<head>
...
</head>
<body>
<div id="menu">
...
</div>
<div id="leftPane" phptal:debug=""
tal:comment="this div seems buggy, keep
trace of where errors are thrown">
...
</div>
</body>
</html>
5.2. phptal:cache
この属性は、キャッシュ期限が切れるまで、このタグに含まれる要素すべてがディスク上にキャッシュされ、再評価されないようにします。
Note
キャッシュが有用なのは、外部ファイルのマクロや、データベースアクセスを伴うようなPHP式やオブジェクト等の複雑な要素の場合のみです。 それ以外の場合、非キャッシュされたテンプレートも同じくらい速いでしょう。
このattributeの内容の有効期間(どのくらいの間キャッシュされるのか)は、’d‘、’h‘、’m‘、’s‘サフィックスを伴う数値によって書くことができます。
<div class="footer" phptal:cache="3h">...</div>
上記の例では、<div> の中身は3時間に一度評価されます。 有効期間には”per“オプションをつけ、キャッシュがどのように共有されるか定義することができます。 これにより、このテンプレートを使うすべてのページでキャッシュを共有することができます。 “per url“を付加すると、URLごとのキャッシュを持たせることができます。
<ol id="breadcrumbs" phptal:cache="1d per url">...</ol>
<ol>は各ページ独立に1日間キャッシュされます。 “per expression“と追加することで、式の値ごとにキャッシュを保持することができます。(実行結果は文字列でなければなりません) ただし、同一のエレメント内で、tal:defineによって定義された変数を参照することができません。
<ul id="user-info" phptal:cache="25m per object/id">...</ul>
<ul>は、objectのidごとに25分間キャッシュされます。
注意
ユーザーのプライベートな情報をキャッシュする際は気をつけてください。
per user/idなどのようにユーザー毎に指定しない限り、全員で共有されてしまいます。
5.5.2.1. リフレッシュ
キャッシュをクリアする代わりに、perパラメータに対してバージョンや最終更新時刻を付加することは良いアイデアです。
これにより、キャッシュされたテンプレートはバージョンやタイムスタンプが変わるタイミングでリフレッシュされるため、キャッシュのクリアのための特別な処理が不要になります。
<div phptal:cache="100d per php:news.id . news.last_modified_date">...</div>
5.5.2.2. 制限:
phptal:cacheはネストすることができます。 しかし、一番外のブロックは、それらの新しさに関わらずネストしたブロックをキャッシュします。metal:fill-slotをphptal:cacheの中で使うことはできません。
5.5.3. phptal:tales
このattributeによってPHPTALESの挙動を帰ることができます。
通常の挙動はZPTの方法で属性式を解釈することですが、単にPHPが使いたい場合、いたるところでphp:モディファイアを使うことになります。
PHPTALについてのもう一つの問題としては、PHPTALがパスを解釈する方法です。
たとえば、 myobject/mymethod/property/10/othermethod/hashkeyは、解釈するには比較的長い時間がかかります(パスが長すぎるかどうかについて、あまり気にしすぎないでください。 最適化するのはそのパフォーマンスが実際に問題になってからにしてください)
PHPTALはmyobjectを受け、それがオブジェクトであることを(実行時に)決定したうえで、’mymethod’がそのオブジェクトの変数ではなくメソッドであることを決定します。 そしてそのメソッドをコールし、戻り値がプロパティをもったオブジェクトであることを決定し、それは配列であり、10番目の要素を持ち、そしてそれはまたオブジェクトであり、そのオブジェクトのothermethodは(変数ではなく)メソッドであり、それは戻り値を持ち、その戻り値はオブジェクトであり、キー値’hashkey’に関連した値を持つ。 と。
もちろんこれは極端な例ですし、このプロセスは十分速いので、私たちはあまり気にしません。 しかし、このようなとても長いパスが深いtal:repeatの中で呼ばれたら?D’oh!(ホーマー・シンプソンを参照) phptal:talesは以下のように私たちの助けになります。
<html>
<body>
<table phptal:tales="php">
<tr tal:repeat="myobject document.getChildren()">
<td tal:content="myobject.mymethod().property[10].otherMethod().hashkey"></td>
</tr>
</table>
</body>
</html>
上記の例は以下と同等です。
<html>
<body>
<table>
<tr tal:repeat="myobject php:document.getChildren()">
<td tal:content="php:myobject.mymethod().property[10].otherMethod().hashkey"></td>
</tr>
</table>
</body>
</html>
Note
‘php:’モディファイアについてはphp:の章で説明します。
5.6. tal:block
tal:blockは、多くのTAL属性をもつエレメントを表示させたくない場合のための糖衣文(シンタックスシュガー)です。
<tal:block define="myvar string:Some value"/>
これは以下と同等です。
<span tal:define="myvar string:Some value" tal:omit-tag=""/>
もう一つの例としては
<tal:block condition="someCondition" repeat="item someRepeat"> <div metal:use-macro="x"/> </tal:block>
これは以下と同等です。
<div tal:omit-tag=""
tal:condition="someCondition"
tal:repeat="item someRepeat">
<div metal:use-macro="x"/>
</div>
5.7. PHPTALES
- 5.7.1. path:
- 5.7.2. 条件式
- 5.7.3. string:
- 5.7.4. php:
- 5.7.5. not:
- 5.7.6. exists:
- 5.7.7. true
- 5.7.8. default
- 5.7.9. structure
- 5.7.10. 式の連結
PHPTALESはTALESに相当します。TALESはTemplate Attribute Language Expression Syntaxの略であり、TALやMETAL、PHPTAL attributesおよび、${…}といったインラインの式において使われます。
これまでの例で、いくつかのPHPTALESの例(string:、php:、not: …)を見てきたと思います。この章ではテンプレート中におけるPHPTALESの使い方について説明します。
TAL属性の値は大抵、一つ以上の式を含み(例:tal:define)、それらの式は’;‘によって分割されます。
5.7.1. path:
これはTALS式において、何のモディファイアも書かれていない場合のデフォルト動作です。
以下の行は同じ結果をもたらします。
<span tal:content="data/user/name"/>
<span tal:content="path:data/user/name"/>
<span>${data/user/name}</span>
${path/to/my/variable}の書式を使えば、テンプレート中や式中の文字列でコンテキスト変数を参照することができます。
<h1>${document/title}</h1><span tal:replace="string:welcome ${user/name},
this page has been readed ${page/countRead} times"/>
注意
存在しない変数を参照しようとした場合、PHPTALは例外を投げます。
変数が参照可能かどうか調べるにはexists:を利用してください。
5.7.2. 条件式
‘<’ も ‘>’ 属性式から排除しなければなりません。 PHPTALは同等の、伝統的な文字列による比較演算子を提供しています。
おそらく、このステートメントのほとんどはtal:condition attributeや、php: 式の中で使われるでしょう。
- < : LT (less than)
- > : GT (greater than)
- <= : LE (less or equal)
- >= : GE (greater or equal)
5.7.3. string:
式のセパレータは’;’であり、また、’$’マークはパスの開始を意味しますので、以下の様にエスケープする必要があります。
- ‘
;;‘ ‘;’ そのものを文字として使いたい場合場合 - ‘
$$‘ ‘$’ そのものを文字として使いたい場合
<span tal:replace="string:this is a $$100 page"/> string:foo $bar baz <!-- $bar が置換されます --> string:foo $$bar baz <!-- 置換は行われません --> string:foo ; php:doFoo() <!-- 二つの式として解釈されます --> string:foo ;; php:doFoo() <!-- "foo ; php:doFoo()" という文字列として解釈されます -->
5.7.4. php:
この式はPHPの式を実行しますが、’->’はドット’.’に置き換える必要があり、変数のプリフィクスである’$’は省かなければなりません。
空白を挟んだドット’.’は連結を表します。
php:htmlentities(foo)
php:'string ${varReplaced}'
php:'string ${some.path().to[0].var}'
php:NOT foo OR (bar GT baz)
php:a + b
php:array('a', 'b', 'c')
php:range(0, 90)
php:foo . a.b.c(e) . htmlentities(SomeClass::staticMethod())
php:SomeClass::ConstOfClass
php:SomeClass::$staticVar
php: 式は慎重に利用すべきであり、おそらくその8割の場合は必要ありません。 しかし、そのテンプレート中で動的に、ユーザーがログインしているかどうかに関わりなく、なんらかの特別なメソッドを呼ぶ必要がある場合、もしくは状態に依存した複雑な形式のデータを検索しなければならない場合などでは必要とされるでしょう。
5.7.5. not:
この式は一つの論理演算子です。 tal:condition内で便利です。
<span tal:condition="not: logged">not logged</span>
5.7.6. exists:
この式は、事前にパスが定義されていた場合にtrueを返し、そうでない場合にfalseを返します。 つまりPHPのisset()のように働きます。 通常、存在しないパスを利用様とした場合、”Cannot find variable ‘foo‘ in current scope”の様なエラーを投げます。 このようにして、不確実なパスを最初にチェックしておきます。
<span tal:condition="exists:user/preferences" tal:content="user/preferences"> Use user/preferences here if defined </span>
Tip
PHPTALESの内部でisset()が使用されます。
5.7.7. true
この式はパスが存在する場合、および、それがtrueを表す変数である場合にtrueを返します。
PHPの!empty()のような物です。
Tip
PHPTALESの内部で!empty()が使用されます。
5.7.8. default
これは式では無く、キーワードです。 エラーが起きた場合や、何かが定義されていない場合、替わりにタグの内容を値として利用することをデザイナーに許可します。
<span tal:define="myVar path/to/possible/var | default"> var のデフォルト値です </span> <span tal:content="some/var | other/path | default"> some/var も other/path も存在しない場合、この文字列が表示されます。 </span> <a href="unknown.html" title="Unknown page" tal:attributes="href item/href | default; title item/title | default" tal:content="item/title | default">存在しないページです</a>
上記の例では ‘|’ 文字を利用して替わりの定義や表示を許可しています。
5.7.9. structure
これも式ではなくキーワードです。
PHPTALテンプレート中で変数を表示する際は、出力のvalidityを確保するためにすべての変数をエンコードすることに注意してください。
とはいえ、HTMLやXMLをそのまま出力したい場合があるかもしれません。
<h1 tal:content="structure document/title"/><span tal:replace="structure document/content"/>
上記の例では、$document->title と $document->content の値は、そのまま出力可能なHTMLであると仮定してください。
5.7.10. 式の連結
式は ‘|’ で区切って連結することができます。
‘|’ で区切られた式を実行する際、PHPTALはその式の結果が非null、もしくはエラー無しで無ければ式の実行を停止します。
たとえば、string: 式は常にtrueを返しますので、その後にどのような式が続いたとしてもstring:の場所で式の実行は停止します。
連結式の中で、php:式を使うこともできます。
<h1 tal:content="page/title | page/alternativeTitle | php:get_default_title()" />
6. PHP による開発
目次
- 6.1. 定数
- 6.2.
PHPTALクラス - 6.3.
PHPTAL_PreFilterクラス - 6.4.
PHPTAL DOM - 6.5.
PHPTAL_Filterインターフェース - 6.6.
PHPTAL_Triggerインターフェース - 6.7.
PHPTAL_TranslationServiceインターフェース - 6.8. gettextによる処理
- 6.9. Creating custom expression modifiers
この章は開発者を対象とし、使い方、およびシンプルで高度なPHPTALのカスタマイズ方法について説明します。
PHPTAL: PHPTAL のメインとなるクラスです。 テンプレートを読み込み、実行するために使われます。PHPTAL_Filter: テンプレートやPHPTALの出力のフィルタリングを行います。PHPTAL_Trigger:phptal:id要素のハンドリングと出力を行います。PHPTAL_TranslationService: あなた自身の国際化システムのためにgettextを置き換えます。
1. 定数
唯一の定数PHPTAL_VERSIONがPHPTAL.phpで定義されています。
これはあなたのシステムにインストールされたPHPTALのバージョンを(x.x.xフォーマットで)表します。
古いバージョンには設定のための定数がありましたが、それらはメソッドで置き換えられました。
6.2. PHPTALクラス
ライブラリの主役となるクラスです。
<?php
// ライブラリをincludeします
require_once 'PHPTAL.php';
// 使用するテンプレートを指定してPHPTALオブジェクトを生成します
$tpl = new PHPTAL('mytemplate.xhtml');
// コンテキスト変数を設定します
$tpl->title = 'my title';
$tpl->values = array(1,2,3,4);
$tpl->user = new User('Joe');
// テンプレートを実行し、'安全'な方法でその戻り値を得ます
try {
echo $tpl->execute();
}
catch (Exception $e){
echo "Exception thrown while processing template\n";
echo $e;
}
?>
コンテキスト変数を設定した後でテンプレートを指定することもできます。
<?php
…
$tpl = new PHPTAL();
// setメソッドを使うかどうかは好みの問題です
$tpl->set('title', 'my title');
$tpl->set('values', array(1,2,3,4));
$tpl->set('user', new User('Joe'));
$tpl->setTemplate('mytemplate.xhtml');
…
?>
ファイルの代わりに文字列をテンプレートとして利用することもできます。
<?php
$src = <<<EOS
<html>
<head>
<title tal:content="title">my title</title>
</head>
<body>
<h1 tal:content="title">my title</h1>
</body>
</html>
EOS;
require_once 'PHPTAL.php';
$tpl = new PHPTAL();
$tpl->setSource($src);
$tpl->title = 'this is my title';
try {
echo $tpl->execute();
}
catch (Exception $e){
echo $e;
}
?>
上記の例では、PHPTALは$srcのmd5を一意な識別子として使用します。これは、PHPTALがテンプレートに一意な識別子を要求するためです。(大抵の場合は、テンプレートファイルのパスが使用されます。)
setSource()の第二引数を利用することで、識別子を指定することができます。
<?php
$src = <<<EOS
<html>
<head>
<title tal:content="title">my title</title>
</head>
<body>
<h1 tal:content="title">my title</h1>
</body>
</html>
EOS;
require_once 'PHPTAL.php';
$tpl = new PHPTAL();
$tpl->setSource($src, __FILE__);
$tpl->title = 'this is my title';
try {
echo $tpl->execute();
}
catch (Exception $e){
echo $e;
}
?>
6.2.1. 設定メソッド
PHPTALは可能な限り最適な標準設定を使用しますので、設定を変更する必要はありません。
PHPTALクラスのsetメソッドはそのクラスのインスタンスを返すので、メソッドチェーンにすることができます。
<?php
echo $phptal->setPhpCodeDestination('/tmp/phptal')->setOutputMode(PHPTAL::XML)->setTemplate('tpl.zpt')->execute();
?>
これらは以下と同等です。
<?php
$phptal->setPhpCodeDestination('/tmp/phptal');
$phptal->setOutputMode(PHPTAL::XML);
$phptal->setTemplate('tpl.zpt');
echo $phptal->execute();
?>
6.2.1.1. setOutputMode(mode)
mode)
出力の書式を変更できます。指定可能なモードは以下です。
PHPTAL::XHTML
標準ではこのモードが指定されます。
- 空のエレメントは、空要素タグ(<br />や<img/>等)を利用することが強制されます。空ではないエレメントは必ず閉じられます。
注意
XMLアウトプットモードはRSSと互換性の無い形に<link>エレメントを変換してしまいます。
RSSフィードやAtomを生成する場合はXMLモードを利用してください - 真偽値の属性(checked, selected 等)はXHTMLの仕様により、常に値を持ちます。
<[CDATA[ブロックは自動的に追加、もしくは削除され、XMLとHTMLの両方で安全な構文にエスケープされます。
Tip
もし常に
application/xhtml+xml形式で出力したい場合は、XMLアウトプットモードを利用してください。PHPTAL::HTML5
- このモードではHTMLブラウザの後方互換のためにXHTMLを出力します。
このモードでは現行のブラウザに適したtext/htmlでドキュメントを出力します。なお、XMLではありません。
PHPTALはDOCTYPEを<DOCTYPE html>に変換し、名前空間の定義や、属性のプリフィクス、明示的なCDATAセクションなどの、HTML非互換要素を削除します
Note
このモードは"tag soup"ではありません。 PHPTALは必要な場合であれば、全てのエレメントを閉じ、属性をクオートします。
出力は完全にHTML5準拠となり、また、現行のHTML4ブラウザとも後方互換が保たれます。
PHPTAL::XML
このモードは"純粋な"XMLを出力します。(
text/html
- との互換性はありません。)このモードはフィードや、SVG、MathML、RDFやその他のXML文書を生成する場合に利用してください。
6.2.1.2. setEncoding(encoding)
encoding)なたのテンプレートのエンコーディングが何であるかを指定します. デフォルトはUTF-8です。
PHPTALはすべてのテンプレートおよびドキュメントは同じエンコーディングであると仮定します。
UTF-8文書からBOMは削除されます。
Note
PHPTALはXMLファイルからencodingを読みません。また、エンコーディングの変換も行いません。
Tip
トラブルを避けるために、いつでもUTF-8を使うようにしましょう。
6.2.1.3. その他のメソッド
6.2.1.3.1. setTemplateRepository(string_or_array)
string_or_array)
テンプレートをどこから探すか指定します。 もし文字列を指定すれば検索パスの中に追加されます。
配列を指定すれば、検索パスを置き換えます。
すべてのテンプレートをルートに置かなくても良くなりますので、デザイナーの仕事と分担するためにサブフォルダーを使用することができます。
それは、実パスではなくリポジトリの中での相対パスによってテンプレートの参照を許可するための近道です。
6.2.1.3.2. setPhpCodeDestination(path)
path)
PHPTAL に一時的なPHPファイルの置き場所を指定します。 デフォルトでは、PHPのsys_get_tmp_dir()で指定されたディレクトリです。 大抵の場合それは'/tmp'ディレクトリです。
6.2.1.3.3. setPhpCodeExtension(string)
string)
一時的なPHPファイルの拡張子を指定します。 デフォルトでは'php'であり、変更する必要は無いでしょう。
6.2.1.3.4. setCacheLifetime(num_days)
num_days)
最大何日間、一時的なPHPファイルと、phptal:cacheのキャッシュを保持するか指定できます。
デフォルトでは30日です。キャッシュはPHPTALがファイルを再コンパイルする際(かつ平均して30回再コンパイルされるうちの1回)にスキャンされ、クリーンアップされます。
シェルを利用することで、PHPTALの動作を待たずにキャッシュをクリアすることができます。(7. システム管理者のためにを参照)
6.2.1.3.5. setForceReparse(boolean)
boolean)
常にテンプレートのパースを行わせます。テストとデバッグの際にのみ利用してください。
PHPTAL自体に手を加えた場合や、pre filterのテストの際に有用です。
注意
PHPTALの挙動が大変遅くなります。実行環境ではenableにしないようにしてください
6.2.2. execute()メソッド
テンプレートを実行し、マークアップを返します。
PHPTALは複数回実行された場合も全ての変数を保持しています。
このことを利用して、同一ページのバージョニング(変数のみの差分による)や、同一変数セット下での複数テンプレート生成(setTemplate()を利用します)を行う事ができます。
Note
"新鮮な"PHPTALコピーが必要な場合は新しいオブジェクトを生成してください
6.2.3. echoExecute()メソッド
execute()の最も一般的な使い方はそれを出力させることです。
PHPTALはバッファリングせずに即時に出力させるためのメソッドを提供しています。 大きなサイズの出力を行う際に、メモリーの制約に引っかからないようになります。
Note
tal:on-errorやphptal:cacheなどで分割されているテンプレートはバッファリングされます。
6.2.3.1. 制約
echoExecute()を使う際、ファイル中のXML宣言部やDOCTYPE指定で定義されたマクロを呼ぶと例外をthrowします。
一般的なPHPTALは他のファイルからDOCTYPEを"継承"することを許容します。ではありますが、バッファリングを回避することはできません。
そう言う場合は以下を試してみてください。
- 制約を引き上げて、
echo $tpl->execute()を使ってください echoExecute()が呼ばれる前に、全てのDOCTYPEやXML宣言を削除してください
6.2.4. addPreFilter()メソッド
Note
PHPTAL 1.2.1以降、setPreFilter()は非推奨となり、このメソッドに置き換えられました
テンプレートに適用させるための新たなプリフィルターを追加します。
プリフィルターソースコードやテンプレート中のパースされたDOMノードに変更を加えることができます。
プリフィルターはセットされたもの全てが適用されます。
独自のプリフィルターを作成する場合の説明はPHPTAL_PreFilterクラスを見てください
6.3. PHPTAL_PreFilterクラス
プリフィルターはテンプレートがコンパイルされる前に一度だけ実行されます。
プリフィルターはテンプレートのソースコードを処理しますが、それらの値や変数にはアクセスすることができません。
ではありますが、TALマークアップを見たり編集したりすることができます。
プリフィルターを作成する際は、PHPTA_PreFilterクラスをextendし、必要に応じてfilter*()メソッドを実装してください。
6.3.1. filter()メソッド
テンプレートのソースを文字列として受け取り、新しいソースを返す事を期待されます。
ソースコードを単純に検索/置換する際に利用できます。ただし、テンプレートの文法エラーは検出しないので注意してください。
注意
PHPTALのエラーメッセージはフィルター後の行番号を参照します。
6.3.2. filterDOM()メソッド
パースされたファイルのルートとなるPHPTAL_DOMを受けとり、それを編集します。
以下のサンプルは全てのコメントを削除するプリフィルターです。
function filterDOM(PHPTAL_Dom_Element $element)
{
foreach($element->childNodes as $node) {
if ($node instanceof PHPTAL_Dom_Comment) {
$node->parentNode->removeChild($node);
}
else if ($node instanceof PHPTAL_Dom_Element) {
$this->filterDOM($node); /* 全てのエレメントを再帰的にフィルタリングします */
}
}
}
6.3.3. getCacheId()メソッド
このフィルターと設定に対して、一意な識別文字列を返さなければなりません。これはテンプレートのキャッシングに利用されます。
異なる文字列が返されたときはいつでも、テンプレートが再コンパイルされます。
フィルターの出力が設定(プロパティ等)に依存する場合に、このメソッドを実装してください
Tip
フィルターの開発中(や、テスト中)は、PHPTALが強制的にテンプレートを更新するようにsetForceReparse(true)を設定してください。
でなければ、フィルターの出力はキャッシュされるため、変更点をチェックすることができなくなります。
6.3.4. getPHPTAL()メソッド
このフィルターで利用されているPHPTALのインスタンスを返します。
エンコーディングやその他の設定などを取得するために使えます。
6.4. PHPTAL_DOMクラス
PHPTALの内部では、ドキュメントをW3CのDOM(に近い形)として表現します。
一方で、PHPTALのAPIではいくつかの基本的なDOM操作メソッドが用意されています。
PHPTAL_Dom_Elementクラスは以下のプロパティとメソッドを提供します。
array chiuldNodes ;数値でインデックスされた配列に子エレメントが入っています。
注意
この配列を直接編集せず、
appendChild()などを利用してください。
Note
PHPTALはnextSibling,firstChildなどは実装していません
PHPTAL_Dom_Element parentNode ;現在のエレメントの親ノード(エレメント)です。DOMツリーを上へ向かう際に利用してください。
void appendChild($node);エレメントのノードを追加します。ノードはこのエレメントから削除したり、末尾に追加したりすることができます。
注意
PHPTALは、名前空間の定義を扱うことができません。異なる名前空間のエレメントでノードを移動させた場合、ドキュメントの意味が変わってしまいます。
void replaceChild($new_node, $old_node);ノードを入れ替えます。
void removeChild($node);ノードをその親から削除します。
- string getAttributeNS($namespace_uri, $local_name);
エンティティ以外の、エスケープされていない値を属性から取得します。
$a->getAttributeNS('','href')Tip
XML内では属性はエレメントの名前空間を継承しません。全てのXHTML属性はデフォルトの名前空間に属します。
array getAttributeNodes();すべてのエレメントの属性を表現する
PHPTAL_Dom_Attrオブジェクトの配列を返します。これを使えば、setAttributeNodes()を使わずに属性の値を変更することができます。void setAttributeNodes(array $attrs);全てのエレメントの属性を置き換えます。
string getLocalName();エレメントのローカルネームを返します。 例:
<atom:title>はローカルネームtitleを持ちます。string getNamespaceURI();エレメントの名前空間URIを返します。 例:
<atom:title xmlns="http://www.w3.org/2005/atom">は名前空間http://www.w3.org/2005/atomを持ちます。Tip
XHTML名前空間は
http://www.w3.org/1999/xhtmlです。
テキストCDATAや属性ノードは、テキストの実態を読み書きするためにgetValueEscaped()やsetValueEscaped()メソッドを持っています。
6.4. PHPTAL_Filterインターフェース
このインターフェースを使ってテンプレートの実行結果に対するフィルターを作成することができます。
ポストフィルターはsetPostFilter()メソッドを使ってセットします。
Tip
フィルターの動作が遅い場合、プリフィルターを代わりに使ってみてください。テンプレートのコンパイル後に一度だけ実行されます。
テンプレートの実行結果(全ての変数とTALマークアップの適用後)にあなたのフィルターのfilter()メソッドに渡されます。
<?php
require_once 'PHPTAL.php';
class MyPreFilter implements PHPTAL_Filter {
public function filter($source) {
return $source;
}
}
class MyPostFilter implements PHPTAL_Filter {
public function filter($xhtml){
return $xhtml;
}
}
$tpl = new PHPTAL('mytemplate.html');
$tpl->setPostFilter(new MyPostFilter());
echo $tpl->execute();
?>
set*Filterによって一つのpre-filterと一つのpost-filterをセットすることができます。 もし一つ以上のフィルターを連結して使いたい場合、PHPTAL_Filterに実装されているフィルタ連結インターフェースを使って一つのクラス内にラップすることができます。
<?php
require_once 'PHPTAL.php';
class FilterChain implements PHPTAL_Filter {
private $_filters = array();
public function add(PHPTAL_Filter $filter){
$this->_filters[] = $filter;
}
public function filter($source){
foreach ($this->_filters as $filter){
$source = $filter->filter($source);
}
return $source;
}
}
$myfilter = new FilterChain();
$myfilter->add(new CommentFilter()); // imaginary filter
$myfilter->add(new TidyFilter()); // imaginary filter
$tpl = new PHPTAL('mytemplate.html');
$tpl->setPostFilter($myFilter);
echo $tpl->execute();
?>
6.5.1. Multiple post filters
一つだけポストフィルターを使う場合はsetPostFilter()が使えますが、もし複数のフィルターチェインがある場合、フィルターチェインをinvokeするようにPHPTAL_Filterインターフェースを実装することで、それらを一つのクラスとしてラップすることができます。
<php
require_once 'PHPTAL.php'
class FilterChain implements PHPTAL_Filter {
private $_filters = array();
public function add(PHPTAL_Filter $filter) {
$this->_filters[] = $filter;
}
public function filter($source) {
foreach($this->_filters as $filter) {
$source = $filter->filter($source);
}
return $source;
}
}
$myfilter = new FilterChain();
$myfilter->add(new CommentFilter()); // 架空のフィルター
$myfilter->add(new TidyFilter()); // 架空のフィルター
$tpl = new PHPTAL('mytemplate.xhtml');
$tpl->setPostFilter($myFilter);
echo $tpl->execute();
?>
6.6. PHPTAL_Triggerインターフェース
phptal:idattributeはPHP5バージョンから、古いPHPTAL_Cacheインターフェースを置き換え、それらをもう少し要約するためにPHPTALに追加されました。
phptal:idに達した時点で、PHPTALはトリガーリストからidに一致するトリガーを探し、そのstart()とend()メソッドをエレメントの開始時、および終了時に実行します。
もし PHPTAL_Trigger::start() メソッドの戻り値が PHPTAL_Trigger::SKIPTAG である場合、PHPTALはこのエレメントとその内容を無視します。(start()それに変わる何かを出力するかもしれません。)
トリガーの実行にエレメントや内容を必要とするのであれば、 PHPTAL_Trigger::PROCEEDを返す必要があります。
PHPTAL_Trigger::end() は、エレメント終了時に呼ばれます。 これによって、start()内でob_start()を、end()内でob_get_contents()とob_end_clean()を使ったキャッシュシステムを作ることができます。
<html>
...
<div>
...
foo bar baz <span tal:replace="id"/> foo bar baz
...
</div>
...
</html>
いくつかの理由から、divブロックをキャッシュすることに決め、テンプレートにphptal:idを導入します。
<html>
...
<div phptal:id="somePossiblyUniqueKeyword">
...
foo bar baz <span tal:replace="id"/> foo bar baz
...
</div>
...
</html>
そして、divコンテントをキャッシュするトリガーを記述します。
<?php
require_once 'PHPTAL.php';
require_once PHPTAL_DIR.'PHPTAL/Trigger.php';
class CacheTrigger implements PHPTAL_Trigger
{
public function start($phptalid, $tpl)
{
// テンプレートの実行コンテキストに 'id' が現れる場合、
// このキャッシュは 'id' に依存します
$this->_cachePath = 'cache.' . $tpl->getContext()->id;
// もし既にキャッシュされている場合、キャッシュを読み込んだ上で
// PHPTALにはこのタグを無視するように通知します。
if (file_exists($this->_cachePath)){
$this->_usedCache = true;
readfile($this->_cachePath);
return self::SKIPTAG;
}
// キャッシュが見つからない場合は出力を開始し、
// PHPTALに処理を依頼します
$this->_usedCache = false;
ob_start();
return self::PROCEED;
}
// タグの実行後に呼ばれます
public function end($phptalid, $tpl)
{
// 終了タグ処理。 キャッシュヒットの場合は何もしません
// end of tag, if cached file used, do nothing
if ($this->_usedCache){
return;
}
// そうでなければ、出力バッファのコンテントを取得し、出力した後で
// キャッシュファイルに書き出します
$content = ob_get_contents();
ob_end_clean();
echo $content;
$f = fopen($this->_cachePath, 'w');
fwrite($f, $content);
fclose($f);
}
private $_cachePath;
private $_usedCache;
}
?>
注意すべきは、SKIPTAGかPROCEEDのどちらかがstart()の戻り値となっている点です。
SKIPTAGが返される場合、PHPTALはタグを無視しend()を呼びます。これは大抵の場合、トリガーはそこに何を表示すべきかを受け取ることを意味します。
PROCEEDが返される場合、PHPTALは通常通りタグと内容を実行し、end()を呼びます。 これにより一度タグを実行し、後で呼び出しに利用されるファイルに結果を保存する出力バッファを取り扱うことができます。
このトリガーをインストールするには以下の様にします。
<?php
require_once 'PHPTAL.php';
require_once 'CacheTrigger.php'; // カスタムトリガー
$trigger = new CacheTrigger();
$tpl = new PHPTAL('test.html');
// このトリガーはphptal:id="triggerId"の時だけ呼ばれます
$tpl->addTrigger('somePossiblyUniqueKeyword', $trigger);
$tpl->id = 1;
echo $tpl->execute();
?>
テンプレートに多くのトリガーを追加することもできます。 一般的なキャッシュトリガーは一つ以上のphptal:idやその他を利用するでしょう。
6.7. PHPTAL_TranslationServiceインターフェース
- 6.7.1. setLanguage(…) メソッド
- 6.7.2. useDomain($domain) メソッド
- 6.7.3. setVar($key,$value) メソッド
- 6.7.4. translate($key) メソッド
- 6.7.5. setEncoding($encoding) メソッド
PHPTALは他のセクションで示されているように、標準でgettextによる翻訳サービスを利用していますが、独自の翻訳サービスを実装することもできます。
PHPTAL_TranslationServiceインターフェースはそのような時に使います。
使い方はPHPTAL_GetTextTranslatorと同様となるでしょう。
$tpl->setTranslator($yourOwnTranslatorInstance);
以下のメソッドを実装する必要があります。
6.7.1. setLanguage(…) メソッド
このメソッドはテンプレートの出力言語を切り替える際に呼ばれます。
利用可能な言語の配列を引数にとります。引数配列を使うために、func_get_args()を使います。
あなたのサービスに置ける母国語を設定してください。
設定された言語を返します。
<?php
require_once PHPTAL_DIR.'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
...
public function setLanguage(){
$langs = func_get_args();
foreach ($langs as $lang){
// もし $lang が既知ならそれを利用し、ループを終了します
$this->_currentLang = $lang;
return $this->_currentLang;
}
return $this->_currentLang;
}
...
private $_currentLang;
}
?>
6.7.2. useDomain($domain) メソッド
もし、翻訳ファイルを分割して保存する場合(例えば アプリケーション毎に一つ)、このメソッドによってテンプレートの翻訳ドメインを選択することができます。(i18n:domain)
<?php
require_once PHPTAL_DIR.'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
...
public function useDomain($domain){
if (!array_key_exists($domain, $this->_domains)){
$file = "domains/$this->_currentLang/$domain.php";
$this->_domains[$domain] = include($file);
}
$this->_currentDomain = $this->_domains[$domain];
}
...
private $_currentDomain;
private $_domains = array();
}
?>
上記の例では、キーと翻訳値の連想配列を返すPHPファイルを元にした翻訳を可能にしています。
6.7.3. setVar($key,$value) メソッド
このメソッドはi18n:name呼び出しに相当しています。 後の呼び出しのためにcontextに挿入します。
<?php
require_once PHPTAL_DIR.'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
...
public function setVar($key, $value){
$this->_context[$key] = $value;
}
...
private $_context = array();
}
?>
6.7.4. translate($key) メソッド
最後の重要なメソッドは、あなたの翻訳サービスに現在の選択された言語においてキーに定義された翻訳を問い合せます。
<?php
require_once PHPTAL_DIR.'PHPTAL/TranslationService.php';
class MyTranslator implements PHPTAL_TranslationService {
...
public function translate($key){
$value = $this->_currentDomain[$key];
// コンテキストの連想配列を使って ${myvar}を補完します。
while (preg_match('/${(.*?)}/sm', $value, $m)){
list($src,$var) = $m;
if (!array_key_exists($var, $this->_context)){
$err = sprintf('Interpolation error, var "%s" not set',
$var);
throw new Exception($err);
}
$value = str_replace($src, $this->_context[$var], $value);
}
return $value;
}
...
}
?>
6.7.5. setEncoding($encoding) メソッド
PHPTALクラスはあなたの翻訳サービスに、テンプレートで何のエンコーディングが使用されているかを知らせるためにこのメソッドを呼びます。
translate()メソッドはそのエンコーディングの文字列を返さなければなりません。
もしあなたはいつも、テンプレートと翻訳ファイルに同じエンコーディング(たとえばUTF-8)を使うのであれば、このメソッドは空っぽにしてしまっても良いでしょう。
6.8. gettextによる処理
- 6.8.1. Creating the translation directory structure
- 6.8.2. Portable Object files
- 6.8.3. Translation Domain
- 6.8.4. PHP内での翻訳
- 6.8.5. 変数による補間
gettext はPHPから使える標準的なGNUの国際化/翻訳システムであり、PHPTALでもサポートされています。
gettext™の使い方は単純ですが、あなたのシステムで使えるかどうか確かめるために、いくつかのテストを実行してください。
最初に、PHPが--with-gettextでコンパイルされている必要があります。 コンパイル方法はPHPのドキュメントを見てください。
以下のコードでテストすることができます。
//
// gettext extensionがPHPにインストールされているかテスト
//
if (!function_exists("gettext"))
{
echo "gettext はインストールされていません\n";
}
else
{
echo "gettext がインストールされています\n";
}
6.8.1. Creating the translation directory structure
PHPのgettext™拡張は、翻訳ファイルを含む特殊な構造を必要とします。
/path/to/your/translation_root/en_US/LC_MESSAGES/ /path/to/your/translation_root/en_GB/LC_MESSAGES/ /path/to/your/translation_root/fr_FR/LC_MESSAGES/ /path/to/your/translation_root/es_ES/LC_MESSAGES/ ... and so on ...
言語コードは、その言語自身(en, fr, es, ...)を表す2文字の文字で表されます。
さらに二つの文字は国(US, GB, FR, ES, …)を定義します。 ディレクトリパターンは以下です。
<path_to_where_you_want>/<ll_CC>/LC_MESSAGES/
6.8.2. Portable Object files
POファイルは翻訳を含んだプレインテキストです。 人手で編集することができます。
もっとも小さなPOファイルの例です。 (en_US/LC_MESSAGES/mydomain.po)
msgid "" msgstr "" "Content-Type: text/plain; charset=utf-8n" "Content-Transfer-Encoding: 8bit\n" msgid "Simple test" msgstr "A small sentence in english"
一度編集したらインデックス化しなければなりません。
msgfmt mydomain.po -o mydomain.mo
あなたのシステムにgettext™ツールがインストールされていなければ、このコマンドは動作しません。
これにより、翻訳に素早くアクセスするためにインデックス化されたMO(machine object)ファイルが作成されます。
そして、あなたは他の言語でこのファイルを翻訳しなければなりません。 最小の例は以下です (fr_FR/LC_MESSAGES/mydomain.po):
msgid "" msgstr "" "Content-Type: text/plain; charset=utf-8n" "Content-Transfer-Encoding: 8bit\n" msgid "Simple test" msgstr "Une petite phrase en francais"
そしてまた、インデックス化しなければなりません。
msgfmt mydomain.po -o mydomain.mo
6.8.3. Translation Domain
ドメインは翻訳ファイルを逆からマッチングします。
先ほどまでの例では'mydomain'をドメイン名として利用してきました。
アプリケーションで複数のドメインを持つことができ、一つ以上のファイルに翻訳を分割することでgettext™のパフォーマンスを上げることができます。
6.8.4. PHP内での翻訳
<?php
require_once 'PHPTAL.php';
require_once PHPTAL_DIR.'PHPTAL/GetTextTranslator.php';
try {
$tr = new PHPTAL_GetTextTranslator();
// このセッション内で利用する言語のセット
// 最初に有効と判定された言語が利用されます
$tr->setLanguage('en_GB.utf8', 'en_GB');
// 利用するgettextドメインを登録
$tr->addDomain('mydomain', '/path/to/your/translation_root');
// 現在のドメインを指定
$tr->useDomain('mydomain');
$tpl = new PHPTAL('mytemplate.xhtml');
// PHPTALに、利用するtranslatorをセット
$tpl->setTranslator($tr);
// 翻訳されたテンプレートを出力
echo $tpl->execute();
}
catch (Exception $e){
echo $e;
}
もしテンプレートではない(プレインテキストのe-mailの様な)文字列を翻訳したい場合、PHPTALのtranslatorを再利用することができます。
$tr = $tpl->getTranslator();
$subject = $tr->translate("Registration infomation");
$tr-%gt;setVar("user", $username);
$message = $tr->translate("Dear ${user}, thanks for registering!");
mail($email, $subject, $message);
もしPHPTALの標準gettext™translatorを使っているなら、gettext()もまた利用可能です。
6.8.5. 変数による補間
I18N namespaceは、翻訳に変数を補間することができます。
# 日本語
msgid "welcome"
msgstr "ようこそ ${name} さん。 あなたには ${n} 通のメールが届いています!"
# french
msgid "welcome"
msgstr "Bienvenue ${name} vous avez recu ${n} messages !"
テンプレートはこの補間を以下の様に使うことができます。
<span i18n:translate="welcome"> ようこそ <span i18n:name="name" tal:replace="user/name"/> さん。 あなたには <span i18n:name="n" tal:replace="user/unreadeMails"/> 通の未読メールがあります ! </span>
i18n:translateは値'welcome'を含むので、このテンプレートデータは無視されて、gettext™によるメッセージが替わりに利用されます。
8. Creating custom expression modifiers
PHPTALは基本の式モディファイアを提供しています(not:, exists:, string:, php:, path:)。
これらのモディファイアはZPTによって定義されている物ですが、PHPTALは文字列や、日付、会計、オブジェクト、などの操作のためにモディファイアを拡張することができます。
モディファイアの目的は、templateのPHPソースに含まれた何らかのPHPコードを返すことです。
モディファイアはパース時に利用されます。もしモディファイアの挙動を変えたい場合、生成されたPHPファイルを削除し、そのモディファイアが使われているテンプレートを再度パースしなければなりません。
覚えておくべきは、モディファイアは、コードを処理するがデータを出力しないということです。
“phptal_tales_“で始まるPHP関数をモディファイアとして利用することができます。 モディファイアは二つの引数を持ちます。
- $src: “modifier:”句の後に続く文字列です。
- $nothrow:
phptal_path()の解決において、例外が投げられるか否かを表すbooleanです。モディファイア中で別のモディファイア(phptal_tales_*)を呼ぶ場合は必ず伝播させなければなりません。
以下のTALテンプレート中の例を見てください。
<span tal:replace="some-modifier: my/path/value"/>
src引数は”my/path/value“となり、$nothrowはfalseとなります。 なぜなら、tal:replaceは常に解決可能なパスを要求するからです。 以下の様な式は、
<span tal:replace="some-modifier: my/path/value | other/path"/>
二つのモディファイアが使われています。
- some-modifier: “my/path/value” が $src に、$nothrow はtrueになります。 なぜなら代替式が存在するからです。
- path: “other/path” が $src に、$nothrow は代替式が無いのでfalseになります。
path: は、他のモディファイアが指定されていない場合の暗黙のモディファイアであることを忘れないでください。
モディファイアは、簡単なPHPコードを生成するために他のモディファイアを使うことができます。
//
// このモディファイアは会計フォーマットの文字列を返します (XXX.XX)
//
// usage:
//
// money: path/to/my/amount
//
// このモディファイアは、モディファイアの引数を返すPHPのコードを生成するために、
// phptal_tales()関数を利用します。
//
// 例えば
//
// money: path/to/my/amount
//
// これは以下の様に処理されます。
//
// sprintf("%01.2f", phptal_path($ctx->path, "to/my/amount"))
//
//
// This code will be included right into the template where needed.
//
// @param string $src
// 書式文字列
// @param string $nothrow
// パスが存在しなかった時にphptal_pathが例外を送出するかを示す真偽値
// @return string
// このテンプレートに含めるPHPコード
//
function phptal_tales_money( $src, $nothrow )
{
// remove spaces we do not require here
$src = trim($src);
return 'sprintf("%01.2f", '.phptal_tales($src, $nothrow).')';
}
7. システム管理者のために
PHPTALの機能は、テンプレートロジックによってPHPファイルを生成することです。つまり、それらの生成されたファイルを置くためのディレクトリが必要であり、PHPインタプリタによってパース可能でなければならないと言うことです。
標準状態のPHPTALでは、システムのテンポラリディレクトリ(有効であれば、PHPのsys_get_temp_dir()の場所)か、Unix系システムであれば/tmp、マイクロソフト系であればc:\windows\tempにコンパイルされたテンプレートを置こうとします。
この標準の出力先はsetPhpCodeDestinationに適切なパスを指定することで変更できます。システムのテンポラリディレクトリ、もしくは指定されたそのパスは、PHPのプロセス(もし mod_phpならapacheの実行ユーザー、そうでないならcgiやfastcgiのユーザー)がファイルをcreateおよびupdateできるように設定されている必要があります。
PHPTALはテンプレート毎、さらにphptal:cacheが使われたタグ毎にファイルを生成します。なお、マクロに対しては生成しません(単純に、PHP関数としてコンパイルされるだけです)。
これらのファイルは時々自動的にクリーンアップされます。 明確には、コンパイルされたテンプレートファイルは、setCachePurgeFrequency()メソッドによってコントロールされる確率に従い、setCacheLifetime()によって設定されているよりも古いものが削除されます。
替わりに、古いファイルを消すようなスクリプトをスケジュールしておくこともできます。
find /tmp/ -name tpl_* ( -atime +1 -o -mtime +14 ) -exec rm -v {} ;
8. Useful links
- ZPT Zope Page Template front page,
- TAL the Template Attribute Language page,
- METALis the Macro Expansion of TAL,
- TALES the TAL Expression Syntax.
9. 謝辞
Big thanks goes to:
- ZPT team, who made these useful specifications,
- The PHPTAL community for their support, help and reports,
- Jean-Michel Hiver, who 'forced' me to look at them,
- Olivier Parisy, the first enthusiastic PHPTAL user and bug finder,

