OSC2015北海道で「phpspecで学ぶLondon School TDD」という発表をしてきました

見た目を重視してこんなタイトルにしてみましたが、基本的には私が「実践テスト駆動開発」と「phpspec」をどう解釈したか、というような内容になっています。

45分あってもなかなか伝えるのが難しいテーマだったのですが、とりあえず時間厳守はできてよかったです(一部デモを飛ばしてしまいましたが)。

www.slideshare.net

いくつか補足を。

伝えたかったこと(あるいは上手く伝えられなかったかもしれないこと)

  • 外から中へ、あるいは依存関係の上から下へ実装することで、上位が下位に要求するものが明確になる。
  • setFoo()のような、プライベート変数をセットするだけのメソッドであっても、それは外から観測できる何かに影響を及ぼすはず(影響を及ぼさないのであればそのメソッド自体不要)。その外から観測できるものをモックを使ってテストできるれば、テストのためだけにgetFoo()を追加する必要は無くなる。

QAより

補完について

Specの書き方が特殊なため、Spec内ではIDEの補完がほとんど効きません。調べてみたところ、部分的には@mixinアノテーションを使って補完させることができるようです。

f:id:iakio:20150614155113p:plain

PhpStorm & phpspec | Dimitrios Savvopoulos

しかし、この先のshouldReturn()などは補完することができません。

タイプヒントについて

身内でリハをやったときに、「仮実装を作るときに何故タイプヒントを付けてくれないのか」という質問がありました。

    // このような仮実装を生成するが
    public function toHtmlFromReader($argument1)
    {
        return '<p>Hi, there</p>';
    }

    // なぜこのようにしてくれないのか
    public function toHtmlFromReader(Reader $argument1)
    {
        return '<p>Hi, there</p>';
    }

これについては議論があったようですが結果的にphpspec自体ではRejectされ、extentionで実装されています。

雑感

私は主にLaravel方面をWatchしていますが、ここ最近のPHPフレームワークは、Composer、PSR、PimpleライクなDIコンテナの発明を経て、HTTPからのアプリケーションの分離みたいな方向に向かっているように思います。Laravel方面ではDDD、CommandBus、CQRS、Event Sourcingといったキーワードが注目されているようです。

そしてこれは、Rails likeなアーキテクチャからの決別でもあります(Railsがダメだというのではなく、PHPRailsの真似をするのは無理があるという意味です)。

このような方向性に、phpspecはマッチしているのではないかなという気がしています。

PostgreSQLで「1時間以内に解けなければプログラマ失格となってしまう5つの問題」の5問目を解いてみた

久しぶりにこういうのやってみました。良い問題。

1時間以内に解けなければプログラマ失格となってしまう5つの問題が話題に

create or replace function eval(expr text, out ret integer) as
$$begin execute 'select ' || expr into ret; end;$$ language plpgsql;

with
    op (c) as (
       select unnest(ARRAY[ ' + ', ' - ', '' ])
    ),
    a (c) as (
        select 1 || op1.c
            || 2 || op2.c
            || 3 || op3.c
            || 4 || op4.c
            || 5 || op5.c
            || 6 || op6.c
            || 7 || op7.c
            || 8 || op8.c
            || 9
          from op as op1
             , op as op2
             , op as op3
             , op as op4
             , op as op5
             , op as op6
             , op as op7
             , op as op8
    )
select c
  from a
 where eval(c) = 100 
 order by 1
;

巨瀬亮一さんの「勝ち自体にはそんなにこだわっていなかった」の意味について

巨瀬さんは将棋電王戦トーナメント(電王戦に出場するソフトを決める予選)の優勝者インタビューでも同様の発言をしている。

第2回 将棋電王トーナメント 決勝トーナメント最終日 決勝・準決勝 - 2014/11/03 11:00開始 - ニコニコ生放送

7:17あたりより

電王戦出場にあたっては、棋士の先生には申し訳ないですが、 そこまで勝負自体にはこだわりはなくて、 コンピューターを使っていかにプロ棋士が強くなって、 プロ将棋のレベルが上がっていくことに貢献できるかということに 私の場合はモチベーションがあるというか、

コンピューターと人間両方強くなっていくというのが、 今後の将棋界のありかたとしては一番良いのではないかと思っております。

将棋のプロを目指し、挫折し、コンピューター将棋の面白さに出会い、電王戦を迎えた巨瀬さんの心境はどのようなものだったか、ちょっと本人以外には想像できないものではないかという気がする。

