素のPHPはもはやテンプレートエンジンとしては使えない

なんか未だに「PHP自体がテンプレートエンジンなのになんでSmartyとか使うの?」みたいに考えている人がいるようなのでちょっと。
といっても、言いたいことはSymfony開発者でありTwigの現在の開発者*1でもあるFabien氏がこれでもかってぐらいに語っているので、それを読んでもらったほうが早い。というか読むべき!


Templating Engines in PHP - Fabien Potencier
http://fabien.potencier.org/article/34/templating-engines-in-php


ちなみに、PHPを使ってる事自体が云々とかいう茶々はいりません。



まぁようするに、

<div><?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8'); ?></div>

おまえ本当にこんなんでいいと思ってんの?と。
さすがにこれはベタすぎるので百歩譲ってこうだったとしても、

<div><?= h($var); ?></div>

それでも自分はこっちのほうがいい(↓はSmarty)。

<div>{$var|escape}</div>

この例だと文字数はほぼ変わらないけど見やすさは全然違う。

もう少し具体的な例

別にフレームワークを対比させたいわけじゃないので何のフレームワークとは言わないけど、とりあえず素のPHPのViewのやつとTwigのやつを。
内容は適当にでっちあげた。

<div id="products">
  <?php if (!empty($products)): ?>
    <?php foreach ($products as $id => $product): ?>
      <div class="product">
        <div><?php echo $this->Html->link(Inflector::humanize($product['title']), array('action' => 'products', $id)); ?></div>
        <div><?php echo h(date('Y/m/d', $product['published_at'])); ?></div>
        <div><?php echo h(number_format($product['price'])); ?></div>
        <div><?php echo h(!empty($product['description']) ? $product['description'] : '-'); ?></div>
      </div>
    <?php endforeach; ?>
  <?php else: ?>
    <p>no products found</p>
  <?php endif; ?>
</div>
<div id="products">
  {% for id, product in products %}
    <div class="product">
      <div><a href="{{ path('products', { 'id': id }) }}">{{ product.title|title }}</a></div>
      <div>{{ product.published_at|date('Y/m/d') }}</div>
      <div>{{ product.price|number_format }}</div>
      <div>{{ product.description|default('-') }}</div>
    </div>
  {% else %}
    <p>no products found</p>
  {% endfor %}
</div>

※Twigの場合(大抵は)出力がデフォルトでエスケープされる設定にされているので{{ $var|escape }}みたいな書き方はしなくてよい。


ヘルパーとか使いまくりの例だともっと顕著な差が出てくると思うんだけど、それってフレームワークの問題でもあるので、やや無難な比較になってしまったか。
あと面倒くさかったので省いてしまったけど、プルダウンメニューとか状態によって変わってくるものもテンプレートエンジンだとスマートに書くための方法が用意されているよね。


素のPHPでも同じように関数なりヘルパーを書けば似たようなことはできるけど、結局素の構文に引きずられるので、

$this->helper->escape($this->helper->currency($price))

みたいな長ったらしい書き方になって、もうそれだけで気が滅入るほどに可読性を下げてるんだよ。打つのもめんどくさいし。

$price|currency|escape

って書けたほうが楽だしわかりやすいと思いませんか?(自動エスケープがあれば最後のescapeすら不要だし)


それにフィルタ的なものを関数で実現する*2となると、グローバル空間にそういう関数をわんさか作っていくってことになるよね。グローバル空間にcurrency()とか定義しちゃうの?*3
それは正直耐えられない。耐えられないっていうか絶対そのうち何か起こるよね。

素のPHP派の主張とか

  • テンプレートエンジンを通す分遅くなる
  • 新しい文法を覚えないといけない
  • どうせ分業できずにプログラマが移植するんだからPHPでいいじゃないか
  • いざというときにPHPの処理が書けなくなるじゃないか


もう今となっては当時どういう議論が多かったのかよく覚えてないけど、だいたい反対派の主張は「余計なものを増やすな」って感じが多かったと思う。
でも自分にしてみれば、表現が長ったらしくて可読性の低いViewを書くことのほうが遥かに苦痛だったし、速いとか遅いとかそういうところじゃなくて、ただでさえカオスになりがちなHTMLテンプレートをいかに簡潔に、安全に、保守しやすく書けるかってこのとほうが重要だった。




確かに当時はテンプレートエンジン的な立ち位置だったのかもしれないけど、もう今はこんなんじゃ通用しねーだろ。

*1:オリジナルはArmin Ronacher氏かな

*2:個人的にはパイプのほうが読みやすいかなと思う。これは好みの問題?

*3:PHP 5.3ならクロージャをテンプレート変数にアサインして使えば多少はマシか?