Technote

by sizuhiko

CakeFest2012で発表&参加しました

<!– more –>

今年もBakerの祭典CakeFestが(昨年と同じ)マンチェスターで開催されました。

今年はCFPが通ってスピーカーとして参加することになり、だいぶ準備に時間をかけて….と思ったら実質2週間ぐらい前に夏休みの宿題状態で片付けたのでした。。。

CakeFestのカンファレンスは9/1と9/2の2日間で行われたのですが、私は休暇(30,31の)も含め8/29夜からマンチェスター入りをしていました。 ヒースローの乗り継ぎ(五輪効果で綺麗になっていた)や空港からの鉄道や徒歩など慣れたもので、飛行機や鉄道が遅れるという英国定番のトラブル(でもないらしい)で疲れての到着となりました。

カンファレンス1日目

簡単に1日目に参加したセッションをふりかえっておきます。

09:00 - 09:25 Keynote: CakeFest 2012 Introduction

これは本来グラハムがやる予定だったのですが、 急遽家庭の事情で来れなくなったので、最初のセッションがスキップされるかと心配された。時間になっても始まらないし、特に説明はないのだけれど、まぁCakeのコミュニティっぽいと言えばそうかなと思ったら、9:20になってマークが壇上に上がってなんとかキーノートを開始。

  • 今年は10カ国以上から参加者がいる。世界的に興味を持たれている
  • 2日間のワークショップと2日間のカンファレンス。2トラック、ライブストリーミングなど見どころがたくさん

実はマークとグラハムはSkypeでやろうと思ったけど、うまくつながらなかったというのを休憩時間に聞きました。

09:25 - 09:40 Keynote: Hell has Frozen Over

  • マイクロソフトのスポンサーセッション
  • 99.95% SLA
  • Linux,Mac,Windowsに対応したツールがある
  • 言語はASP.NETかNode.jsかPHP
  • FTPかGitでデプロイできる
  • 始めは無料だよ
  • サーバを作るときはリージョンとDBが選べる。MySQLが使えるよ
  • Gitリポジトリを設定して、ダウンロードできるよ
  • sharedインスタンスと、reserved(専用)インスタンスみたいなのがあって良いよ。最初sharedでresercedに移動も可能
  • ロードバランサでどこかが止まっても違うインスタンスに自動的に再デプロイされる
  • ビッグデータ、データベース、NoSQLストレージ、最適なネットワーク、キャッシュ(Memcacheとかね)、メッセージングなど使えるよ
  • 3ヶ月は無料
  • 詳しくは彼に聞いてね(午後のセッションをやる技術者らしい)、てきな振りで終わり w

09:40 - 10:30 Keynote: Making the most of 2.2

コアデベロッパーの中心人物であるマークとホセのセッション

ViewブロックがDRYじゃなくてなんとかしたかった

  • Jinja/Twigから影響を受けたよ
  • slots/blocksレイアウトがテンプレートで使える

JSONとかXMLとか面倒だった

  • _serializeというset名でビューに特別に使えるようにしたお

Setの問題があったね

  • Set::extractはいいけど、遅いよ
  • いくつかのxpathには対応していなかったし、解決できない問題もあった。内部はクレイジーだったぜ
  • ハッシュにして1.6倍ぐらい速くなったよ
  • ハッシュはsetの90%ぐらいが実装されている

CakeTimeとCakeNumberの問題があったね

  • HelperじゃなくてUtilライブラリで良いよね。HTML以外でも使うし
  • 良くテストされた datetimeを使っているよ

ロギングは特に問題だったね

  • pythonのscopesからアイデアをもらったよ
  • Configで設定すればmongoとかにもストアできる

イベント

  • いろんなところで使えるイベントシステム
  • いろいろとイベントが使えるようになっているよ
  • コールバックを使って簡単にできるよ
  • 独自イベントも簡単!

キャッシュ

  • assetコンパイルを使うと、部品をキャッシュできるよ
  • beforedispacherとかafterdispacherとか使うといいよ
  • Responseオブジェクトを使って設定するよ

キャッシュグループ

  • 何それ?
  • キャッシュに論理的なグループを作れるよ
  • 動的にはできないから、自分のstrategyで計画しなくちゃ行けない

バリデーション

  • 動的に追加、変更、削除できるようになったよ

11:00 - 11:30 Lightning Strikes from Windows Azure

ここから2トラックになる。 マイクロソフトのスポンサーセッションで技術担当のCoryによるテクニカルセッション

  • Macでも開発できるよ(ここ強調しているな)
  • 数クリックで開始できるよ
  • スケールを簡単に管理できるよ
  • 自動的にロードバランスするし、インスタンスを超えてストレージは共有される

