Symfony Advent Calender 10日目を書きました
2011/12/10 土曜日 16:49:29テーマは Timezone 対応。
Doctrine2 は datetime型 のフィールドについて \DateTime で値が帰ってくるので
だいぶ楽になっている感じです。
テーマは Timezone 対応。
Doctrine2 は datetime型 のフィールドについて \DateTime で値が帰ってくるので
だいぶ楽になっている感じです。
タイトルのとおりであります。
http://labs.bucyou.net/php54-2011-3/
テーマは、基本的な関数の増減と、仕様変更です。
JsonSerializable は、リソースからJSONへの変換方法が統一されそうなので、
便利なんじゃないかなー。なんて考えました。
前回のブログを書いたあとに、 @hidenorigoto 先生から指摘を受けてしまったので Mockeryを紹介してみるよ。
知らなかった自分が恥ずかしいが、PHP5.3以上に対応ということも助けて、かなり高機能だよ。
パフォーマンスとかはどうかはまだ体感していないですが。
本シリーズで紹介している通り、static method によるテスト阻害は
なかな苦しいものです。
しかし、ロード済みでないという制約下のもとで、Mockery による
static method のモックを作ることができます。
(やはり、クラスオートロード利用向けな予感がします。)
以下が例です。
SampleSingleton クラスが、仮にDBの接続系だったりすると、それを利用している
Sample::sfoo() などのテストはやりにくくなってしまいます。
なんとかなるよ! Mockery ならね。
なんという事でしょう。これだけで、OKです。
Prefixing the valid name of a class (which is NOT currently loaded) with “alias:” will generate an “alias mock”. Alias mocks create a class alias with the given classname to stdClass and are generally used to enable the mocking of public static methods. Expectations set on the new mock object which refer to static methods will be used by all static calls to this class.
(アホ超訳)
“alias:” というのを接頭語として正しいクラス名 (まだロードされていない) を利用すると、エイリアスモックを作成します。エイリアスモックは stdClass に対象クラス名でクラスエイリアスを作成します。それは一般的に public static メソッドのモッキングに有効です。static method を参照する新しいモックオブジェクトにセットされた expectations は、そのクラスのすべての static call が呼ばれたときに使われます。
なるほどなるほど。
ちなみに、PHP5.3新関数 の class_alias() という関数を駆使しているかんじです。
今回の例では、SampleSingleton をエイリアスとして、モックのクラスを読んでいます。
で、これまた PHP5.3 の新機能である __callStatic を使って何やらムフフやっている予感がします。
Mockery は、本当にリア充なので、 JMockit でできるようなパーシャルモックもやってのけます。
上の場合、Sample::bar() メソッドだけ、モックするよーという感じです。
Sample::foo() は維持してくれます。
残念なことに、private method のモック実現は runkit 使わないと難しいそうだけど。
まだまだ、ドキュメントを読み始めたばっかりなので、
面白い使い方があったらまた紹介します。
前回の Java編に引き続きまして
今回も、テスト対象コードを変えずに、頑張ってテストを書くにはどうしたらいいのかを考えてみます。
// もちろん、リファクタリングをするべきですが、リファクタリングの足掛かりとして
// テストを先に書いておきたいという思いがあるのです。
PHPでも Mock というか、Test Double を楽しむための方法が幾つかありそうなのでまとめてみる。
symfony1.x の、 sfContext (Singletonパターン) に関わるテストで利用されている手段です。クラスの定義してあるファイルを自動的にロードするための機能spl_autoload_register() で、登録した関数は、すでにクラスが定義済みであった場合は呼ばれません。
これを利用して、全く同名のモッククラスを定義したファイルを用意しておき、テスト時に require_once などで、そのファイルを呼び出してやるわけです。これで、対象とするクラスの動きとしてはモッククラスの動きになります。
非常でお気軽な方法ですが、モック対象が頻繁に変更するようなケースなどではオススメできないかもしれません。
すべてのメソッドを網羅している必要はなく、騙すだけで良いならばこの方法はいいかもしれません。
テキトーなので、autoload.php は PSR-0 などに全然沿っておりませんのでご了承ください。 test1.php に関しては、オートロードで lib/Sample.php がロードされることはなく、先に定義した Sample クラスが利用されていることがわかります。
JMockit のように、メソッドの一部のみの挙動を変更したい!というニーズに答えるならば runkit になるかと思います。 php モジュールになってしまいますが、メソッドの内容を書き換えたり、定数を消したりとなかなか魔術を感じる便利なものです。 ただし、PHP5.3 では pecl からインストールができないので svn からソースコードを落とし、手動でコンパイルしてやる必要があります。
$ svn co https://svn.php.net/repository/pecl/runkit/trunk/ runkit
$ cd runkit
$ phpize
$ ./configure
$ make
$ sudo make install
以下は、クラスの定義を書き換えている様子です。
runkit を利用して、かつ JMock のように使いやすくやろうじゃないか的なライブラリもあるのですが、
どうやらメンテされてません。誰かクールなのを作りませんか? 言いだしっぺですかそうですか。
PHPUnit の Mock は、テスト可能なコードを書いていけば使い物になりますが、レガシーなコードのテストをまずは書いて進みたいという時には適さない様に思えます。
ちなみに、Symfony2 のコンポーネントは、この機能を利用しているため、テストもコードも非常に洗練されているような気がします。
@hidenorigoto 先生にツッコミを受けたので、続編を書きます。mockery はどこまで出来るかわからないですけど、ドキュメント見た限り、なかなかクールでした!(ってか初めて知りました。)
この物語はフィクションです。そういうことにしておいてください。
こんにちは。Java訓練中の川原です。
テストがない。
そんな現実のレガシーコードと戦う事になったとします。
テストがないコードというのは、テストを実行することを考えて作られていないので、いざテストを書こうとすると(僕の技量がまだまだなことも大きいが)怖気づいてしまい、結局またテストが書けないという悪循環に陥る可能性があります。
改修するタイミングなどに、思い切って改修する場所の周辺だけでもテストを作っておきたいものです。
勇気必要ですよね。ケント・ベック本の「まえがき」にもあるとおりです。

