読者です 読者をやめる 読者になる 読者になる

sekaie engineers' blog

セカイエ株式会社が主催するエンジニア勉強会について

完全版! テンプレートエンジン チートシート (blade, twig, volt, smarty, mustache, の比較もあるよ)

まいど。セカイエでエンジニアやっている @M_Ishikawa こと石川と申します。好きな言葉はちくわ大明神です。

この記事は セカイエ Advent Calendar 2015 8日目の記事です。


Advent Calendarに参加するにあたってネタが思い当たらなかったのですが、最近ちょっと変わった経験をしたのを思い出しました。
それというのも、この半年間で、

と沢山のPHPフレームワークを同時並行して開発していたのです。


もうあたま炭酸です。スパークしちゃいます。
PHPフレームワークは、とはいえ、PHPで書けばどれも動きます。

問題はテンプレートエンジンです。

記法がそれぞれ微妙に異なるのです。
プロダクトで使用したテンプレートエンジンも様々で、

  • blade
  • twig
  • volt
  • smarty
  • mastache

全部別かよ!

...というわけで、テンプレートエンジンチートシートを作ってみました。
得になるのはぼくくらいしかいないと思いますがそこは大丈夫です。気にしていません。

なお、bladeはlaravel5、smartyはsmarty3を使用しています。


各種PHPテンプレートエンジンの概要

チートシートの前に、各テンプレートエンジンの概要について少し纏めてみました。これから選定する方は参考になればと。

  blade twig volt smarty mustache
サイト blade f:id:sekaie:20151208204730p:plain:w200 twig f:id:sekaie:20151208204754p:plain:w200 volt f:id:sekaie:20151208204838p:plain:w200 smarty f:id:sekaie:20151208204900p:plain:w200 mustache f:id:sekaie:20151208204918p:plain:w200
依存フレームワーク laravel symfony。他でも使用可能 phalcon。他でも使用可能 依存なし(php全般) 依存なし(phpに限らず利用できる)
開発言語 PHP PHP C PHP JavaScript
個人的な印象 一番新しいので一番イケていると感じる。phpとして書けるのは非常に便利で良くも悪くも自由度が高い。Laravelとの記法に差がないのでLaravelだけやっていれば混乱しない。独特なものがなく「そうだ、phpってそもそもテンプレートエンジンみたいなものだったもんな」というのを思い出させてくれる。これはいい意味で。 イマドキのテンプレートエンジンの先駆け。といいたいが、実際に先駆けたのはpythonのテンプレートエンジンjinjaで、それに影響を受けている。smartyの不満を解決してくれた。 twigぽいphalcon用の、つまり、Cエクステンションのテンプレートエンジン。大差無いように感じる上、劣っているようにも感じるのでphalcon使わないでvoltを使う理由はないと思う。 老舗中の老舗。歴史がある分、枯れているしドキュメントも多数。しかしいまとなってはイケてない仕様が残っている。さらに長年で変化してきた仕様において古いものもWebに混在していて注意が必要。 非常に低機能だけど、沢山の言語に対応していて恩恵を受けるのはphpバックエンドを介さずに、Javascriptフロントエンドだけでテンプレートをブラウザ表示確認できるのは魅力。とはいえいまとなってはスタンドアロンサーバを気軽に立てたりできるのでなんともいえない


PHPテンプレートエンジン・チートシート

本題のチートシートです。「できない」と書いてある者はデフォルトで用意されていないだけで、フィルタ、ヘルパー、エクステンションと言われるものを軽く実装するだけでだいたい実現できます。(mustacheを除く)

  blade twig volt smarty mustache
ファイルの拡張子 .blade.php .twig .volt .tpl .mustache
変数の出力
{{ $val }}
{{ val }}
{{ val }}
{ $val }
{{ val }}
配列のアクセス
{{ $arr['name'] }}
{{ arr.name }}
{{ arr.name }}
 
or
{{ arr['name'] }}
{ $arr.name }
 
or
 