Hello Worldのデモ

  • Macだぜ
  • azure site でオプションがいっぱい出る
  • azure site create ‘cf-cakegest’ –git
  • git add/commit/push
  • azureの管理画面で cf-cakefestが出ている
  • azure site browse – ブラウザが立ち上がった
  • pushすると自動的にdeployment historyがpushされているように見える
  • azure site deployment list – 一覧が見える
  • azure site deployment redeploy 'コミットID’
  • モニターでいろいろチェックして(404の数とか)追加で見れる
  • VMのOS選択はいろいろ選べるよ。もちろんLinuxも
  • azure vm コマンド使うよ
  • azure vm create -h とか
  • azure vm create 'name’ 'vm’ 'user’ 'password’ -l 'リージョン’
  • composerやgithubが使えるぜー

11:30 - 12:30 Create a RESTfull api

@ceeramのほうのマークによるセッション。

RESTとはの基本にとても時間をかけて説明していた(重要)。基本からPATCH、HATEOASまで全部入りだった。その後CakePHPでの適用方法について

router に追加するぞ

  • Router::parseExtensions(); // all
  • Router::parseExtensions('json’); // only json

RequestHandlerコンポーネント使うよ

  • viewClassMapを使うと変えられるよ
  • 'RequestHandler’=>array('viewClassMap’=>array('json’=>'RestJson’,'xml’=>'RestXml’))) とか

