コンピュータやソフトウェアのあれこれ@道民(&元道民)
PHP
[PHP]PHPUnitのMockが生成するコード
5月 2nd
PHPUnitのMockはコードを生成してeval()しているわけだけれども、PHPUnit_Framework_TestCase#getMockBuilder()の引数に存在しないクラス名を渡すと、そのクラスを生成するコードも出力する。
どんなコードを生成しているかはこのように確認できる。
<?php require_once('Text/Template.php'); require_once('PHPUnit/Framework/MockObject/Generator.php'); $mock = PHPUnit_Framework_MockObject_Generator::generate('Foo'); echo $mock['code'];
実行結果:
class Foo
{
}
class Mock_Foo_ce23079e extends Foo implements PHPUnit_Framework_MockObject_MockObject
{
protected static $staticInvocationMocker;
protected $invocationMocker;
public function __clone()
{
$this->invocationMocker = clone $this->__phpunit_getInvocationMocker();
}
public function expects(PHPUnit_Framework_MockObject_Matcher_Invocation $matcher)
{
return $this->__phpunit_getInvocationMocker()->expects($matcher);
}
...
と、Fooを作ってからそのサブクラスを作って、そのオブジェクトをMockとして返している。
何でこんなことを調べたのかというと、Fooが存在しているつもりでモックを作ったのだが実は存在していなかった(requireされていなかった)、で、本当はFooのコンストラクタには引数が必要だったのだがそれを忘れていた。だがここでのFooはMockObject_Generatorが作った空のFooなので当然エラーにはならない。単体では。
しかしこれを他のテストケースと一緒に実行すると、別のところでFooがrequireされていたためにエラーが発覚する。というはまり方をしたためです。
[PHP]Symfony2/SilexのControllerResolverを読む2
4月 1st
ところで、Silex\ControllerResolverにはこんなコードがあるんですがこれは何をやってるんでしょうねえ。
protected function doGetArguments(Request $request, $controller, array $parameters)
{
foreach ($parameters as $param) {
if ($param->getClass() && $param->getClass()->isInstance($this->app)) {
$request->attributes->set($param->getName(), $this->app);
break;
}
}
return parent::doGetArguments($request, $controller, $parameters);
}
引数の$requestは当然リクエスト、$controllerは通常Silexでは無名関数、$parametersはReflectionFunction#getParameters()の戻り値で、ReflectionParameterの配列です。
http://jp2.php.net/manual/ja/reflectionfunctionabstract.getparameters.php
$param->getClass()->isInstance($this->app)
の部分ですが、ReflectionParameter#getClass()はReflectionClassのインスタンスを返すので、ReflectionClass#isInstance()が呼ばれています。これはこう書き換えることができます。
is_a($this->app, $param->getClass()->getName())
つまり、コントローラの仮引数がApplicationクラスであれば、$request->attributesにセットしている。で、最後にparent::doGetArguments()を呼び出しているということはもしかして、、、
コントローラの仮引数に"Application $app"って書いておけば勝手に渡ってくるってこと!?
つまり実は前回のコードはこれでいいってことか。
<?php require_once __DIR__.'/../vendor/.composer/autoload.php'; use Silex\Application; class LeapYearController { function indexAction(Application $app, $year) { //... } } $app = new Application(); $app->get('/is_leap_year/{year}', 'LeapYearController::indexAction'); return $app;
あーこれなら書いてみる気になりますね。よくできてるなーって関心しかけたけどいやいやいやいやちょっと待って。ってことは
$app->get('/hello/{name}', function($name) use($app) {
return 'Hello '.$app->escape($name);
});
じゃなくて
$app->get('/hello/{name}', function(Application $app, $name) {
return 'Hello '.$app->escape($name);
});
でも良かったってことじゃん!!
まだ続くかも。
[php]Symfony2のControllerResolverを読む
3月 25th
"Create your own framework... on top of the Symfony2 Components"を読んで、Symfony2とSilexに興味がわいてきたので色々実験してみました。とりあえず"Create your own framework..."でやったCalendarをSilexでどう実装するかを考えてみます。
Silexといえば
$app->get('/hello/{name}', function ($name) use ($app) {
return 'Hello '.$app->escape($name);
});
$app->run();
こんな感じのコードというイメージがありますが、中身としてはpart6で出てきたSymfony\Component\HttpKernel\Controller\ControllerResolverの派生クラスであるSilex\ControllerResolverを使っていて、$app->get()の第二引数には無名関数以外のものも渡すことができます。
ではどのようなものを渡すことができるのか、ControllerResolver#getController()を見ると以下のようになっているようです。
1. コールバック配列。つまり array($class_name, $method_name) 又は array($obj, $method_name)
2. __invokeを実装するオブジェクト
3. __invokeを実装するクラスのクラス名
4. 関数名
5. クラス名::メソッド名
無名関数の場合は2.になります。"Create your own framework..."では
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array(
'year' => null,
'_controller' => 'LeapYearController::indexAction',
)));
のようにルートを定義していたので、同様に、Silexでも
$app->get('/hello/{name}', 'LeapYearController::indexAction');
と書くこともできます。しかしこれはあまり嬉しくありません。LeapYearController::indexAction()に$nameを渡すことはできますが、無名関数を使った時とは異なり、サービスロケーターである$appを参照することはできないからです。
(2014/04/01追記:実は簡単にできました Symfony2/SilexのControllerResolverを読む2 - iakioの日記)
ではSymfony2の場合はどうなっているのでしょうか。Symfony2のコントローラーはController#getContainer()でDIコンテナを参照することができます。これは、Symfony2のControllerResolverはSymfony\Bundle\FrameworkBundle\Controller\ControllerResolverであり、そこでControllerResolver#createController()をオーバーライドして、コントローラーをnewした後にController#setContainer()を呼び出しているためです。
Silexでも同様のControllerResolverを作ることも可能です。非常に雑に書くとこんな感じ。
<?php require_once __DIR__.'/../vendor/.composer/autoload.php'; use Silex\Application; use Silex\ControllerResolver; class MyControllerResolver extends ControllerResolver { function __construct($app) { $this->app = $app; } protected function createController($controller) { list ($obj, $method) = parent::createController($controller); $obj->app = $this->app; return array($obj, $method); } } class LeapYearController { function indexAction($year) { //... } } $app = new Application(); $app['resolver'] = $app->share(function () use ($app) { return new MyControllerResolver($app); }); $app->get('/is_leap_year/{year}', 'LeapYearController::indexAction'); return $app;
と、$app['resolver']に独自のControllerResolverのインスタンスを返す無名関数をセットしてやれば良いわけです。
とまあ長々と書きましたがこれはあくまで実験で、実際のところこういうやり方はSilexっぽくは無いと思いますw。ただ、これだけでもSilexやSymfony2がとても柔軟にできていることは実感できました。
続く予定。
[PHP]Create your own framework… on top of the Symfony2 Componentsを読んだ
3月 20th
2ヶ月前にブックマークした”Create your own framework... on top of the Symfony2 Components”を写経しつつやっと読みました。面白かったです。
フレームワークがどのような問題を解決しようとしているか、という話もありますが、それよりSymfonyやSilexをやっている人が理解を深めるのに良いエントリなのではないでしょうか。
個人的に驚いたのはpart 6のControllerResolverのあたりでした。Requestから呼び出すべきコントローラーとその引数を決定するんですが、仮引数の名前とType Hintを使って、いい感じで引数を渡してくれるようになっています。
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array(
'year' => null,
'_controller' => 'LeapYearController::indexAction',
)));
// このようにルートが定義されていればコントローラ側では
...
public function indexAction($year) // 動く
public function indexAction($y) // エラー
public function indexAction(Request $request, $year) // 動く
public function indexAction($year, Request $request) // 動く
public function indexAction(Request $request) {
$year = $r->attributes->get('year'); // こう書けば動く
...
内部ではやはりリフレクションを使っているようですね。
https://github.com/symfony/HttpKernel/blob/master/Controller/ControllerResolver.php
あとComposerは便利ですね。nodeのnpm、RubyのbundlerのPHP版です。今後はスクリプト言語のパッケージ管理はこういう風になっていくんだろうなと思いました。
最後に「もっと知りたければ、Silexの、特にApplicationクラスを読んでみるといいよ」と言われたのでこれから読んでみたいと思います。
php5.4 RC5 を手元の Mac に入れてみた
1月 6th
そろそろ時代が php5.4 になるということで、さすがにちょっと触っておくかと、とりあえずインストールをしてみました。 build-in-server があるので、とりあえずは apache とか無視してホームディレクトリに置きました。
mkdir -p local/src
cd local/src
curl -LO http://downloads.php.net/stas/php-5.4.0RC5.tar.bz2
tar xf php-5.4.0RC5.tar.bz2
cd php-5.4.0RC5
./configure \
--prefix=$HOME/local \
--disable-cgi \
--enable-mbstring \
--without-pear
make && make test && make install
と、この状態で だけ書いた test.php を用意して php -S 0.0.0.0:8080 test.php を実行し、ブラウザで http://127.0.0.1:8080/ にアクセスして phpinfo が表示されたのでとりあえずこれで良いかなあ。いろいろ遊んでみたい。
Debian にも入れようと思って、弊社 CTO のパッケージを使おうと思ったんですが、なんか Apache 系のなにかに依存しているエラーでちゃったので、とりあえず放置。 Apache 入れたほうがいいのかな。
http://php.marvel.strk.jp/deb/
wget -nd -r -A .deb http://php.marvel.strk.jp/deb
dpkg -i *.deb
[Win32][PHP][tdd] Windowsでphpunit –colors
12月 16th
あるいはコマンドプロンプトでANSIカラーを表示させる方法。
Windowsのコマンドプロンプトでphpunit --colorsすると、
こんな感じになります。ゲンナリです。グリーンにならないならテストに対する意欲も半減です。コマンドプロンプトに色を付けるには、SetConsoleTextAttribute()とかいうWin32APIを使う必要があるらしいです。で、調べてたら、
WindowsのコマンドプロンプトでもANSIカラーを使いたい「wac」 - MOONGIFT|オープンソース・ソフトウェア紹介を軸としたITエンジニア、Webデザイナー向けブログ
こんなのを見つけました。これはANSIエスケープシーケンスを読みこんでSetConsoleTextAttribute()を呼び出してくれるもの。なんですが中を見てみたら結構手抜きの実装で、phpunit --colorsの出力では上手くいきません。
なんでこれを直してやろうかと色々試行錯誤していたら、別の方法を発見しました。
パイプでwacじゃなくて単にcatに渡してやればいい。このcatはmsysのcat(\msys\1.0\bin\cat.exe)です。msysGitを使ってればそっち(\Program Files\Git\bin\cat.exe)でも良いと思います。
どこのレイヤでそうなってるのかよくわからないような、当たり前といわれれば当たり前ような解決でした。
[php][tdd]TDD Boot Camp 札幌 2.3に参加してきた
12月 12th
お題はボーリングのスコア計算システム。PHPチームで参加してきました。
ドキュメントを見ようとしたら、PHPUnitのサイトに全然接続できなくてどうしようかと思いましたが、githubでドキュメントのソースのxmlを見ながらどうにか頑張りました。
@shuji_w6eからもお誉めの言葉をいただき、半日Emacsと格闘したかいがありました。
よかったこと
- PHPUnitで試してみたかったことが色々できた
テストメソッド名は日本語で書いてみました。--testdoxで出力する場合、英字の大文字小文字変換を行なうところで若干文字化けがあったため、先頭は英字という命名規則でやりました。testdoxを使わないのであれば問題無いと思います。
@testアノテーションも使ってみましたが、メソッド名にtest付けた方が速いかなという気もしましたw。@expectedException、@dataproviderアノテーション、markTestIncomplete()は普通に便利。assertThat()も使ってみたかったんですがドキュメント見るのしんどくて断念。
- 「1つ上のレイヤーからテスト駆動開発にアプローチする」というテーマを実践できた
- 簡単にテストを実行すること。見やすく書きやすいテストケースを書くことが重要ということを再認識できた
反省点
- 終了直前までボウリングのスコア計算のルールを勘違いしていた。まだまだ実装するつもりだったのに気付いたら機能テストが通っていた
- 機能テストが通った時点で満足したのと若干集中力が切れたのでニコ動とか見てた
他のチームの振り返りを聞いている途中でやっと何故テストが通ったのかきちんと頭が整理できました。振り返りの順番が最初だったら説明できなかったかもしれませんw
ちなみに見ていた動画はこちら
2008年の角谷さんの講演。2/3からボウリングスコア計算のライブコーディング。見所満載です。
その他
- Behatやってみたい
- JavaScriptやってみたい
- pgTAPやってみたい
- SmalltalkとTDDBCの相性は異常
- Perlのテストコードも見てみたい
- 関数型言語のテストコードも見てみたい
- SCMのイベントもできそうじゃない?
他にも色々ありましたが気が向いたら書きます。
ただいま、Ethna
10月 23rd
みなさんお元気ですか。
僕はなんとか生きています。
今日は、久々の Ethna のお話です。
僕が Ethna を触り始めたのは 2004 年末、まだ高校2年生の頃でした。
そして 2007 年ころから rhaco を触り始めたので、それ以来の Ethna です。
つまり、4 年振り 2 回目。
話は変わりまして、この間、 Crocos で開発合宿に行って来ました。
軽井沢に 2 泊 3 日で。軽井沢で所持金 31 円とかたぶん僕だけだったんじゃないかなって思います。
せっかく開発合宿なんで、ちょっと楽しいサービスを妄想しつつ。
Crocos のサービスは基本的にすべて Symfony2 で作られていて、Symfony2 はよくできたフレームワークだなー。とは思ってるんですが、やっぱり開発スピードあがんないよねー。とか、ウェブサービスには向いてないよねー。とかの不満はあるわけです。まあ、お前の頭が悪いからといわれればきっとその通りなんでしょうけどね。
軽井沢まできて Symfony2 も触りたくないし、そうなるともう PHP 界には Ethna しか無いわけですよね。
というわけで、Ethna を使って開発を始めたんですけど、やっぱりテンプレートは Twig 使いたいよねとか、Ethna_UrlHandler とかまじ難しくない?とか、思ってきちゃうんですよね。いやほんと、UrlHandler は謎すぎて未だにわかりません。
ついカッとなった僕は、Ethna_Renderer_Twig と Ethna_UrlHandler_Simple を作りました。
そしてつい先程、Ethna の develop ブランチに Ethna_UrlHandler_Simple が取り込まれましたことをご報告致します。
Twig のほうはちょっとまだフォームヘルパとか半分くらいしか作れてないんで、もう少しかかるかなって感じですが、使ってみたい人は github においてあるので、お試しください。もちろん、残りのフォームヘルパ作ってくれたりしてもいいですよ!ね!!
使い方とか書こうと思ったけどめんどうだからやめます。
簡単に言うと、APPID_UrlHandler で Ethna_UrlHandler_Simple を継承するようにして、action_map の定義をこのへんを参考にして書けば OK です。
おやすみなさい。
第24回北海道開発オフに参加しました
10月 17th
10月15日(土)、第24回北海道開発オフに参加してきました。
最近忙しかったのもあり、こまごまとしたやりたいことがたまっていたのでコツコツ、まったりと消化しました。
午前
午前中は、次回読書会の予習も兼ねて、アジャイルサムライを黙々と読んでました。
開発オフは静かで集中できる環境なので、実は読書にもってこいなのです。
(はじめて参加された方は、会話のなさに驚いたのではないだろうか・・・)
しかし、自宅でもそうなのだけど、この本は大事なことの合間にクスッと笑える表現が挟まっているので、ときどき笑い声を出しそうになってしまいました
ランチ
『Bem Bera network company』のスープカレー。
初めて食べた。あっさりしていて「スパイスたっぷりのカレースープ」という感じ。
しかし薄いとかいうわけでもなく、しっかりとスープカレーであり、大変美味。
フォーが入っているのが独特ですね。
午後
午後からは気合いを入れてプログラミングです。(満腹で若干眠気がアレでしたけど)
今回は、
- お道具箱スペースをつくる
- RSSを解析するプログラムを作る
- TwitterBotのメンテナンス
が目標です。
お道具箱スペースをつくる
bitbucketでgitを使えるようになったので、その環境を作ってみたいというのが動機。
あとは恥ずかしいけど書いた物を見えるところにおいておこうかな、と。
環境は、特に困ることもなくすんなりできました。
ssh環境も設定したのだけど、単純に自分用(ローカルで作った物をpushするだけ)ならssh環境がなくても大丈夫でした。
お道具箱はここ。過疎化しないようにしたいな・・・
RSSを解析するプログラムを作る
これは仕事でFaecbook APIを使用しているのですが、ときどきSDKの不具合が発生するので、プラットフォームの状態を表示しているページのRSSを取得して、わかりやすい場所に通知する仕組みが欲しいなあと思ったのが動機。
ただ、調べていくうちにメール通知してもらえる仕組みが既にあることがわかってしまった・・・。
そこで気持ちを切り替えて、rubyのrssを使って、RSSオブジェクトを扱う方法について勉強しました。
こんな短いコードでRSSがオブジェクトとして取得できるのに感動。
require 'rss'
require 'open-uri'
rss = open('http://www.facebook.com/feeds/api_messages.php'){|file|
RSS::Parser.parse(file.read)
}
xmlタグを外した中身をどうやって取得すれば良いのかに若干手間取りましたが、irb上でなんどかトライしたらさくっとできてしまいました。
rss.items.each{|item|
p 'title: ' + item.title.content
p 'link ' + item.link.href
p 'published ' + item.published.content.strftime("%Y-%m-%d %H:%M:%S")
}
こういうのをちょっとずつ覚えていって、必要となった時にささっと作れるようになりたいな。
TwitterBotのメンテナンス
PHPでつくったTwitterBot達。
短縮URLの扱いがまちまちだったので、整理することにしました。
といっても、URLは短縮しなくてもtwitter側でよしなに扱ってくれるようなので、いったん短縮URLの実装をはずしました。
bit.lyのAPIを使って解析できるようにしようかな、というのが次の課題です。
(中途半端にbit.ly使っていたのもいったん外しています。)
最終的にアウトプットしたソースコードは10行だったのですが、自習癖がなくなっていた自分にはとてもよい刺激となりました。
ありがとうございました。
phpとmysqlをアップデートしました
7月 8th
ServersMan@VPSを使ってるのですが、
WordPress3.2を入れるには外部リポジトリからphpとmysqlをアップデートする必要があって、
これまた不慣れなのでおっかなびっくりやったのですが、なんとかなりました。
まずは、「php serversman」とかでググるといろいろ出てくるのですが、
アップデート後、こんなエラーに遭遇しました。
PHP Warning: PHP Startup: Unable to load dynamic library
'/usr/lib/php/modules/imagick.so'
- /usr/lib/php/modules/imagick.so:
undefined symbol: php_set_error_handling in Unknown on line 0
php-pearがアップデートから漏れてたので、
先にこれをアップデートした後、こんな感じで回避しました。
$ pecl uninstall imagick
$ pecl install imagick
おしまい。