今回は、 Java のケースを想定してください。
Controller.execute() の改修を行うので 、命綱として Controller のテストを書いておきたいとします。
名前の通り、 Singleton は、 Singleton Pattern を実装したクラスです。 例えば、データベースの接続状態などを管理し、データを取得するような機能を持つとかそういうのを想定してください。
Util はクラスメソッドをもっています。やはり、こちらも外部コンテンツに接続した結果が帰ってくるようなものを想定してください。
おまけに、 Controller には、 fetchData() という、これまた何が帰ってくるかわからないようなメソッドがあったと想定します。
Singleton.foo() 、 Util.bar() そして、 Controller.fetchData() は、テストとしてはなかなか扱いづらいです。しかも、ものすごくたくさんの場所で使われているとして、他のクラスがまったくテストを書いていないとすればいきなり全力でゴリゴリとリファクタリングしまくるのも辛いわけです。
(時間ないなら尚更辛い!)
そんな時に、Mock を書くことになるかと思いますが JMock では苦しい気がしました。なぜなら、JMock では Mock として利用されるインスタンスは、インターフェイス化されていないといけないからです。
また、 Util.bar() のようなクラスメソッドをモックする機能は JMock にはありません。
Java には、 Mockito という Mocking Framework もありますが、やはりクラスメソッドのモック化は厳しいような雰囲気でした。
というわけで、 現実と戦うためには JMockit が最強っぽい予感です。
// Controller.java
package net.bucyou;
import org.apache.commons.lang3.RandomStringUtils;
public class Controller {
public String execute() {
return fetchData()
+ Singleton.getInstance().foo()
+ Util.bar();
}
public String fetchData() {
return RandomStringUtils.randomAscii(2);
}
}
// Singleton.java
package net.bucyou;
import org.apache.commons.lang3.RandomStringUtils;
public class Singleton {
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
if (null == Singleton.singleton) {
Singleton.singleton = new Singleton();
}
return Singleton.singleton;
}
public String foo() {
return RandomStringUtils.randomAscii(2);
}
}
// Util.java
package net.bucyou;
import org.apache.commons.lang3.RandomStringUtils;
public class Util {
public static String bar() {
return RandomStringUtils.randomAscii(2);
}
}
さて、厄介なコードを用意しました。
RandomStringUtils.randomAscii(2); に関しては、ランダムで2文字のAscii文字を返します。
当然、 Controller.execute() した結果として、何が帰ってくるかはわかりません。
// ControllerTest.java
package net.bucyou;
import static org.junit.Assert.*;
import static org.hamcrest.core.Is.is;
import org.junit.Test;
public class ControllerTest {
@Test
public void testExecute() {
Controller controller = new Controller();
assertThat(controller.execute(), is("AABBCC"));
}
}
とりあえず書いたテストです。当然失敗です。
さて、モックを作る準備として、 jmockit.jar をビルドパスに組み込みます。
ドキュメントによると、 classpath の順序に注意らしいです。JUnit の前に持って行きましょう。
さらに、テスト実行時に -javaagent:jmockit.jar などと、 VM argument に追加してやる必要があるそうです。
(ココらへんが、どういう理由で必要なのかはまだちゃんと理解してないです。)
さぁ、モックを書こう!
// ControllerTest.java
package net.bucyou;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import mockit.Expectations;
import mockit.Mocked;
import org.junit.Test;
public class ControllerTest {
@Mocked
final Util util = null;
@Test
public void testExecute() {
final Controller controller = new Controller();
// Expections は呼ばれる順番もチェックするぞ!
new Expectations(controller) {
Singleton singleton;
{
// controller.fetchData() のパーシャルモック
// このメソッドだけ挙動変更とかができる。
controller.fetchData();
result = "AA";
// Singleton のモック
Singleton.getInstance();
result = singleton;
singleton.foo();
result = "BB";
// Util のモック
Util.bar();
result = "CC";
}
};
assertThat(controller.execute(), is("AABBCC"));
}
}
そして、実行。
ハハハ。美しい Test 成功のグリーンが輝いておる。

