書初め

<?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

StackPHPを使ってみる(Twitter認証)

StackPHPというのが正式名称なのかわからないけど。

http://stackphp.com/

ひな形

  1. HttpKernelInterfaceを実装した$appを準備する。
  2. スタックを作る。
  3. $stack->resolve($app);
  4. $appを実行する
<?php
$app = ...;  // 1.
$stack = (new Stack\Builder())  // 2.
    ->push(...)
    ->push(...);

$app = $stack->resolve($app);   // 3.

// 4.
$request = Request::createFromGlobals();
$response = $app->handle($request)->send();
$app->terminate($request, $response);

Hello, world

1のHttpKernelInterfaceを実装は、SymfonyやLaravelのアプリケーションでも良いのだけれど、 より簡単に作るためにstack/callable-http-kernelというのが用意されている。

また、4の部分を簡単に書くために、stack/runというのが用意されているので一緒にインストールしておく。

$ php composer.phar require stack/builder stack/callable-http-kernel stack/run

ちなみに最近、composer requireでバージョンを指定しなくても良くなったらしい。

<?php
// index.php
require "vendor/autoload.php";

use Stack\CallableHttpKernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

$app = new CallableHttpKernel(function (Request $request) {
    return new Response("Hello, world");
});

$stack = new Stack\Builder();

Stack\run($stack->resolve($app));

何もstackにpushしてないけどとりあえずこれで動く。

$ php -S localhost:9000

Twitter認証してみる

stack/sessionとigorw/stack-oauthをインストールする。

$ php composer.phar require stack/session:dev-master igorw/stack-oauth:dev-master
<?php
require "vendor/autoload.php";

use Stack\CallableHttpKernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
$app = new CallableHttpKernel(function (Request $request) {
    $token = $request->attributes->get('oauth.token');
    if (!$token) {
        return new RedirectResponse('/auth');
    }

    $params = $token->getExtraParams();
    return new Response("Hello, " . $params['screen_name']);
});

$stack = (new Stack\Builder())
    ->push('Stack\\Session')
    ->push('Igorw\\Stack\\OAuth', [
        'key' => getenv('OAUTH_KEY'),
        'secret' => getenv('OAUTH_SECRET'),
        'callback_url' => 'http://localhost:9000/auth/verify',
        'success_url' => '/',
        'failure_url' => '/auth',
    ]);

Stack\run($stack->resolve($app));

一応動くけど多分このigorw/stack-oauthバグってるな。

続くかも。

Introducing Modelling by Example - BDDの新しいアプローチ

@everzet氏のブログ記事より。例によって翻訳は無理なので気になったところだけ要約。

http://everzet.com/post/99045129766/introducing-modelling-by-example

  • Cucumber、Behat、SpecFlowなどのGherkin-based BDD toolのシナリオをユビキタス言語で 書くといいんじゃない?

つまり、

Scenario: Showing delivery cost for a product on the basket page
  Given there is a product:
    | name  | White Marker |
    | price | £5           |
  And I am on the "/catalogue" page
  When I click "Buy" in the "White Marker" product block
  And I go to the "/basket" page
  Then I should see a list with 1 product
  And the overall price should be shown as £9

ではなく、

Scenario: Getting the delivery cost for a single product under £10
  Given a product named "White Marker" and priced £5 was added to the catalogue
  When I add the "White Marker" product from the catalogue to the picked up basket
  Then the overall basket price should be £9

こう書く。

  • そしてUIやインフラストラクチャー層を除く、ドメインモデルだけのstep definitionから実装を始める。 一部のシナリオをピックアップして、同じフィーチャーに対してエンドツーエンド用のstep definitionを書く。 つまり、1つのシナリオに対して2種類のstep definitionを書くことになる (このフィーチャーに対してはこのstep definitionを使ってね、ということを定義する"suites"という 機能をBehat v3に実装した。CucumberやSpecFlowを使っている人は中の人におねだりしよう)。

  • こうすることで、インフラストラクチャ層がコアドメインをするのを素早く発見できるようになる。

  • BDDとDDDは共にTranslation Costを排除しようとしているがレイヤーが異なっている。 BDDは対話、DDDはコードにフォーカスしている。二度翻訳するのは無駄だし間違うかもしれないよね。

  • インフラストラクチャー層はシナリオをパスするための手段だ。 全てのアプリケーションがMySQLへの接続を必要としているからではなく、 永続化レイヤーが欠けていることによってシナリオがパスしなくなったから永続化レイヤーをアプリケーションに追加するんだ

"Design How Your Objects Talk Through Mocking"を見た

Konstantin Kudryashov - Design How Your Objects Talk Through Mocking at Laracon EU 2014 - YouTube

BehatやPhpSpecの作者である@everzetによるLaracon EU 2014での講演です。 英語が苦手なので翻訳することはできませんが印象的かつ聞き取れた部分を要約して紹介します。


  • モックはデータベース接続等の遅い部分をテストから分離して速くするためのものだと思われているが、それは誤解だ(そのような目的ではFake Implementationを使う)。

  • モックは、結果や状態に注目したクラシカルなTDDでは隠されていたオブジェクト間のメッセージングをさらけだし、設計の問題を明らかにするデザインツール

  • メッセージングではなく、結果をさらけ出そうとするのなら、コードにさらにgetterを追加することを強いられるだろう

  • 存在しないメソッド、インタフェース、クラスをモックしたことを知らせてくれないような壊れたMocking Frameworkを使わない

  • 自分が所有していないオブジェクトをモックしない

  • (QAより)

    Q:例えばS3のようなサードパーティー製のAPIを使う場合はどうすべきか。

    A:インターフェースを作り、それを実装したS3と通信するクラスを作る。そのクラスのインテグレーションテストを書き、インターフェースをモックする。

    インテグレーションテストはPHPUnitや他のテスティングフレームワークで書き、実際にS3にファイルが保存されるかをテストする。


感想。

サンプルコードはPHPUnit+Prophecyという構成で書かれています。説明のためにより広く知られている記法を選んだのではないかと思いますが、PhpSpecで書けばより簡潔なコードになるでしょう。

Mockを使った開発の問題として、Mockと実装が食い違ってしまうという点が挙げられる場合があります。しかしそれはMocking Frameworkがチェックすべきだと@everzetは指摘しています。多くのMocking Frameworkでは存在しないクラスやメソッドをMockすることができますが、PhpSpec/Prophecyではエラーとなります。

個人的な経験としては、PhpSpecを学ぶことで、Mock、システム境界、Hexagonal Architecture が一つの線で結ばれ、The GOOS bookに書かれていることが初めて理解できたように思います。

最初にThe GOOS Bookを読んだときは、「モックするのは自分の持っている型だけ」の意味が理解できなかったのですが、PhpSpecでコードを書いてみると、そもそもモックを書くのは次に実装しようとしているコラボレーターの場合が多く、モックは設計なんだ、既に存在しているものをモックする意味は無いんだということが自然に理解できたように思います。

The GOOS Bookって良い本だけどちょっと難しいので、同じようなテーマでPhpSpecで書かれたものがあればいいなあ。

実践テスト駆動開発 (Object Oriented SELECTION)

実践テスト駆動開発 (Object Oriented SELECTION)