電王戦がエンターテイメントとして盛り上がっていることは良いことだけれど、すこし煽り過ぎなのではないかと思う。棋士が負ければ「引退しろ」といわれたり、ソフトが負ければ「もう二度と将棋界に関わるな」といわれなければいけないほどのものなのだろうか。

一方で、コンピューターから見た将棋と人間から見た将棋の違いから、改めて将棋というのはどういうゲームなのかということに気づかされることもあって、そこが電王戦の面白さだ。

もう少し冷静にコンピューター将棋と人間との関係を楽しめるようになればいいなと思う。

www.asahi.com

nlab.itmedia.co.jp

書初め

<?php
$dt = new DateTime("2014-12-27");
$aDay = DateInterval::createFromDateString("1 day");

for ($i = 0; $i < 7; $i++) {
    echo $dt->format("Y-m-d, o W D"), PHP_EOL;
    $dt->add($aDay);
}
$ php year.php
2014-12-27, 2014 52 Sat
2014-12-28, 2014 52 Sun
2014-12-29, 2015 01 Mon
2014-12-30, 2015 01 Tue
2014-12-31, 2015 01 Wed
2015-01-01, 2015 01 Thu
2015-01-02, 2015 01 Fri

Rebuild: 73: It Is 2015 Already (N, naan)

2014年はこんな年でした

  • 初めてTOEICを受験した
  • 将棋ウォーズを始めた。今10切れ4級。これ以上あがりそうにない
  • 初めてコンサドーレの試合を見に行った
  • 個人的にtravisや、herokuで遊んでみた

気になったこと

phpspec、behat

phpspecには本当に感銘をうけた。他のxUnit Frameworkとはかなり考え方が違うので合う合わないはあると思うけど、 「単純で小さな部品を組み合わせてソフトウェアを作る」という発想は自分の理想に近いなと思った。

今のところQiitaにphpspecについて書いているのは私だけのようだけど。

TypeScript

「あれなんかわかんねーな、ここだけJavaScriptでいーや」みたいな撤退がしやすいところがTypeScriptの良いところだと思う。 変数は型を持たないけど型アノテーションは付けられるよというのが流行りつつあるようだ。 phpではタイプヒント実行時に見てDIとかMockを生成するみたいなのが流行っているけれども、 TypeScriptでも型情報を何か別の用途に使えないかなと思っている。

Brackets

ここ数年、デザイナーとプログラマーがどうやって一緒に仕事をしていくかみたいな話がよくあるけれど、 Extract for Bracketsがその間をいい具合に埋めるツールにならないかなと思っている。 あと、TypeScriptでコード書くときも結構使える。

もっと前はこんな年でした

そもそも本当に個人情報をデータベースに保存していいのか

Kazuho's Weblog: Heartbleed脆弱性と、その背後にあるWebアプリケーションアーキテクチャの一般的欠陥について

今年最も気になった記事です。

にもかかわらず、ウェブ関連のソフトウェアにおいては同原則を用いずに、安全性の根拠をプログラムにバグがない点に求めるという悪しき慣習が続いています。特に、機能別の権限分離はまだしも、アクセスユーザー別の権限分離については系統だった実施例が非常に少ないという印象をもっています。

たとえば、SQL Injectionに代表されるSQL関連の情報漏洩も、アクセス制御にRDBMSのアクセス制御機構を用いず、アプリケーションプログラム内のSQL(とそのエスケープ)が正しく記述されている点に、安全性の根拠を求めているが故に発生しているわけです注1。

ここでDJBの名前を出したのは、ずっと前から気になっていたこの記事を思い出したからです(もしかしたら若い人はDJBとかqmailとか知らないかもしれないけど)。

セキュリティに関するいくつかの考察 - qmail 1.0 から十年 (Some thoughts on security after ten years of qmail 1.0)

この中でDJBは、セキュリティホールを無くするための取り組みの一つとして、「信頼されたコードをなくす」というのを挙げています。

「信頼されていない」というのは、 これらの檻の中のコードは — 何をしようと、どんなに悪いふるまいをしようと、 どれほど多くのバグがあろうと — ユーザのセキュリティ要求を 侵犯できないという意味である。

一般的なWebアプリケーションでは、全てが「信頼されたコード」として作られているように思います。

SQL Injectionされないように実装することは当然重要ですが、 SQL Injectionが可能になったくらいですべてのデータが丸見えになってしまうようなアーキテクチャというのは、 Webサーバーをroot権限で動かしているのと同じような危険を冒しているのかもしれません。

例えば原始的なWikiのように、誰でも同じデータを参照できて編集できるようなWebアプリケーションであればこれで問題はありませんが、 現在の多くのWebアプリケーションは、そうではなくなってきています。 にもかかわらず、アーキテクチャは変わっていません。