その他

  • 2.x からは _serialize を使って set すれば良いよ
  • 見つからなかったら NotFoundException とか
  • Router::mapResources(array('Cats’, 'Users’))とか使うとマップできるよ
  • APacheとかでPUTやDELETEを許可しておく必要があるよね
  • Router::resourceMap(array(array('action' => update','method'=>'PATCH'. 'id'=>true) みたいに書くといいよね
  • Router::mapResources(array('Cats’, 'USers’, 'Pizza.Orders’),array('prefix’=>’/api’) とか書くといいよ
  • Router::connect('url’, action… ) とかでもOK
  • CakeRequest::addDetector('patch’, array('env’=>'REQUEST_METHOD’, 'value’=>'PATCH’)); なんて書き方もできるよ(知らなかった)
  • CakeResponse 使ってね
  • 認証もいろいろサポート
  • Versioningが重要だよ

12:30 - 13:30 Lunch

カンファレンスに来たらランチが美味しいことはとても重要だと感じました。今年は本当においしかった!!

Random tips that will save your projects life

マリアーノのセッション。毎年興味深いセッションである。 内容はとにかく広範囲にわたっていて、

  • Git使えやー
  • Monitだろー
  • サーバ監視しろやー
  • PHPログはトラックした方がいいよね
  • Duckesboard
  • statsdとか
  • Google Analyticsもいいよね
  • やっぱ Varnishだよね
  • PHP -> Redis pub/sub -> Node.js -> soket.io -> Client でいいよね
  • Sphinx はスケーラビリティがあるし、PECLかSQL使えるし、リアルタイムインデックスだしいいよね

みたいな感じ。

How to make your CakePHP website into a Phone app

ここでは安藤さんのセッションの裏で、関西弁こてこてのリチャードさんによるセッションに参加。

  • PhoneGap では WebView を使ってappっぽく見せた
  • Zendで作ったけどね(笑)
  • 2,231,400 のユニークユーザを捌いたぜ。さすがBBC&オリンピック
  • 15,000,000 ページビュー/日だったぜ

その後

コアチームによるQ&Aセッションがあり、 いろいろ興味深いトークが繰り広げられました。

さらにLTがありの流れでビアタイムに。LTは発表者が何度もやったり、スライドはない方が多かったりドラはなかったり(まぁ当然か)、なんか日本のLTに慣れているとフワフワ感がたまりませんな(笑)

急遽#CakeRadioGaGaをGoogle+でやることになり、日本時間は日曜日の2時ぐらいだったのかな?ゲリラだったのに数名の閲覧者が見てくれました。

カンファレンス2日目

今日は発表日なのでなんか寝付きが悪くて、何度も目を覚ます事に。。。

09:00 - 10:00 Simple search with elastic search

2日目は最初から2トラックで。メイン会場はマークによりelastic seachのセッション。いわゆる検索エンジンで、よくApache Solrと比較されるやつ。マークのセッションはとてもわかりやすく、Cakeからのインテグレーション方法もよくわかった。これはスライドを見た方が良いね。

10:00 - 10:30 BDD for CakePHP applications

さて私のセッションですが、Twitterでの感想はなかなか好評だったみたいで、助かりました。マークからもランチのときにコメントをもらえたし。

トラブルが2つあって、

  • セッション中にレンタルしていた3G Wifiが死亡して、急遽ホテルのWifiへ切り替え。デモの最中に慌ててしまった
  • セッションのコンテンツ(英文)をDropboxに入れて、AndroidのMarkdownビューワーで見ながら話していたけど、Androidの自動回転がONのままで頻繁に回転が起きてしまう自体に。そうするとページTOPまでスクロールして、見直したら位置がおかしくなってアワアワしてしまった。自動回転はオフにしておかないとね。後半は記憶に頼って話しました。

で、ネタとしては、BDDのBはBeerだよね、というのは今年も鉄板でこれは世界中で通じる良い導入になります。それとマンチェスターなので香川ですな。和みました。

その後

なぜかここからざっくりとしたまとめになりますが、あまりCakeと直結する話題が少なくなったので、軽く。

  • あのYour code sucks, let’s fix itのRafaelによるセッションが。彼は現在アムステルダムに住んでいて、発表の内容からするとSymfonyガイなのかもしれないなと思いました。Composerの発表もしていたし。
  • 再びmackstarさんのセッションで、Backbone.js。これはCakeと直結しているのですが、まぁmackstarさんのgithubをチェックしましょう。Ember.jsについてはまだPHP界隈では使われていないし知っている人も少ないからBackbone.jsが良いねという話でした。
  • グラハムがやる予定だったFablicのセッションをマークが。ちょうど新原さんがTwitterでFablicについてつぶやいたのでストリーミング見ているのかと思ったら偶然だったらしく、びっくりしました

で、この後 PechaKuchaトークをやるまでの間に #CakeRadioGaGaの生放送を再び。日本では明日は月曜日なので早めの時間に。

この間にやっぱり結構帰る人がいて、さらに戻ってみるとラリーが機材を撤去していたり、何だろうと思ったら応募者がいなかったらしい。まぁLTでだいぶ発表があったのでそんなもんかなと。あとグラハムがいなくてイベントの仕切りが終始ふわふわしていたのも影響しているみたいでした。

もちろん定番のリアルケーキも出ましたよ。大きいものはホテルでは作れないということで、小さいものが7周年を記念して7つになったということでした。ランチデザートで出て、各テーブルに配られて行きました。そういえばケーキの前でのコアデベロッパー写真とか、全体の集合写真とかなかったなー えっ?レポートが段々尻すぼみだって?。いや、実際イベント全体のテンションもそんな感じで。。。。察してください www

まとめ

良かった事、反省点、いろいろあるけど、悪い事はないのでみんな海外のカンファレンスも出るといいね。もちろん発表することが重要で、ボクみたいにあんまり英会話得意じゃないけど発表すれば声もかけてもらえるし、プラグインや発表資料をチェックしてくれたりするので、それでフォローできればOKと思っています。

来年(こそ?)は、カルフォルニアらしいのでマンチェスターより行きやすい!。みんなで参加しよう!!

BDD Plugin for CakePHP2を公開しました

<!– more –>

BDD Plugin for CakePHP (https://github.com/sizuhiko/Bdd) はCakePHPからRuby on Railsのように2つのBDDフレームワークを実行できるようにするインテグレーション・プラグインです。
2つのBDDフレームワークは

  • スペック・フレームワークは Spec for PHP
  • ストーリー・フレームワークは Behat を利用しています。
    それぞれのフレームワークの特徴は、共に外側のインターフェースを自然言語で記述できることでコミュニケーション・ツールとしての役割を持っています。

BehatをCakePHPから利用するためのプラグインとしてCakeBehatを公開してきました。
そういう意味で、これはCakeBehatの改良版と言って間違いありません。
CakeBehatからの進化ポイントは以下のとおりです。

  • スペックフレームワーク Spec for PHP を実行可能に
  • 依存関係の解決に Composer を利用

詳しくは github のREADMEで書いているのですが、英語(あっているか不安がたっぷりですが)で書いてあるので、安心の日本語で解説したいと思います。
今回はサンプルアプリケーション BddExampleApp (https://github.com/sizuhiko/BddExampleApp) に簡単なチュートリアルを書いているので、こちらの流れを中心に説明していきます。

インストール

  • CakePHP2 をインストール (2.2以降を推奨)
  • PHPUnit をインストール(pearなどを利用)
  • git cloneを使うかgithubのサイトからサンプルアプリケーションBddExampleAppをCakePHPのルートディレクトリに展開。このとき app, feature, spec ディレクトリが必要になります。
  • テストデータを投入するのにfixture以外の仕組みとして fixture-factory(https://github.com/rodrigorm/fixture-factory) を使っています。リンク先の installation を参考にインストールして欲しいのですが、ディレクトリ名が解決できない問題(?)があるのでvendorsディレクトリをvendorにリネームしてください。

    $ mv app/Plugin/fixturefactory/vendors app/Plugin/fixturefactory/vendor

  • データベースを作成して、テーブルを作成してください。データベースはテスト用、開発用の2つが必要で、その両方に app/Config/sql/posts_ja.sql のSQLを実行してください。

  • 環境設定ファイル app/Config/core.php と database.php を環境に合わせて変更してください。database.phpのデータベース名やログイン、パスワードなどは前項で設定した値に変更してください。またブラウザ経由のテストでテスト用データベースを参照するために test.localhost のようなテスト時のホスト名が必要なので、そのままhostsに設定するか違う名前に変更してください。

  • BDDプラグインをインストールします。CakePHPのプラグインディレクトリ (/plugins)  に移動して git clone するか、githubのサイトからダウンロードして展開してください。

  • そうすると /plugings/Bdd のようなディレクトリ階層になるので、その /plugings/Bdd に移動してください。Composerをインストールして依存関係を解決します。

    curl -s https://getcomposer.org/installer | php php composer.phar install –dev

  • 最後にbehatが利用するテスト用URLを設定します。app/Config/behat.ymlの設定値はひとまず「base_url: ’http://test.localhost:8888/bdd/‘」のようになっているので、実際にインストールしたアクセス可能なURLに変更してください。このときdatabase.phpで設定したテスト用のホスト名になってることが重要です。

インストールが完了したら、http://localhost:8888/bdd/posts/ にアクセスして3件のリストが表示されていれば大丈夫です。

テストを実行してみる

CakePHPのルートディレクトリに移動して以下のようにコマンドを実行します。behatのテストでseleniumを利用しているので、可能であればコチラ(http://www.phpunit.de/manual/3.6/ja/selenium.html)を参考にseleniumをダウンロードして実行しておいてください。

$ lib/Cake/Console/cake Bdd.spec

Welcome to CakePHP v2.x.x Console
---------------------------------------------------------------
App : app
Path: /your CakePHP root/app/
---------------------------------------------------------------
•••••••••••••••••••••••••••••••••••••••••••••••••
▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁
✔ OK ❯ Passed 49 of 49 (0.17s 22Mb)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔

$ lib/Cake/Console/cake Bdd.story

Welcome to CakePHP v2.x.x Console
---------------------------------------------------------------
App : app
Path: /your CakePHP root/app/
---------------------------------------------------------------
Feature:
  In order to tell the masses what's on my mind
  As a user
  I want to read articles on the site

  Background:              # /your CakePHP root/features/posts.feature:7
    Given there is a post: # /your CakePHP root/features/steps/posts_step.php:3
      | Title              | Body                          |
      | The title          | This is the post body.        |
      | A title once again | And the post body follows.    |
      | Title strikes back | This is really exciting! Not. |

  Scenario: Show articles                 # /your CakePHP root/features/posts.feature:14
    When I am on "TopPage"                # FeatureContext::visit()
    Then I should see "The title"         # FeatureContext::assertPageContainsText()
    And I should see "A title once again" # FeatureContext::assertPageContainsText()
    And I should see "Title strikes back" # FeatureContext::assertPageContainsText()

....

スペックフレームワークの使い方

基本的なコマンドラインのオプションなどはSpec for PHP(https://github.com/drslump/Spec-PHP) に対して透過的になっていて、意味もPHPUnitと似ている(実際内部はPHPUnitである)ので、感覚は掴みやすいと思います。
前のコマンド実行例では Bdd.spec にオプションがない状況でしたが、代表的な使い方は以下のとおりです。

# デフォルトディレクトリ(spec)にあるspecファイルをすべて実行
lib/Cake/Console/cake Bdd.spec

# 指定したディレクトリ(spec)にあるspecファイルをすべて実行
lib/Cake/Console/cake Bdd.spec spec

# 指定したファイルだけを実行
lib/Cake/Console/cake Bdd.spec spec/post.spec.php

# オプションの詳細を見る
lib/Cake/Console/cake Bdd.spec --help

スペックファイルの命名ルールは以下のようにspec.phpで終わる3通りです。

  • post.spec.php
  • post_spec.php
  • PostSpec.php

モデルをテストする

モデルをテストするためにCakeTestCaseを継承したCakeSpecというPHPUnitのテストケースを用意しています。これを classアノテーションで指定します。

あとはフィクスチャですが、これはCake本来のテストで利用するものと同じで、宣言する場所と意味がちょっと変わってきます。
Cakeのユニットテストでは、テストケースクラスのメンバ変数として$fixuresを宣言するのですが、スペックでは$W(ワールド)インスタンスのfixtures変数にセットします。arrayの書き方は同じです。ただしスペックコードではitブロックの度にfixturesが解決されるのでitの前のbeforeやbefore_eachブロックで書いておけば1ファイル中に何度出て来ても問題ありません。ちょうどloadFixtures()のような動きなのですが、この場合もCakeTestCaseではfixturesにテストケース内で利用するすべてのフィクスチャを書いておく必要がありますが、スペックでは下位階層で利用する分だけでOKです。これはスペックコードのスコープによることろなのですが、これを詳しく知るには、Spec for PHPがどのようにDSLで解釈してPHPコードをPHPUnitに実行させるかを見た方が良いでしょう。

# スペックコードをPHPコードに変換して出力
$ lib/Cake/Console/Bdd.spec spec/post.spec.php --dump

Spec for PHP はPHPUnitのTestSuiteとTestCaseを利用しています。このうちitブロックがTestCaseに該当します。その他のdescribe、contextやbeforeはTestSuiteになっています。つまりitブロックで $this 変数を使うと TestCase のインスタンスになります。それ以外のbeforeなどは実際にはTestSuiteにマッピングされるので、実行中のテストケースインスタンスを取得したい場合は DrSlumpSpec::test() を呼び出す事で利用可能になります。このスコープが重要になってくるのが、次のコントローラのテストです。

コントローラをテストする

コントローラをテストするためにControllerTestCaseを継承したControllerSpecというPHPUnitのテストケースを用意しています。モデルと同様にclassアノテーションで指定します。ControllerTestCaseを継承しているのでもちろんtestAction()が利用可能なのですが、ここで重要になるのが、スコープです。it以外でtestActionはControllerTestCase($this)から呼び出せないので、DrSlumpSpec::test()->testAction のように記述します。fixturesに関してはモデルと同じく下位階層に対して有効になります。

Spec for PHPを使いこなすには

Spec for PHPの評価(マッチング)はHamcrestを利用しているので自然言語で書く事が可能です。私はPHPUnitのassertXXXXXを覚えるよりも直感的で、こう書いたらできそう(実際できる!!)というのが良いと思っています。
どんな書き方があるかは、サンプルアプリケーションの spec 下に post 以外のテストコードも使い方例として入れてあるので、それを読むとわかるのではないかと思います。

ストーリーフレームワークを利用する

こちらもスペックと同じく基本的なコマンドラインのオプションなどはBehat(http://behat.org/) に対して透過的になっているので、詳しくは公式のドキュメントかhelpで見るのが良いでしょう。
先のコマンド実行例では Bdd.stoy にオプションがない状況でしたが、代表的な使い方は以下のとおりです。

# デフォルトディレクトリ(features)にあるfeatureファイルをすべて実行
lib/Cake/Console/cake Bdd.story

# 指定されたfeatureファイルを実行
lib/Cake/Console/cake Bdd.story features/posts_ja.feature

# より詳しいヘルプを見る
lib/Cake/Console/cake Bdd.story --help

これ以外に関していえばCakeBehatと基本的な仕組みは変わっていません。

フィーチャーを書く

公式ドキュメントの “Define your Feature in Quick Intro to Behat” : http://docs.behat.org/quick_intro.html#define-your-feature を見ると良いかな….(手抜きですみません)

実際のサンプルには日本語記述も入っています。

ステップを書く

これも公式ドキュメント(See “Writing your Step definitions in Quick Intro to Behat” : http://docs.behat.org/quick_intro.html#writing-your-step-definitions)を読むといいね、ってことなんですが、Behatのドキュメントで最初に出会えるステップの書き方は、FeatureContextにステップを追加していく書き方です。これだと独自ステップが少ないうちは良いのですが、増えて来たら分割するのか、どういう単位で分割するのか、そのままビッククラスにするのか…という分岐点にたたされます。
それぐらいなら最初から分けておこうということで、例えばフィーチャの単位であったり自分たちで決めたルールでステップファイルを分割できる方式にしておきましょう。Behatではクロージャ定義も可能なので http://docs.behat.org/guides/5.closures.html の書き方に従って書いてみましょう。サンプルアプリケーションもこの流れに沿っています。
基本的にステップをstepディレクトリ以下に書くようにすることで、FeatureContextを弄る必要はなくなるはずで、将来プラグインのバージョンが上がってFeatureContextが書き変わっても問題はおきません。

CakePHPとの関係

まぁこのままだとただのBehatなので、プラグインは何をしてくれるの?というとCakeBehatと同じくapp/Config/database.phpの設定を元にテストデータを投入するためのインターフェースを用意しています。まぁそれだけなんですが(それを言うとスペックもアノテーション部分ぐらいしか…. )

先の日本語フィーチャでテストデータを登録するステップで、以下の2つのCakePHP連携APIが利用可能です。

  • truncateModel(モデル名) : 指定されたモデル名のテストデータを削除します
  • getModel(モデル名) : テストデータを登録するモデルのインスタンスを取得します(ClassRegistory::init(モデル名)と同じです)

テストデータは自動で削除されないので、このように必要なタイミングにてtruncateModelを呼び出すようにしてください。

ちょっと便利な機能

Behatは表面的には「前提 “トップページ” を表示している」のように表示するページを指定する場合はURLを書くようにガイドされています。しかし内部的には別名をURLに置き換える事も可能なので、BDDプラグインでは features/support/env.php にページ別名を書けるようにしています。この方がフィーチャファイルの可読性があがると覆います。

今後の展開

このプラグインを使ったBDDについてCakeFest2012の30分枠で話します。
その後、CakeBehatは完全にこちらへ移行となり(マイグレーションもそれほど難しくないと思う)ます。
Spec for PHPについては、私のdevelopブランチを利用しています。こちらは本家にはない機能(subjectもその1つ)を実装していたりします(そのうちPRするかも)。
Behat/Minkについても日本語問題をチェックしていたりしますので、統合的にメンテしていきたいと思っています。
また、これらで発生した問題についてもgithubのissueに日本語で構わないので指摘していただければと思います。

PHPのスペックフレームワーク

<!– more –>

PHPでのBDDについては、何度か取り上げてきましたがほとんどがストーリフレームワークであるBehatについてで、スペックフレームワークについてはあまりふれてきませんでした。せっかく今回はPHPカンファレンス関西のLTで2種類のBDDフレームワークについて話したので、スペックフレームワークについてまとめてみたいと思います。

最初にたどり着くのはPHPSpec

LTのスライドでも紹介したのはPHPといえばまず最初に思いつく PHPSpec です。大抵はまず「言語プレフィクス+Spec」で検索してみますよね。PHPはユニットテストがPHPUnitだからPHPSpecだよね、と誰もが連想できます。

で、PHPSpecの書き方としては、

class DescribeNewBowlingGame extends PHPSpecContext {  
    private $_bowling = null;  
    public function before() {  
        $this->_bowling = $this->spec(new Bowling);  
    }  
    public function itShouldScore0ForGutterGame() {  
        for ($i=1; $i<=20; $i++) {  
            // someone is really bad at bowling!  
            $this->_bowling->hit(0);  
        }  
        $this->_bowling->score->should->equal(0);  
    }  
}  

のように、まぁPHPのコードだとすれば読むのはそれほど困難ではないわけですが、やっぱりSpec(仕様)という視点で眺めると、ちょっと無駄な記述が目立ちますよね。それは言語の文法だからしょうがない、そうなんですが。

その他のスペックフレームワーク

で、試しにGitHubで他にないのかと探すと、意外とスペックフレームワークがある事に気がつきました。

名前所感

PHPSpec 最初に出てくる有名どころ

pecs JSpecに影響を受けている。しばらくメンテされていない…

spectrum まだα版でドキュメントもロシア語しかないが、設計の筋が良くいろいろできそう。今後に期待

speciphy yuya-takeyamaさん(@yuya_takeyama)がRspecに影響を受けて作成。subjectが使える。コアはPHPSpecを利用している

Spec for PHP Hamcrestライブラリとtokengetall関数を使って、自然に英語として読める文章として記述が可能。解析した結果をPHPUnitのテストケースとして生成して実行している

以下のコードは、Speciphy を例にPHPSpec以外でどのように書けるかをあらわしています。pecsもspectrumもほぼ同じような記述になります。

namespace SpeciphyDSL;
return
describe('Bowling',
    describe('->score',
        context('all gutter game',
            subject(function () {
                $bowling = new Bowling;
                for ($i = 1; $i <= 20; $i++) {
                    $bowling->hit(0);
                }
                return $bowling;
            }),
            it('should equal 0', function ($bowling) {
                $bowling->score->should->equal(0);
            })
        )
    )
);

だいぶ読みやすくなりました。ではRSpecと比較してみま….す?えっ、やっちゃうの、それは….

describe Bowling, "#score" do
  it "returns 0 for all gutter game" do
    bowling = Bowling.new
    20.times { bowling.hit(0) }
    bowling.score.should eq(0)
  end
end

こんな感じです。subjectやcontext使っている違いとかありますが、PHPの場合はどうしても無名関数を使うのに

function () {…}

を使わなくてはならないので、そこが惜しいのです。

変態PHPスペックフレームワーク Spec for PHP

そこで Spec for PHP ということなのですが、こいつはだいぶ変態です(ごめんなさい。良い意味です)。まず先程紹介したような funcition(){ … } の無名関数で書いたような記法も使えます。それはほぼ同じように書く事が可能です。で、何が変態かと言うと Hamcrestライブラリとtokengetall関数を使って構文解析することで、以下のようなSpecが書けちゃいます。

describe "Bowling"
  describe "#score"
    it "returns 0 for all gutter game"
      $bowling = new Bowling;
      for ($i = 1; $i <= 20; $i++) {
        $bowling->hit(0);
      }
      $bowling->score should equal 0
    end
  end
end

it ブロックの内容は、Hamcrestを使って$bowling->score should equal 0 のように書いていますが、基本的にはPHPのソースコードであるとわかるでしょう。ただしdescribeやitなどの構文からはfunctionが消えて見通しが良くなっています。

では早速試してみましょう

Spec for PHP は以下の依存関係を持っています。

  • PHP 5.3
  • PHPUnit 3.5
  • Hamcrest matchers library(ベータ版のときに作成されたらしい)
  • PHP Object_Freezer 1.0.0

この記事の執筆時点で最新版の依存関係を適用すると、PHPUnitとHamcrestを利用している箇所で一部エラーになってしまいます。そこで、これらを修正し本家にPull Requestを送付しています。
このため、本家に取り込まれるまでは、私のforkリポジトリから取得することを推奨します。

https://github.com/sizuhiko/Spec-PHP

上記問題に対応するための修正と、日本語関連でPHP関数名の正規表現に x7f-xff の範囲を追加した修正が、 develop ブランチに入っていますので、こちらでお試しください。

まず必要なpearパッケージをインストールします。すでにインストール済みならスキップして構いません。

pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit
pear install phpunit/Object_Freezer
pear install Console_CommandLine

Install Hamcrest matchers library
pear channel-discover hamcrest.googlecode.com/svn/pear
pear install hamcrest/Hamcrest

つづいて、githubからcloneしてdevelopにスイッチします。

git clone git@github.com:sizuhiko/Spec-PHP.git
git checkout origin/develop -b develop

Spec for PHPはPHPUnit経由の実行方法と、Cliランナーも持っています。

  • phpunit tests/AllTests.php
  • ./spec4php.php tests

さいごに

Spec for PHP はテストコードを書いていて楽しい気分になれるような気がします。まだまだ奥がありそうなので、ちょっと追いかけてみようと思っています。また本家にPRが取り込まれたときなどに、インストール方法など更新予定です。

また、PHPのスペックフレームワークについて「もくもく会」か「勉強会」をやろうと思っていますので、こちらは@sizuhikoのTwitterなどをチェックしておいていただければと思います。

PHPカンファレンス関西2012に参加しました

<!– more –>PHPカンファレンス関西2012のため、5/11夕方から5/13昼頃まで大阪へ出かけていました。

今年は2トラックで興味深いセッションが多く、とても楽しめました。スタッフの皆様、スポンサー、並びに忍びの方々 ありがとうございます。

前夜祭では噂の無限もやしを食べられた(※1)し、懇親会や2次会、3次会ではかなり濃い技術ネタにあふれていてとても楽しかったです。あぁ、こう書くと飲みに行ったみたいに見えちゃいますね….(半分は事実ですが)

イベントでは「BDDで行こう」というLTが無事当選し、発表してきました。資料はこちら(TwileShare)です。タイミング良くこれから私が作成したCakeBehatの紹介が始まる、というところで銅鑼が鳴りまして、まぁ発表者的には美味しい流れでした www

きっかけは作れた(※2)ので、続きはWebで(GitHub) ということですね。スライドの内容とGitHubのREADMEを併せてみてもらえれば、CakeBehatが何をしているのかわかると思います。
とはいえまずはBehatを使ってもらって、その後でCakePHP使っている人はぜひ、という流れです。

それと懇親会LTが行われるということで、本編LTでも発表するので空気を読んで自重していたのですが、追加枠が出たということで14:30ぐらいの段階で申し込みました。懇親会まで3時間ぐらい、その間に本編LTもある中でスライド(TwileShare)を作成しました。内容はBlogで公開しているQueryPathの紹介と、GitHubで公開しているQPPHPのREADMEを合体したものです。これまでにLTをやったことない人でも、このように既出のBlog記事などから資料を作れば簡単にできると思います。@koyhogeさんの懇親会LTでも出てきたのですが、5分なので何があってもノリで切り抜けられる場合がほとんどです。見慣れた顔ぶれだけじゃなくて、新鮮なネタも応募してもらえるといいなぁと思います。

LTで発表したCakeBehatQPPHPについて、興味をもたれてわからないことがあれば、GitHubのissueなどでご連絡いただければと思います。

また来年のPHPカンファレンス関西が楽しみです。

それでは皆様、PHPカンファレンス(東京)とPHPMatsuri(福岡)でお会いしましょう!!

※1: もちろん有限になる場合があります。翌日3次会で行ったら終わっていたのです。。。

※2: 5分ではすべて紹介できないので、面白そうと思ってもらえたところで時間(銅鑼)になることで、Webサイトを訪問してもらえる&誤解を招かなくていいかなぁと思っていたりします。

QueryPathを使ってHTMLを操作する

<!– more –>昨年末phpQueryについて、これはいいね!と取り上げたのですが、その時点でも若干更新が止まっていたり、XHTMLの解釈が微妙だったりとかいくつか不安要因があったのですが、そんな不安を解消するjQueryインターフェースを実装したライブラリを発見しました。

それがQueryPathです。

QueryPathが当たり前に良いと思う点

  • 日本語が普通に使える
  • XHTMLが普通に使える
  • 更新されている
  • 拡張(extension)が書きやすい
  • テストコードがある
  • jQueryインターフェースがほぼ忠実に再現されている

こういう当たり前にできて欲しい事が、意外と日本語環境だとうまくできなかったりするのですが、特に意識することなくできるのは良いと思います。 もちろんmetaの文字コード指定など、ある程度ちゃんとHTMLが書かれている事は重要です。

QueryPathが独特と思う点

QueryPathは標準で5つの拡張機能(extension)を持っています。

  • QPDB : データベースへのアクセスと、検索結果をテンプレートへの差し込むことができるようになる
  • QPList:配列で渡した内容をtableタグやul、olなどに展開してHTML化できる
  • QPTPL:連想配列のキーをidやclassなどのクエリ情報と解釈して、テンプレート流し込み機能を提供する
  • QPXML:XMLを操作する拡張機能
  • QPXSL:XSLTを利用する拡張機能

独特というのは、拡張機能が簡単に実装できるので、拡張を使えばこんな事、あんな事、jQueryプラグインを書くかのように自由にできますよ、という部分に集約されると思います。

使ってみよう

インストール

QueryPathは3つの導入方法に対応しています。

  • PEARパッケージ
  • pharファイル
  • Composer

ここではpharファイルをダウンロードして始めてみます。pharファイル以外の導入方法はGitHubの公式ページを参照してください。

ダウンロードページより QueryPath-version.pharというファイルをダウンロードします。この記事時点での最新versionは2.1.2です。ダウンロードしたファイルの名前をQueryPath.pharにリネームしておきます。

サンプルHTMLの取得

前回のphpQueryと同じく、WORDPRESSの日本語TOPページのHTMLを使ってみたいと思います。

wget http://ja.wordpress.org/

phpshを使って試す

こちらも前回と同様に、簡単に試すにはインタラクティブシェルを使うのが便利なので、phpshを使います。

bash-3.2$ phpsh
Starting php
type 'h' or 'help' to see instructions & feature

php> require 'QueryPath.phar';

php> $html = htmlqp('index.html');

php> = $html->find('#blog .post .entry')->eq(0)->text()
"ntttttttttWordPress 3.3.2 が利用できるようになりました。これはすべての前バージョンへのセキュリティアップデートです。nWordPress に含まれる3つの外部ライブラリでセキュリティアップデートがありました:n    Plupload (バージョン 1.5.4)、WordPress ではメディアのアップロードに使用しています。n    SWFUpload、WordPress では以前にメディアのアップロードに使用していました。おそらくプラグインによってはまだ使われています。n    SWFObject、WordPress では以前に Flash コンテンツの埋め込みに使用していました。おそらくプラグインやテーマによってはまだ使われています。ntttttttt"

ブログエントリーの先頭記事を抜き出してみました。phpQueryは配列キーにセレクタを記述する方式だったのですが、QueryPathではPHP言語としての「->」の部分以外はjQueryと同じだとわかるでしょう。 またQueryPathにはqpというコア関数もあるのですが、こちらはXML用で、HTMLを利用する場合はhtmlqpを使っておいた方が良さそうです。

最後に

php> = $html->top()->html();

を実行すると、HTML全文を文字列として取得することができます。日本語がまったく問題ないことを確認できると思います。

※ $html->top()->innerXHTML(); ではなく、html関数でないと htmlタグとか含まれないので修正しました。(5/7)

まとめ

現時点でもHTMLの取得にはURL指定、ファイル指定など様々な対応がされており、DB連携やテンプレート機能などそのままでも結構つかえるんじゃないかと思っています。

ただ毎度DOM操作するの?とかいう部分もあるとは思うので、phpQueryっぽいPHP構文が含まれたPHPファイルを出力できるような拡張を作ってみたいなと思っています。まずは素のPHP部分から、その後でCakePHPとの連携も含んでみたいなぁという計画です。拡張名は標準のものを参考にするとQPPHP?なのかな。Pが続くと見づらいなぁ。名前は大事なので何か考えないと。。。