やったね!これで、テストを足掛かりに少しは前進することができるかなー。とか考えたわけです。
あと、モックを作るのは楽しいです!
JMockit は、カバレッジ計測を行ったりもできるらしいので、リア充な Mocking Framework だと思います。リア充乙。
ちなみに、これなんて読むんですか。
ジェイモキットとか読んでるんですけど。 JMock + Mockito で、ジェイモヒートとかなんですかね?
今年も PHPMatsuri に参加してきてしまいました。
去年は学生+ハッカソン初参加ということで、こんなにも高いお金を払っていいものか…。
ということでビクビクしながら参加ボタンを押したのを覚えていますが、
今年は、昨年の慣れもあり全力で参加ボタンを押しました。
今年も、Symfony2 周りの情報を漁りに言った。
というのも、個人としてもお仕事としても Symfony2 は使ってみたいフレームワークだったからだ。
なんと、 Fabien が来たので幾つか質問してみた。
// 僕の英語ほとんど通じませんでしたorz
1. Symfony Framework 上で使える Twig タグ(等) のドキュメントはあるか?
ない。とのことだった。チケット作るか、作っておくれ。という解答といただく。
僕がこのリストを欲しいと思った経緯は、Twig として利用できるタグ一覧は、Twig Document で見ることができるが、Symfony Framework の Bridge が追加しているタグについては一覧で見ることができないなーと思ったことが発端にある。
今のところ、全手動でリストを作ったり、Bridge のコードを見て確認していた。これは、自動化されていないし非効率。
Twig タグ一覧用の Bundle をつくろうかなー。っと思ってた矢先、Fabien はものの数時間でやってのけた。
イケメンすぎるぜ…。
こういう、質問して新たな方向性が生まれていくと、質問してすごく良かったって思いますね。
2. Sismo って version=0.99999 とかなっているけど、 1ってあるの。
なんか、version=1はありえんとか言ってたw
永遠のベータ版ですね。わかります。
でも、バージョン番号が銀河鉄道みたいな感じになっているのは面白い。
このイベントの醍醐味はなんといっても、ごった煮。ハッチポッチである。
本当にいろんな働き方をしている人がいるし、それに触れることができる。
そして、なんだかよくわからないけどトンデモなく楽しい。
今年は、こいつ↓とGarrett先生にだいぶ毒されていた可能性もあるけど。
VOYAGE GROUP の中の人がやっていた、
「今日もまた〇〇な環境で仕事をはじめるお 〜エンジニア理想の職場と私の職場〜」
という、トークでは思わず良い環境だなーーー転職したいなーーとか多少なりとも思ったけど、
たぶん、そういって転職しても絶対に味わえない楽しさが PHPMatsuri にはあると思う。
今年は、去年よりしっかりした成果物を出すことができた。
PHP Conference LT では Sismo の紹介に限ったが、今回は Hack してみようと思った。
Sismo が Symfony Component を利用しているということで、Symfony利用でLT。
次点ということでTシャツなどを頂いた!ありがとうございます。
今後は、svn対応とかもっと闇に満ちた Hack をしたいと思います。
あと、闇PHPMatsuriにも参加しました。
ドラ忍者の登場には驚きました。
PHPのマニュアルにある謎関数は、闇に満ちてます。
というわけで、最終LTはなんと40人。
参加者の大体半分が何らかの発表をしたことになります。
なかなかこういうアグレッシブで大きくてごった煮なイベントはないんじゃないでしょうか。
こんな大好きなイベントに、来年もあれば出たい。
そして、スタッフ・ゲストの皆さんと、特に僕の厄介な質問に答えてくれた Fabien と、全力でイベントを盛り上げていた参加者の皆さんに感謝したい。
このブログが、ワークショップのレビュー場になりつつある大原部長です。
(ほんとうは、もっと翻訳とかオープンソースネタを晒したいんだお。
とかいうと、どうでもいいことはポストしてんじゃねーか。と言われそうなのでオフレコで)
昨日、 TDD BC Tokyo 1.6 が開催されました。
和田さんの素晴らしい講演や、Java初心者 (3ヶ月程度) で 挑んだペアプロなどなど
非常に熱いものでした。