ではどうするべきなのか。今すぐには解決策は思いつきません。 データベース側で何かをすることが難しければ、レイヤを一つあげて、ORMあたりが常にユーザーの権限を意識するような作りは可能かもしれません。あと詳しく見てませんが最近Googleに買収されたFirebaseなんかは面白そうだなと思っています。

StackPHPを使ってみる(Middlewareを作る)

自分でMiddlewareを作る

ひな形はこんな感じ。

<?php
// 何もしないミドルウェア
class MyMiddleware implements HttpKernelInterface {
    private $app;
    public function __construct(HttpKernelInterface $app, array $options = []) {
        $this->app = $app;
    }

    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        return $this->app->handle($request, $type, $catch);
    }  
}

テンプレートエンジンをMiddlewareとして実装してみる

Mustacheを使ってみる。一番単純に実装するとこんな感じ。

<?php
class Mustache implements HttpKernelInterface {
    private $app;
    private $engine;
    public function __construct(HttpKernelInterface $app, array $options = []) {
        $this->app = $app;
        $this->engine = new Mustache_Engine($options);
    }

    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $request->attributes->set('mustache', $this->engine);
        return $this->app->handle($request, $type, $catch);
    }  
}

使う側はこんな感じ。

<?php
$app = new CallableHttpKernel(function (Request $request) {
    $token = $request->attributes->get('oauth.token');
    if (!$token) {
        return new RedirectResponse('/auth');
    }

    $params = $token->getExtraParams();
    return new Response($request->attributes->get('mustache')->render('Hello, {{ name }}', ['name' => $params['screen_name']]));
});

// ...

$stack = (new Stack\Builder())
    ->push('Stack\\Session')
// ...
    ->push('Mustache');

returnがかっこ悪いのでヘルパーメソッドにするとかTraitを使うのが良いかもしれない。その前にそろそろCallableHttpKernelから卒業しよう。

<?php
class MyApplication implements HttpKernelInterface {
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $token = $request->attributes->get('oauth.token');
        if (!$token) {
            return new RedirectResponse('/auth');
        }

        $params = $token->getExtraParams();
        return new Response($request->attributes->get('mustache')->render('Hello, {{ name }}', ['name' => $params['screen_name']]));
    }
}

さてrenderメソッドを実装してみようと思ったんだけど、これこうなっちゃうな。

    public function render($template, $params, $request)
    {
        return new Response($request->attributes->get('mustache')->render($template, $params));
    }
// 又は
    public function render($template, $params, $engine)
    {
        return new Response($engine->render($template, $params));
    }

MiddlewareはRequestを受け取ってResponseを返すだけなので、Middlewareからアプリケーションに何かを渡すには$request->attributes経由ということになるんだけど、そうするとrenderメソッドの引数が1つ増えることになってイマイチ。 この辺がStackPHPを使う上でのキモかもしれない。

あるいはこう書けるようにした方がいいか。

        return $request->attributes->get('mustache')->render('Hello, {{ name }}', ['name' => $params['screen_name']]);

Mustache_Engine#render()は文字列を返すけどHttpKernelInterface#handle()はResponseを返さなければならないので、単にMustache_Engineのインスタンスを返すのではなく、render()メソッドの生えた何かを返すようにしよう。本来は別のクラスを作った方が良さそうだけど単純にMiddlewareにrenderメソッドを実装してしまおう。

<?php
class Mustache implements HttpKernelInterface {
// ...
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $request->attributes->set('mustache', $this);
        $request->attributes->set('mustache.engine', $this->engine);
        return $this->app->handle($request, $type, $catch);
    }

    public function render($template, $params)
    {
        return new Response($this->engine->render($template, $params));
    }
}

あと、renderの引数はファイル名にしよう。

$stack = (new Stack\Builder())
    ->push('Stack\\Session')
// ...
    ->push('Mustache', [
        'loader' => new Mustache_Loader_FilesystemLoader(__DIR__ . '/views'),
    ]);

ここでMustache_Loader_FilesystemLoaderをnewするのは好きじゃない人もいるかもしれない。stack-sessionなんかは内部にPimpleのインスタンスを持っていて実行時に評価されるようにしているようだ。

テンプレートを作る。Mustache_Loader_FilesystemLoaderのデフォルトでは拡張子".mustache"を要求する。

<!-- views/index.html.mustache -->
<h1>Hello, {{ name }}</h1>

使う方はこうしておこう。

        $mustache = $request->attributes->get('mustache');
        return $mustache->render('index.html', ['name' => $params['screen_name']]);

https://github.com/iakio/stackphp-example