素のPHPをテンプレートエンジンとして使うときのコーディング規約

Posted by Hiraku on 2011-10-01

プログラムとしてPHPを書くときのコーディング規約は、PEARZendなど代表的なものがたくさんありますが、テンプレートエンジンとしてPHPを使う場合にはそのまま適用しにくいものです。

テンプレートエンジンのコーディング規約って、検索してもあまり見つからなかったので、個人的に採用しているものを晒してみます。あんまり語る人を見たことがないので、「俺はこうしてるよ」とか「ここキモくね?」とかご意見いただけるとうれしいです。

目指すところ

  • 複雑なロジックをテンプレートに書かない / 書けないように規約で縛る
  • 少しでも読みやすさを追求する
  • できあがりのHTMLの美しさも追及する
<%= $this->doctype() %> 
<html>
<head>
 <%= $this->headMeta() %> 
 <%= $this->headLink() %> 
 <%= $this->headTitle() %> 
</head>
<body>
 <div id="container">
  <div id="header">
   <h1><%=$this->headTitle()%></h1>
  </div>

  <div id="content">
   <dl>
<% foreach ($this->data as $d): %>
    <dt><%=$d['title']%></dt>
    <dd><%=$d['description']%></dd>
<% endforeach %>
    </dl>
  </div>
 </div>
 <%= $this->inlineScript() %>
</body>

</html>

規約1) asp_tags=on & short_open_tag=off

あまり同意してくれる人に会ったことがないのですが、asp_tagsは個人的に超好きなPHPのオプションです。onにすると<% ... %><%= ... %>という短いタグが使えるようになります。

short_open_tagの形式<? ... ?>もいいのですが、やはりXML宣言<?xml ... ?>と重なるのが気になります。かと言っていちいち<?php ... ?>とか<?php echo ... ?>なんて書いていたら、タイプ数の多さに発狂しそうになります。

asp_tags形式のタグを常に使い、echoやprintは禁止します。=で出力するよう統一します。

規約2) 代替構文を常に使う。{}禁止

ここでいう代替構文とはforeach (): ... endforeachとかif (): ... else: ... endifなどの構文のことです。制御構造に関する別の構文を参照のこと。

なぜ{}を禁止してわざわざタイプ数を増やすのかというと、目立たせるためです。HTMLはただでさえ記号が大量に登場するテキストです。そのなかに放り込まれた<% } %>なんて、擬態でもしてんのかってぐらいに目立ちません。endforeachやendifなどを使った方が少しマシになります。

2011年10月02日追記
{}だけを使うより、式の対応がわかりやすくなる効果もあります。コメント欄も参照してください。kwatchさん、ご指摘ありがとうございます。

ちなみにPHPで{}を禁止するということは、functionやclass、interfaceなどの使用も禁止するということになります。まあ普通テンプレートでこれらは不要でしょう。ヘルパー関数が必要であれば別のファイルに定義して適宜includeするようにします。

<ul>
<% foreach ($data as $d): %>

<%   if ($d['anonymous']): %>
 <li>匿名希望さん</li>
<%   else: %>
 <li><%=h($d['name'])%>さん</li>
<%   endif %>

<% endforeach %>
</ul>

規約3) セミコロン禁止(forを除く)

PHPは閉じタグの直前のセミコロンを省略できる仕様です。セミコロンを禁止するというのは、タグの中に複数の文を書くなということです。タグ中の文が一つだけなら、一切セミコロンを書かずにテンプレートを書いていくことが可能です。

複文を許すと、複雑なロジックをうっかり書いてしまうことにつながるので、必ずタグ一つにつき文は一つとします。

規約4) h()でエスケープする

生PHPで書く以上、HTMLエスケープは手動でやるしかありません。htmlspecialcharsは名前が長いので、h関数を定義してそちらを使いましょう。

function h($str, $encoding='UTF-8') {
  return htmlspecialchars($str, ENT_QUOTES, $encoding);
}

htmlspecialcharsの第二引数はよく議論になりますが、せっかくラップ関数を用意するならENT_QUOTES指定がベターかなと思います。シングルクオートでHTMLを書いてしまうドジっ子にソースを引き継ぐ可能性が少しでもあるなら、エスケープしておいた方が安心です。そのドジっ子は5日後のあなたかも知れません。

規約5) 論理タグはインデントしない

HTMLのインデントと、テンプレートのロジックに関するインデントはしばしば合致しません。なのでいっそのこと「論理タグのインデントを禁止し、PHPのコードは常に行頭から始まることを保証した方が見やすいのではないか?」と考え、インデントなしで書いています。見づらい場合はタグの中でスペースを使ってインデントします。大事なのはタグ自体をインデントするなというところです。

出力後のHTMLのインデントが崩れないのもメリットといえばメリットですかね。

<!-- NG -->
<ul>
 <% foreach ($data as $d): %>
  <li>
   <ul>
    <% foreach ($d['children'] as $c): %>
     <li><%=h($c)%></li>
    <% endforeach %>
   </ul>
  </li>
 <% endforeach %>
</ul>

<!-- OK -->
<ul>
<% foreach ($data as $d): %>
 <li>
  <ul>
<%   foreach ($d['children'] as $c): %>
   <li><%=h($c)%></li>
<%   endforeach %>
  </ul>
 </li>
<% endforeach %>
</ul>

…ここ、すごく個人的に悩んでるポイントです。テンプレートのインデントをきれいに書く方法があったら教えてほしいです。


あとは破ってしまうことが多いけど、こうした方がいいんでないかという要素です。

推奨1) HTMLを出力するヘルパー関数はなるべく使わない

DRYの観点から、よく使うHTMLを吐き出す関数を作りたくなると思いますが、やりすぎは禁物です。複雑なHTMLを関数によせてしまうと、HTMLの情報があちこちに分散してしまいます。ソースの構成を知っている人ならいいのですが、他の人が「ここのデザインを少しいじりたいなー」というときにあちこちの関数定義を読んでいかなければならない事態は、あまり好ましくありません。ビュースクリプトだけで見た目に関係する情報はなるべく完結しておいた方が見やすいと思います。

個人的にフォーム自体renderしちゃうフォームライブラリがあんまり好きになれないのは、このルールと反するのが理由です。。。

ヘルパーはモデルのデータ構造を加工して、テンプレートを書きやすくするだけに留めておくのがベストだと思います。

推奨2) 勝手にechoする関数を作らない

副作用を避ける、というやつですね。ヘルパーは文字列を返すようにして、受け取った側で出力した方が、追加で文字列置換などもしやすいので拡張性が高くなります。

推奨3) 改行削除を抑制する

PHPは閉じタグ直後の改行文字を削除します。このせいでHTMLのインデントが崩れることがあります。この挙動を抑制したい場合、閉じタグ直後に半角スペースを一つ入れて直後が改行でないようにします。

タグの中で改行を改めて出力する手もありますが、"\n"とかPHP_EOLとかをタイプするより、半角スペース一文字をタイプする方が圧倒的に楽なので、この方法を使っています。

<% foreach ($data as $d): %>
<%= h($d) %> 
<% endforeach %>

まとめ

全然自信ないので、他にいいコーディング規約があったら教えてください。

あ、そうだ。ちなみにですが、Vimの場合はこんな設定を追記しておくとasp_tagsでもうまい具合に色分けしてくれます。

let g:php_noShortTags=1
let g:php_asp_tags=1

そろそろPHP_CodeSnifferあたりで規約チェックできるようにするべきですかねー。。



keyword: テンプレートエンジン テンプレート PHP

PHPの最新記事

×

この広告は180日以上新しい記事の投稿がないブログに表示されております。