2011-09-11 20:27:09
スライドはこちらです.
また, 当日 Ustream で配信された動画はこちらにアーカイブされています.
動画中の一番最初がボクの発表です.
Lightning Talk
この LT では DSL のサンプルとして Paml というテンプレートエンジンを紹介しています.
また, オチとして, [] で DSL だけじゃなく汎用言語も実装できる, ということで PHP で実装された Lisp ライクな言語 LisPHP を紹介しています.
いずれも GitHub にリポジトリを公開しているので, 興味のある方はご覧ください.
結構ネタ的な要素も強いのですが, この発表でボクが割と真面目に主張したかったのは以下の点です.
- PHP 5.4 では [] が配列リテラルとして導入された
- PHP の array は他の言語と比べて特殊なデータ構造である
- その特性を DSL 的に活用することができる
スライドのまとめと若干表現が違っていますが, 大筋は変わりありません.
LT の 5 分間ではこれらについて説明しきれなかった部分があるので, この記事で補足しようと思います.
(なお, 同カンファレンスで他の方の発表を聞いての感想などは改めて別の記事に書くつもりです.)
1. PHP 5.4 では [] が配列リテラルとして導入された
特にこれ以上の説明は必要ないとは思いますが, 念のため.
今までは配列を以下のように記述していましたが,
PHP 5.4 alpha 3 以降からは以下のように置き換えることができます.
見ての通り, array() を [] に置き換えることができるようになった, というだけで機能的な変化はありません.
いわゆる Lightweight Language はその多くが配列リテラルとして [] を採用していますが, PHP では長らく使用することができませんでした.
パッチを送った @rsky さんを始めとする開発者の皆様のおかげでようやく使用することができるようになったので, 感謝をもって使用しましょう.
2. PHP の array は他の言語と比べて特殊なデータ構造である
これはスライド中でもそのまま説明していますが, PHP が持つ特徴として, 配列とハッシュに区別が無い, ということが挙げられます.
PHP における array は配列っぽさとハッシュっぽさを持った, 特殊なデータ構造であり, それらを混合して記述することができてしまいます.
これもスライド中で紹介していますが, PHP では以下のような記述が問題なく通ります.
そしてこちらはスライド中に一度入れて後に割愛したものですが, 要素の順序を入れ替えたらどうなるか.
表示上の順序こそ記述した通りの順番に変わっていますが, 集合としては等しい値であることがわかります.
0 番目の要素のあとに foo という要素を追加して, さらにもう一つ要素を追加しても, その要素には添字として 1 が割り当てられます.
3. その特性を DSL 的に活用することができる
これらを踏まえて, [] で奇妙なデータ構造を記述して, DSL っぽく活用することを思いつきました.
いきなりですが, スライドで紹介したテンプレートエンジン Paml を例に出します.
Paml には REPL (インタラクティブシェル) が同梱されており, Paml のコードを HTML に変換して出力することができます.
入力としての Paml (まぁ配列なんですが) の要素は順番が入れ替わっていますが, 出力はいずれも同じになっています.
もちろん, プログラムがそういう実装になっているというのもありますが, 前提としてこの 2 つの配列は集合として等しい, ということが重要です.
実装の詳細としては, この配列とハッシュが混合したデータ構造から, 配列とハッシュをそれぞれ取り出してから処理するようになっています.
スライドでは, より現実的なフレームワークへの応用として, モデル定義の記述に活用することを提案しています.
例えば, phpDataMapper (なんかここ数日アクセスできない状態ですが) という O/R マッパーのモデル定義は以下のようになっていますが,
これを以下のようにしてみてはどうか, ということです.
違いとしては, array() の代わりに [] を使っているのもそうですが, 後者ではデータ型の指定時の type という添字を使っていません.
イメージとしては, 第一引数に型名を指定して, そのあとにはさらなるオプションがキーワード引数として指定できる, といったような感じです.
型名はどのカラムにも指定する必要で省略不可能なので第一引数としていますが, 後は全てオプションなのでキーワード引数にする, といった具合です.
おまけ: LisPHP の紹介
スライド上ではコードとその出力を出しただけですが, ちゃんと実装しています.
以下は円周率 PI と円の面積を計算する関数を定義し, 実際に計算する例です.
その他, examples ディレクトリには LisPHP のサンプルコードもあります.
フィボナッチ数列による再帰の例 (スライドに掲載したものです) もあるので, ご興味のある方は是非.
LisPHP の特徴は以下の通りです.
- PHP の array をコードとして扱う (code as data)
- PurePHP で記述されている (ただし, PHP 5.4 alpha3 以降が必要)
- 同梱の REPL とインタプリタは PHP の eval を利用することで字句解析も構文解析も PHP まかせ
実はこれにも元ネタがあって, 元は Python で書かれた Lispy というものです.
こちらは string.split とかで超お手軽に字句解析・構文解析はしていますが.
((Pythonで) 書く (Lisp) インタプリタ
発表時, スクリーン上に LisPHP のことが出ていたのはたったの 5 秒間でしたが, 思いのほか笑いを取れて満足しています.
同カンファレンスにスピーカーとして来日していた方にもウケていたとのことで, 発表前日夜に思いついて, 5 時間ぐらいかけて実装した甲斐があったというものです.
(逆に Paml へのリアクションは思いのほか薄かった. これもネタだったのに)
まとめ
人前で LT をやるのは初めてで, とても緊張しましたが, とりあえずは満足する形で終えることが出来て良かったです.
素晴らしい機会をくださった PHP カンファレンス 2011 実行委員会の皆様, そして会場で聞いてくださった皆様に感謝致します.
なお, 次回があれば 30 分ぐらいで現場の役に立つ話がしたいと思っているので, 来年以降の開催も楽しみにしています.
2011-08-16 03:07:11
皆さん, ユニットテスト書いてますか.
TDD (テスト駆動開発) によるプログラミングは本当に楽しいものですが, コマンドをいちいち手動で実行するのは面倒ですよね.
テストを自動化しているんだから, その実行も自動化したいですよね.
この記事では, 私が仕事や趣味で使っている PHPUnit を例に, テストの実行の自動化について紹介します.
PHPUnit の, としてはいますが, 他の言語で使えるテクニックもあります.
なお, ここでの自動化は開発しながらの自動実行のことで, CI (継続的インテグレーション) の話は出てきません.
その前に...
私の開発時のターミナルは以下のようになっています.
[caption id="attachment_1298" align="alignnone" width="300" caption="開発時のターミナル"]
[/caption]
GNU screen での画面分割を利用して, 左半分をソースコードに, 右上をテストコードに, 右下をテストの実行に使っています.
これは作業の途中でいろいろと変動しますが, 右下ではほぼ常にテストが自動実行されるようになっています.
(ターミナルの透明化を利用してバックグラウンドでニコ動を観ることもできますね)
この小さい画面での実行を前提に, 紹介していきます.
シェルスクリプトを利用する
まずはとにかく, 決められたコマンドを定期的に実行しましょう.
コマンドを定期的に実行, というと watch コマンドを思い浮かべる人が多いと思いますが, これは使いません.
何故か, ターミナル上の色が無効化されてしまい, レッドやグリーンがわかりづらいからです.
色の使える watch は無いか, と探した所, superuser という Q&A サイトにちょうどいいものがありました.
bash watch command with colors preserved
ここへの書き込みを参考に, 以下のようなシェルスクリプトを用意しました.
適当な場所に保存して, chmod +x して実行権限を与えたら, 以下のように使用することができます.
$ ./watch2 phpunit --colors
これで, 出力の色づけを維持したまま, PHPUnit を 2 秒間のインターバルで起動し続けることができるようになりました.
プロジェクトの規模によっては全てのテストを実行するのに時間がかかることもあるので, そういうときは特定のファイルを指定しましょう.
$ ./watch2 phpunit --colors tests/Foo/BarTest.php
今自分が作業しているファイルだけを指定することで, フィードバックを素早く的確に得られるようになります.
コマンドの指定は手動ですが, 環境の用意はとても楽なのでよしとしましょう.
Stagehand_TestRunner を利用する
Stagehand_TestRunner は, PHPUnit だけでなく Lime や PHPSpec にも対応したテストランナーです.
(2011-08-16 14:00 追記: Lime への対応は勘違いでした. @heavenshell さんご指摘ありがとうございました.)
インストールについては本家の Wiki を参照しましょう.
テスト駆動開発のためのテストランナー
PHPUnit 本家よりも優れた表示が特徴です.
[caption id="attachment_1303" align="alignnone" width="300" caption="Stagehand_TestRunner による実行結果"]
[/caption]
この出力形式は testdox と呼ばれるもので, テストメソッド名を英文っぽく表示してくれます.
testdox を表示する機能自体は PHPUnit にもあります (--testdox オプション) が, Stagehand_TestRunner を使うと色づけされるので, よりわかりやすくなります.
Stagehand_TestRunner には他にも様々な機能がありますが, ここで紹介するのはディレクトリの監視によるテストの自動実行です.
これを phpunit.xml として保存して, 同じディレクトリ上で以下のコマンドを実行します.
$ phpunitrunner --phpunit-config=phpunit.xml -cRa
c が色づけ, R が指定ディレクトリ (phpunit.xml で指定していますね) 以下を再帰的に, a が自動実行のためのオプションです.
これで, ディレクトリに変更のあったときだけテストが実行されるようになりました.
実行対象のテストを正規表現で絞り込んだりすることもできますが, それらの詳細については以下の Wiki が参考になります.
Stagehand_TestRunner ユーザーガイド
watchr を利用する
最後に紹介するのは, 最近一番よく使っている方法です.
watchr はファイルの変更を監視するツールで, 検出時にあらゆる処理をフックさせることができます.
watchr は Ruby 製のツールなので, 処理は Ruby で書く必要があります.
といっても, 簡単な正規表現が書ければ充分なので, Ruby 未経験でも問題無いでしょう.
インストールについては Rubygems をインストールして gem install watchr するだけです.
watchr の設定ファイルとして, 以下のようなものを用意します.
これは src ディレクトリのソースコードの変更時に対応するテストを実行すること, テストコードの変更時にはそのファイル自身のテストを実行すること, を設定しています.
そして以下のようなコマンドを実行することで, 監視状態に入ります.
$ watchr phpunitrunner.watchr
前提として, ソースコードが src/Foo.php であればテストコードは tests/FooTest.php というように, 命名規則にそったファイル名になっていることが必要です.
変更のあったときにだけ, そのファイルだけテストが実行されるので, 素早く確実にフィードバックを得ることができます.
また, Ctrl + \ を押すことで, 全てのテストを実行することもできます.
注意としては, 監視対象のファイルの読み込みが watchr の起動時なので, 例えば途中で新規に追加したファイルは監視対象に入っていないことです.
ある程度開発が進んで, 作業のメインが既存ファイルの編集になったときに, より高い効果を発揮するでしょう.
まとめ
テストだけでなく, その実行まで自動化することで開発にリズムが生まれ, フロー状態に入りやすくなります.
開発効率を上げ, 楽しい TDD ライフを送りましょう.
その他, オススメのツールややり方などありましたら, 是非 @yuya_takeyama まで教えてください.
2011-08-14 01:58:00
Cache_Lite は未だに PHP4 なコードであんまりだし, Zend_Cache は依存関係がめんどくさそうなので, 外部ライブラリに依存しないものを作ってみました.
Github
Openpear
使い方
Cache_Casual オブジェクトは ArrayAccess インターフェイスを実装しており, 連想配列のように操作することができるので, かなりカジュアルだと思います.
カスタムキャッシュコンテナ
上記の例では, キャッシュコンテナとしてファイルを利用しています.
キャッシュコンテナは Dependency Injection (依存性の注入) により Cache_Casual オブジェクトに差し込むことができるので, Cache_Casual_ContainerAbstract クラスを継承し, Cache_Casual_ContainerInterface インターフェイスのメソッドを実装するだけで簡単につくることができます.
例としては, Cache_Casual_Container_Memory が一番シンプルでわかりやすいと思います.
今の自分の用途としてはファイルコンテナで充分なのでまだ作ってはいませんが, 例えば PDO 用のコンテナを書けば RDB にデータを格納することもできますし, memcached 用のコンテナを書けば同様のインターフェイスを持った KVS にデータを格納することもできると思います.
制限
これは各コンテナに依存する問題ですが, 値が serialize/unserialize できる必要があります.
File コンテナは serialize() してファイルに書き込んでいます.
個人的には今の所配列ぐらいしか保存する用途が無いので全然試してはいないですが, 多分循環参照を持ったオブジェクトとかは格納できないと思います.
テストについて
ユニットテストにはいつも通り PHPUnit を使っていますが, 今回は初めての試みとして, vfsStream というファイルシステムのモックライブラリを使用しています.
これは PHP のストリームラッパーという仕組みを利用して, 実際のファイルシステム上に操作を行うことなく, ファイルを用いた処理をテストすることができるというものです.
とても便利なので, これについては後日ブログにまとめる予定です.