{ $arr['name'] }
{ $arr.name }
オブジェクトのアクセス
{{ $arr->name }}
{{ arr.name }}
配列と同じ
{{ arr.name }}
 
or
{{ arr['name'] }}
配列と同じ
{ $arr->name }
{ $arr.name }
配列と同じ
if文
@if($flag && $status == 'active')
こっちが正
@else
こっちが偽
@endif
{% if flag and status == 'active' %}
こっちが正
{% else %}
こっちが偽
{% endif %}
{% if flag and status == 'active' %}
こっちが正
{% else %}
こっちが偽
{% endif %}
{if $flag && $status == 'active'}
こっちが正
{else}
こっちが偽
{/if}
{{#flag}}
こっちが正
{{/flag}}
{{^flag}}
こっちが偽
{{/flag}}
and, orは存在しないのでネストするか並列で並べるかで記述する。
equalはないのでテンプレートに渡す時点でそのように整える必要がある。(そういう思想)
各種ループ
@for($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor

@foreach($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach

@forelse($users as $id => $user)
<li>{{ $id }}: {{ $user->name }}</li>
@empty
<p>No users</p>
@endforelse

@while(true)
<p>I'm looping forever.</p>
@endwhile
一通り使える
{% for users in user %}
<p>This is user {{ $user->id }}</p>
{% endfor %}
 
{% for id,user in users %}
<li>{{ id }}: {{ user.name }}</li>
{% else %}
<p>No users</p>
{% endfor %}
php版for、whileが使えない
{% for users in user %}
<p>This is user {{ $user->id }}</p>
{% endfor %}
 
{% for id,user in users %}
<li>{{ id }}: {{ user.name }}</li>
{% else %}
<p>No users</p>
{% endfor %}
php版for、whileが使えない
{for $i=0 to 9 step 1}
The current value is {{ $i }}
{/for}
 
{foreach $users as $user}
<p>This is user {{ $user->id }}</p>
{/foreach}
 
{foreach $users as $id => $user}
<li>{{ $id }}: {{ $user->name }}</li>
{foreachelse}
<p>No users</p>
{/foreach}
 
{while}
<p>I'm looping forever.</p>
{/while}
一通り使える
{{{#users}}
<p>This is user {{ id }}</p>
{{/users}}
{{^users}}
<p>No users</p>
{{/users}}
keyにアクセス出来ない等随分制限があるので変数を予め整える必要がある。
四則演算
{{ 1 + 2 }} 
{{ 1 + 2 }} 
{{ 1 + 2 }} 
{ 1 + 2 } 
できない
論理演算
{{ $a && $b || $c }} 
{{ a and b or c }} 
{{ a and b or c }} 
{{ a and b or c }} 
できない
否定
@if(! $flag)
  hoge
@endif
@if($a != $b)
  fuga
@endif
{% if not flag %}
  hoge
{% endif %}
{% if a not b %}
  fuga
{% endif %}
{% if not flag %}
  hoge
{% endif %}
{% if a not b %}
  fuga
{% endif %}
{if ! $flag}
  hoge
{/if}
{if $a != $b}
  fuga
{/if}
{{^flag}}
  hoge
{{/flag}}
否定の比較はできない
比較演算
{{ $greater > $less }}

{{ $a == $b }}
{{ greater > less }}

{{ a == b }}
{{ a is b }}
※同じ
{{ greater > less }}

{{ a == b }}
{{ a is b }}
※同じ
{$greater > $less}

{$a == $b}}
できない
コメントアウト
{{--
コメント
--}}
{#
コメント
#}
{#
コメント
#}
{*
コメント
*}
{{!
コメント
}}
count
{{ count($arr) }}
{{ arr|length }}
{{ arr|length }}
{arr|@count}
{{ arr.length }}
dump
{{ dump($data) }}
{{ dump($data) }}
{{ dump($data) }}
{ $data|@var_dump }
できない
truncate
{{ str_limit($long_text, 20) }}
{{ $long_text|truncate(20) }}
できない
{$long_text|truncate:10}
できない
number_format
{{ number_format($money) }}
{{ money|number_format }}
できない
{$money|number_format}
できない
json_encode
var data = <?php echo(json_encode($data)) ?>;
var data = {{ data|json_encode() }};
var data = {{ data|json_encode }};
できない できない
エスケープ
{{ $data }}
もともとがエスケープされる。逆にしたくない場合は、
{!! $data !!}
{{ data|e('html') }}
{{ data|e }}
{$data|escape:'html'}
{{ data }}
もともとがエスケープされる。逆にしたくない場合は、
{{{ $data }}}
三項演算子
{{ $flag ? 'yes' : 'no' }}
{{ flag ? 'yes' : 'no' }}
{{ flag ? 'yes' : 'no' }}
できない できない
変数へのアサイン
<?php $val = ['Tokyo', 'Osaka']; ?>
{% set val = ['Tokyo', 'Osaka'] %}
{% set val = ['Tokyo', 'Osaka'] %}
{assign var='val' value='999'}
配列のアサインはできない
できない
複数行のアサイン
@section('content')
  <h1>これは</h1>
  <p>テストです</p>
@stop
{% set content %}
  <h1>これは</h1>
  <p>テストです</p>
{% endset %}
できない
{capture name='content'}
  <h1>これは</h1>
  <p>テストです</p>
{/capture}
できない
日時の表示
{{ date('Y-m-d H:i:s') }}
{{ 'now'|date('Y-m-d H:i:s') }}
{{ date('Y-m-d H:i:s') }}
{$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'}
できない
html圧縮 できない
{% spaceless %}{% endspaceless %}
できない
{strip}
 
{/strip}
できない
制御文字のエスケープ
例 {{ test }}
@{{ test }}
{{ '{{' }} test }}
{{ '{{' }} test }}
{literal}
{{ test }}
{/literal}
{{=<% %>=}}
{{ test }}
<%={{ }}=%>
親子テンプレートのサンプル
<html>
  <head>
    <title>
      ほむぺ - @yield('title')
    </title>
  </head>
  <body>
    <div class="container">
      @yield('content')
    </div>
  </body>
</html>
@extends('layouts.master')
@section('title', 'トップページ')
@section('content')
  <p>ようこそ!</p>
@endsection
<html>
  <head>
    <title>
      ほむぺ - {% block title %}{% endblock %}
    </title>
  </head>
  <body>
    <div class="container">
      {% block content %}{% endblock %}
    </div>
  </body>
</html>
{% extends 'layouts.master' %}
{% block title %}
  トップページ
{% endblock %}
{% block content %}
  <p>ようこそ!</p>
{% endblock %}
また、includeも利用可能です
{% include 'inc/header.twig' %}
{# templates/base.volt #}
<html>
  <head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    {% endblock %}
    <title>
      {% block title %}{% endblock %} - My Webpage
    </title>
  </head>
  <body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
      {% block footer %}footer.{% endblock %}
    </div>
  </body>
</html>
{% extends 'templates/base.volt' %}

{% block title %}Index{% endblock %}

{% block head %}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}

{% block content %}
<h1>Index</h1>
<p class="important">Welcome on my awesome homepage.</p>
{% endblock %}
{include file='inc/header.tpl'}
昔ながらのincludeで他のテンプレートを呼びます。
{{> include/header }}
昔ながらのincludeで他のテンプレートを呼びます。
タグヘルパー
(ビューヘルパー)
あり あり あり なし(Pluginで可能) なし
プラグイン なし(phpが記述可能) あり あり あり なし


いかがでしたでしょうか。ここでは性能や詳細な機能については触れていませんが是非いろいろ調べてみてください。結構違っていて面白いなあと思いました。(間違いあったらご指摘下さいmm)

またいろいろ使ってみて発見があったらここに追記していきたいと思います。

ちなみに僕は Laravel - Blade 推しです! Laravel大好き!書いててワクワクします!


ほな!