毎回、このようなイベントでは全体でKPTを行っていますが、個人的にもKPTをやろうと思ったわけです。
最近は、自分自身のOJTでもKPTが十分ハナシのネタになるので、KPTはわかりやすく素晴らしいフレームワークだと思います。
タイムスープハンターとは、NHKで放送されている歴史番組 (?) のことです。
未来のタイムスクープ社が、タイムワープ技術を使い、あらゆる時代にジャーナリストを派遣して
人々の営みを映像で記録していくという背景。
NHKは(おそらく)未来人との交流があるので、現代人の我々が其の映像を見ることができるわけです。
http://www.nhk.or.jp/timescoop/index.html
で、取材が始まるときに、ジャーナリストの沢嶋雄一が必ず言う言葉が
アブソリュートポジションNxxx Wxxx Exxx Sxxx。ポジション確認。
アブソリュートタイムXxxxxxxxx年 xx時xx分xx秒。西暦変換しますと xxxx年x月x日xx時xx分。無事タイムワープに成功しました。
という確認の文言。
アブソリュートタイムというのがとても気になったので、シーズン3から研究してみました。
が、挫折した。
これが問題の研究資料です。
なんと、このアブソリュートタイムというのは、我々が想定する時間の進み方とは全く異なることが判明しました。
注目すべきは「うわなり打ち」の回は、西暦だと1638年で、アブソリュートタイムがB0345728年なのに、
「髪結い」の回は、西暦だと1821年で、「うわなり打ち」の回よりアブソリュートタイムが大きくなると思いきや
B0287394年となっています。
しかも、プレフィックスが “S” になっている時も…。
なんてこった。未来の技術はわからない。
いくつか仮説
何をするために、この研究を始めたかというと、アブソリュートタイムを西暦に変換する
プログラムをつくろうと思ったのですが、これではいかんですなー。
アブソリュートポジションも謎だ。
こんにちは。 先日 6月4日に、Symfony2勉強会が開催されました。
実は、 Symfony2 勉強会への参加はこれが初めてだったりします。
第2部から参加しました。
今、 Symfony2 で Bundle (いつかオープンソース) をつくっておるので、
「あーこうすりゃいいのか」っていうヒントが多く散りばめられていて良かったです。
あと、スタッフのTシャツがオサレですね。
講演のテーマは
ということでした。個別にそれぞれ、感想を書くのもいいですが、
FormComponent を復習したかったので、手を動かしてみました。
以下が発表スライド
http://www.slideshare.net/fivestar/symfony2-8203873
この発表の質問の中に、i18n まわりの事が出ていました。
僕も、大いに気になってしまったので、考えてみました。
Symfony2 の i18n は TranslationComponent という、FormComponent と ValidatorComponent
と分かれているように、これまた別のコンポーネントで管理されています。
となると、何か特別な操作が必要なのでは、と疑うわけです。
結論から言うと、 Label や Error は、 メッセージさえ用意しておけば、 symfony1.x と同じように
勝手に翻訳してくれます。嬉しい\(^o^)/
TranslationComponentの設定や、メッセージファイルの形式・置き方は、
http://symfony.com/doc/2.0/book/translation.html
の通りです。
symfony-standard に入っている、 DemoBundle で試してみます。
まず、fallbackを日本語にします。 (ロケールが明示されていないときの設定)
app/config/config_dev.yml
framework:
router: { resource: "%kernel.root_dir%/config/routing_dev.yml" }
profiler: { only_exceptions: false }
translator: { fallback: ja } # ← 追加
以下のような src/Acme/DemoBundle/Resources/translations/messages.ja.yml を作ります。
(YAMLじゃなくても、symfony1.x でも使われていた、XLIFF 形式も受け付けるよ!
しかし、ネストされたIDを用意できるのは Symfony2の新機能で、YAML (or PHP) 形式だけの強みのようです。
あと、個人的にYAMLが好き。)
Resources/translations/messages.ja.yml
Email: Eメール
Message: メッセージ
http://example.com/app_dev.php/demo/contact にアクセス
見事、日本語になりました。これで、多言語対応への不安が1つ減りましたね。
仕組みもちょっと調べてみました。
Symfony2 では、 Twig をテンプレートエンジンとして利用している場合、 TwigBundle が TranslationComponent
への導線を用意しているようです。
( PhpEngine の場合は、 FrameworkBundle が導線を用意しているようです。)
TwigBundle/Resources/views/Form/div_layout.html.twig の一部
{% block field_label %}
{% spaceless %}
{% endspaceless %}
{% endblock field_label %}
のように、しっかり trans filter を使っているのがわかります。
—
あと、Doctrine2 ORM のドキュメント翻訳やりたいと思ってます。
6/30 に Doctrine 2.1 がリリースされる予定とのことなので、正座して待たずに、
情報を掴んでいきたいところですね。
—
次回は懇親会とか行きたいお。
Symfony2 で遊んでいます。
で、Doctrine2.0 でモデルを定義するときに少し困ったことが。
以下のようなモデルを想定しました。
User (id, ..)
Cart (user_id, …)
id, user_id はそれぞれ主キーで、 user_id は 外部キーです。
User は Cart を 1つしか持てないという想定で、1対1の関係です。
アノーテーションを使ってモデルを定義すると、
User側
/** * @orm:Id * @orm:Column (type="integer") * @orm:GeneratedValue (strategy="AUTO") */ protected $id;
Cart側
/** * @orm:Id * @orm:OneToOne (targetEntity="User") * @orm:JoinColumn (name="id", referencedColumnName="id") */ protected $user;
とかなるかなー、っておもって
doctrine:schema:create をやってみたんですが、Cart に主キーがないんだぜ
と怒られてしまいました。
よくよく、ドキュメントを良く読むと衝撃的なあれが。
http://www.doctrine-project.org/docs/orm/2.0/en/tutorials/composite-primary-keys.html
Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases. For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as primary keys are supported.
間違ってるかもしれない、ほんわか翻訳
Doctrine2は最初から複合主キーに対応しています。複合主キーはとても強力なリレーショナルデータベースの概念であり、私たちは念を入れてさまざまなユースケースの複合主キーに対応したいと思います。Doctrine 2.0 ではプリミティブ型の複合主キーに対応され、2.1ではさらに外部キーを主キーとすることにも対応されています。
うほー。ってことは Doctrine2.1 を乗せないと、上のようなモデルを定義することはできないてことかー?