読者です 読者をやめる 読者になる 読者になる

Shin x Blog

PHPをメインにWebシステムを開発してます。Webシステム開発チームの技術サポートも行っています。

「DDD パターンを活用した Laravel アプリケーション開発」を Laravel Osaka 2016 で発表しました。

2016/10/19 に大阪で開催された Laravel Osaka 2016 にて、「DDD パターンを活用した Laravel アプリケーション開発」を発表しました。

f:id:shin1x1:20161110215814j:plain

会場の MOTEX さん。巨大スクリーンが 2 面あり、話しやすい環境でした。

発表資料

Laravel の具体的なテクニックに比べると抽象的な内容なので、どれだけ伝えられるか思案したのですが、聞いて頂いた方からのフィードバックや参加者アンケートでも概ね良い評価を頂けたので安心しました。

ValueObject については、さらに掘り下げて話せるテーマなので、これ単体でもまた話してみたいです。

いつも blog や資料を参考にさせて頂いている増田さんに tweet して頂きました。インターネットっていいですね :)

質疑応答

発表の後の質疑応答では、多数の質問がありました。質問に対して的確な答えができたかは別にして、その場で今話した内容について意見交換ができるのは楽しいですね。

さらに、イベント終了後の懇親会や、後日、別の場でも発表についての質問や意見があり、そこから議論に発展することもありました。

話したことに対するリアクションが貰えるのは嬉しい限りで、発表者冥利に付きますね。

質問頂いた内容は、ざっと下記のようなものでした。(発表後の質疑応答以外のものも含む)

  • DDD をはじめるには、どこから手を付ければ良いか?
  • ValueObject を多数作った際のパフォーマンスについて
  • デメテルの法則について
  • なぜ、あえて Laravel を使っているのか?
  • リポジトリパターンと Eloquent について
  • 遅延評価クエリをどう組み込むか
  • 例外の使い方

今回の発表内容は、Laravel に関する内容は一部のみで、大筋はどういったフレームワーク、言語でも関連することなので、色々な人が自分なりの解釈で考えやすい題材であったのが一つ理由としてあるでしょう。

また、各技術の紹介や解説の発表に比べ、それらをどういった考え方で、いかに組み合わせるかというものは少ないように思います。

システム開発を行うならば、誰もが日々頭を捻りながら行っている行為なのですが、こういった話が勉強会などで表に出ることはあまり多くありません。

ドメインやコンテキストがそれぞれ異なるので、他人に話しにくいというのはありますが、今回のようにある程度抽象化すれば、みんなで考えられる題材になるので、もっと色々な人がこういった話をすると良いですね。(私はぜひ聞きたいです!)

さいごに

Laravel Osaka 2016 は、おそらく国内での Laravel 単体のイベントとしては最も大きい規模だったと思います。 第 1 回目ということで、関西だけでなく、東京や福岡からスピーカーも参加者も集まっていて、どのセッションも興味深い内容でした。

スタッフのみなさん、楽しいイベントをありがとうございました!

「正規表現再入門」を PHP カンファレンス 2016 で発表してきました

2016/11/03 に開催された PHP カンファレンス 2016 にて、「正規表現再入門」を発表してきました。

資料

speakerdeck.com

togetter.com

内容は、正規表現のマッチングの動き、量指定子のマッチングパターン、バックトラックやパフォーマンスへの影響についてです。

下記のエントリを下地にして、マッチングの動きを分かりやすく伝えることを意識してお話しました。 blog.shin1x1.com

参加された方からは、「分かりやすかった」といった好意的なフィードバックを頂けたので、発表して良かったです。

これから正規表現を学ぶにしても、マッチングがどのように行われるかをざっくりと知っておけば、正規表現を書く際にも動きが想像できるので、この発表がお役に立てると嬉しいです。

偶然、同じ時間帯に発表があった徳丸さんのセッションでも正規表現に触れられていたようで、Twitter の TL が微妙に交錯しているのが面白かったです :)

SQL と似た感覚

下地となったエントリでも書いたのですが、正規表現と SQL はやはり似ているなあと感じます。 懇親会でも、発表を聞いて頂いた方との会話で話題になりました。

宣言的に書く言語は、記述しやすく理解しやすいのですが、どのように実行されるかはブラックボックスになります。 もちろん、それは利点(How ではなく、What に集中できる)なので、実行エンジンが良しなにやってくれる時は全く持って問題ありません。

ただ、今回のバックトラック爆発のように仕組み上発生しうる挙動に関しては、利用側がそれを意識して書く必要があります。

特に、正規表現はバリデーションで外部から来た値をそのまま扱う場面でよく利用されるため、問題があればすぐに影響を受けることになります。

SQL を書く時にデータベースエンジンの動きをざっくりでも意識するように、正規表現でも意識すると良いですね。

さいごに

今年は、午後最後のセッションで、途中にサイン会などもあったので、t_wada さんのセッションだけ参加しました。

このセッションは素晴らしくて、同じように参加した人からも大絶賛でした。 内容はもちろんのこと、構成や説明など伝え方がとても参考になりました。自分の発表にも取り入れられる要素があったので、今後に生かしていきたいです。

懇親会やその後の N 次会でも、色々な方とお話ができて、楽しい時間でした。毎年そうですが、蒲田の PHP カンファレンスは最高ですね!

運営のみなさん、今年も楽しいイベントをありがとうございました。

パフォーマンスを意識して正規表現を書く

正規表現を書く際、どのようなパターンにマッチさせるか、どこをキャプチャするかという視点で記述することはあっても、パフォーマンスを考えて記述するというのはある程度知っている人でなければ忘れがちな視点です。

このエントリでは、バックトラックをメインに正規表現がパフォーマンスに及ぼす挙動について見ていきます。

対象の正規表現エンジン

ここでは、従来型 NFA を対象としています。具体的には、PHP の preg_ 関数で利用している PCRE や mb_ereg 関数が利用している鬼車です。Perl や Ruby、Python、Java、.NET でも従来型 NFA を採用しているので、似た挙動となるでしょう。

「従来型 NFA」や「バックトラック」などの用語については、「詳説 正規表現 第3版」のものを用いています。

バックトラックによるマッチ探査

正規表現エンジンでは、指定された文字列が、パターンにマッチするかどうかを判別する際、記述された正規表現で取り得るマッチパターンが見つかるように何度もマッチングを行います。

例えば、\d+\d という正規表現があり、これに 123 という文字列がマッチするか preg_match 関数でチェックします。

<?php
var_dump(preg_match('/\d+\d/', '123'));

これを実行すると、\d+ の部分は、文字列 123 にマッチします *1。しかし、次の \d にマッチする文字列が無いので、一つ前のパターン(\d+)に戻ります。次は、\d+12 にマッチさせます。次の \d は、3 にマッチするので、これで全体のマッチが成功します。

下記では、この流れを示しています。()\d+ に、{}\d がマッチした箇所で、[]が現在マッチを試みている文字になります。

* (123)[]  <--- `\d+`はマッチするが、`\d`がマッチしないので、`\d+`へ戻る
* (12){3}  <--- `\d+`も`\d`もマッチする

正規表現は、前から順に適用されていくのですが、後続のパターンがマッチしない場合に一つ前のパターンに戻って、別のマッチ方法を試行するのをバックトラック(backtracking)と呼びます。バックトラックは、正規表現に限らず、正しい解を探るためのアルゴリズムです。詳細は、下記で。

https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%83%E3%82%AF%E3%83%88%E3%83%A9%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0

「マッチしない」を確定させるのは遠い道程

次は、マッチが失敗するパターンとして、正規表現 \d+\d[^\d]123 という文字列にマッチさせます。

はじめに \d+ は文字列 123 にマッチします。次の \d にマッチしない(文字列が無い)ので、バックトラックが発生します。次は、\d+は文字列 12 に、\d は文字列 3 にマッチします。最後の [^\d] にマッチしない(文字列が無い)ので、バックトラックが発生します。今度は、\d+ を 文字列 1 に、\d を 文字列 2 にマッチさせますが、[^\d] と 文字列 3 はマッチしません。

次は、文字列の先頭を 1 文字進めて 23 に対してのマッチを試みますが、こちらもマッチには成功しません。さらに、文字列 3 に対するマッチも失敗し、最終的には全てのパターンで失敗します。

下記では、この流れを示しています。()\d+ に、{}\d がマッチした箇所で、[]が現在マッチを試みている文字になります。

* (123)[]    <--- `\d+`はマッチするが、`\d`がマッチしないので、バックトラック
* (12){3}[]  <--- `\d+`と`\d`がマッチするが、`[^\d]`がマッチしないので、バックトラック
* (1){2}[3]  <--- `\d+`と`\d`がマッチするが、`[^\d]`がマッチしないので、バックトラック
* 1(23)[]    <--- 1文字進めてマッチング開始。`\d+`はマッチするが、`\d`がマッチしないので、バックトラック
* 1(2){3}[]  <--- `\d+`と`\d`がマッチするが、`[^\d]`がマッチしないので、バックトラック
* 12(3)[]    <--- 1文字進めてマッチング開始。`\d+`はマッチするが、`\d`がマッチしないので、バックトラック
* 123[]      <--- 1文字進めてマッチング開始。`\d+`がマッチせずに終了

このように、「マッチしない」と結論付けるために、指定した正規表現で取り得る全てのマッチングを行います。この挙動が場合によっては、パフォーマンスに大きな影響を及ぼします。

爆発するマッチング

マッチさせるまで(マッチしないことを確定させるため)、あらゆるマッチングを行うがゆえに正規表現や文字列によっては、マッチングの組み合わせパターンが膨大になる可能性があります。

マッチングが膨大になる様を確認するために、regex101.com というサイトを利用します。このサイトでは、正規表現と文字列を指定すると、正規表現エンジンがどのようにマッチングを行っていくかを表示してくれます。

regex101.com

例えば、上記の例(\d+\d[^\d])であれば、下のような表示となります。これを見ると、文字列 123 に対して、14 steps がかかっていることが分かります。

f:id:shin1x1:20160817160205p:plain

ここで文字列を 123456789 という 9 文字に増やすと、91 steps となります。

マッチングが膨大になる場合を見るために、(\d*\d+)+\d[^\d] という正規表現を利用します。これは、前述の \d+\d[^\d] とマッチする文字列は同じです。このパターンを前述の 9 文字にマッチさせると、なんと 33,805 steps になりました!

https://regex101.com/r/eB0dM5/1

このようにわずか 9 文字の文字列に対するマッチングでも正規表現の書き方によって、マッチングの組み合わせが膨大に膨らむことが分かります。より複雑な正規表現や与えられる文字列が長い場合は、パフォーマンスへの影響が出てきます。

ReDosという攻撃

こうした正規表現エンジンの特性を利用した攻撃が ReDos です。ReDos については、下記の大垣さんのエントリが参考になります。

ReDoSの回避 | yohgaki's blog

特に量指定子(*?+{n,m})を入れ子にしたり、同じ文字クラスを重ねたりすると組み合わせパターンが多くなるので、こうした問題が起こる可能性があります。

末尾にあるスペースにマッチ

上記の ReDos とは別のパターンですが、単純な正規表現でも DoS を引き起こす場合があります。

stackoverflow.com では、文字列前後のスペースを除去する ^[\s\u200c]+|[\s\u200c]+$ という正規表現に対して 20,000 文字のスペースが含まれた文字列が送信されたために、上記のマッチングの組み合わせが膨大になり、34 分間アクセス不能になるという事態がありました。

Stack Exchange Network Status — Outage Postmortem - July 20, 2016

StackExchangeが攻撃されたReDoSの効果 | yohgaki's blog

大垣さんのサイトでも実証されていますが、Stack Overflow の事例を参考に文字列末尾にあるスペースを正規表現でマッチさせて取り除くという処理を見てみます。

まずは、正規表現を単純化して、\s+$ を利用し、文字列は a(半角スペース 3 文字)にマッチさせる場合で動きを見ます。

この場合、下記のようにマッチングを行なわれます。半角スペースが見えるように _ としています。人間が見れば、文字列の末尾が a なので、どのようにマッチングさせても成功しないのは明白なのですが、正規表現エンジンでは、前から順に愚直に評価していきます。

* (___)[a] <--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* (__)[_]a <--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* (_)[_]_a <--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* _(__)[a] <--- 1文字進めてマッチング。`\s+`はマッチするが、`$`がマッチしないのでバックトラック
* _(_)[_]a <--- `\s+`はマッチするが、`$`がマッチしないのでバックトラック
* __(_)a <--- 1文字進めてマッチング。`\s+`はマッチするが、`$`がマッチしないのでバックトラック
* ___[a] <--- 1文字進めてマッチング。`\s+`がマッチしないのでバックトラック
* ___a[] <--- 1文字進めてマッチング。`\s+`がマッチせずに終了

スペースが 3 文字ではなく、20,000 文字となれば、それだけ多くのバックトラックが発生するため、処理に時間がかかるようになります。

パフォーマンスを計測するために下記のコードを実行してみました。それぞれのスペースの最後には a が付いているのでマッチングは失敗します。

<?php
function benchmark($title, callable $target) {
    echo '# ' . $title, PHP_EOL;
    $start = microtime(true);
    $target();
    echo microtime(true) - $start, PHP_EOL;
    echo PHP_EOL;
}

$strings = [];
foreach ([20, 200, 2000, 20000, 200000] as $no) {
    $string = str_repeat(' ', $no) . 'a';
    benchmark('preg_replace' . $no, function () use ($string) {
        return preg_replace('/\s+$/', '', $string);
    });
}

このコードを PHP 5.6 と 7.0 で実行した結果が以下です。2,000文字あたりから処理速度が低下していき、20,000文字、200,000文字で急激に処理時間がかかっています。なお、5.6 より 7.0 の方が文字数が多い場合は処理が速いので、何かしらの最適化が行われているのかもしれません。

  • PHP 5.6.24
文字長 実行時間
20文字 0.05ms
200文字 0.35ms
2,000文字 7.2ms
20,000文字 2,438ms
200,000文字 233,513ms
  • PHP 7.0.9
文字長 実行時間
20文字 0.4ms
200文字 0.2ms
2,000文字 5.8ms
20,000文字 498ms
200,000文字 50,352ms

正規表現を改良

文字列末尾のスペースにマッチングさせる正規表現を改善するために \s++$ に変えてみます。元の最大量指定子(\s+)を絶対最大量指定子(\s++)に変更しただけです。

これでベンチマークを取ると下記のようになりました。両バージョンとも処理速度が改善しており、200,000 文字で見ると、PHP 5.6 で 18 倍、PHP 7.0 で 4 倍と大きな改善となっています。変更後は、PHP 5.6 と PHP 7.0 でほぼ互角になっているのも興味深い点です。

このように正規表現の書き方一つで、大きくパフォーマンスが変わる場合があります。

  • PHP 5.6.24
文字長 実行時間
20文字 0.17ms
200文字 0.041ms
2,000文字 1.91ms
20,000文字 172ms
200,000文字 12,879ms
  • PHP 7.0.9
文字長 実行時間
20文字 0.26ms
200文字 0.047ms
2,000文字 1.29ms
20,000文字 123ms
200,000文字 12,544ms

絶対最大量指定子を使った場合( aへのマッチング)は、下記のようなマッチングを行います。絶対最大量指定子は、貪欲なマッチングとなり、かつマッチした文字列をアトミックに扱います。これにより、バックトラックの発生が抑えられるのでマッチング回数を抑制できます。これは、アトミックグループ((?>))でも同様の効果が得られます。

* (___)[a] <--- `\s++`はマッチするが、`$`がマッチしないのでバックトラック
* _(__)[a] <--- 1文字進めてマッチング。`\s++`はマッチするが、`$`がマッチしないのでバックトラック
* __(_)a   <--- 1文字進めてマッチング。`\s++`はマッチするが、`$`がマッチしないのでバックトラック
* ___[a]   <--- 1文字進めてマッチング。`\s++`がマッチしないのでバックトラック
* ___a[]   <--- 1文字進めてマッチング。`\s++`がマッチせずに終了

今回計測したPHPコードは、下記です。

regex_trim.php · GitHub

あえて正規表現を避ける

PHP でこの処理を単純に考えれば、ltrim関数で簡単に実装できます。

ltrim関数を使うと、200,000文字のスペース + a の場合でも、わずか 1ms 以下でした。

stackoverflow.com では、正規表現を使うことをやめ、文字列処理(substring関数)に取り替えたようです。

さいごに

正規表現の書き方や文字列が、パフォーマンスに影響を与えることを見てきました。

マッチングパターンのみに注力して記述した正規表現が、正規表現エンジンによって、上手い具合にチューニングされて動作すれば良いのですが、最適化が上手く働かないケースでは、その挙動を把握した対処を行わないとパフォーマンスの問題を抱えることになります。

このあたりは、SQL にも似たものを感じしますね。

正規表現は、Web アプリケーションにおいてはバリデーションで利用されることが多く、外部の値をいきなり正規表現にかけてチェックするということもあります。バリデーションは成功しないが、DoS となるような値を投げられて、パフォーマンスが低下したり、サービスが停止させられる場合も考えられます。

正規表現のパターンを考慮する、文字列長をチェックしておく、場合によっては正規表現を使わない、など対処法は考えられるが、まずは正規表現がパフォーマンス上の問題になりうるということを認識することが第一歩になるでしょう。

参考

正規表現エンジンの挙動については、下記の 2 冊がとても参考になります。どちらかというと、「詳説 正規表現」は正規表現を利用する側、「正規表現技術入門」は正規表現エンジンを実装する側から正規表現を見た本となっています。どちらも内部の動きを分かりやすく解説しているので、本エントリのような内容をより知りたい方には特におすすめです。

どちらも Kindle 版が無いので、電子版を購入する場合は、オライリー社サイト、技術評論社サイトで購入します。

詳説 正規表現 第3版

詳説 正規表現 第3版

  • 作者: Jeffrey E.F. Friedl,株式会社ロングテール,長尾高弘
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2008/04/26
  • メディア: 大型本
  • 購入: 24人 クリック: 754回
  • この商品を含むブログ (85件) を見る

正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)

正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)

*1:これは、PCRE の量指定子が最大量指定子なためです。最小量指定子や絶対最大指定子を指定したり、正規表現エンジンタイプが異なればマッチの仕方は変わります。

より実践的なDDD本「.NETのエンタープライズアプリケーションアーキテクチャ第2版 」

.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)

DDD 関連の書籍といえば、Eric Evans の DDD 本( エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践) )や、Vaughn Vernon の IDDD 本( 実践ドメイン駆動設計 (Object Oriented SELECTION) )が有名です。

DDDで登場する実装パターンや周辺知識をより実践的に解説しているのが、「.NETのエンタープライズアプリケーションアーキテクチャ 第2版 (マイクロソフト公式解説書)」(naa4e)です。

.NET 以外にも大いに通じる内容

本書は、.NET の名を冠しており、サンプルコードは、C# で記述されています。また、利用するライブラリも .NET 周辺のものであったり、解説で登場する RDBMS も、Web 系でよく登場する MySQL や PostgreSQL よりも、SQL Server や Oracle がメインとなっています。

しかし、.NET 固有の話はそれだけです。.NET 固有の内容は、枝葉の部分であり、メインの内容は、プラットフォームやプログラミング言語に関わらずとても参考になるものでした。

日頃、.NET 界隈にいない人間としては、正直、マイクロソフト公式解説書としての体裁になっているがゆえに避けてしまう人がいるのが、勿体無いとさえ感じる本でした。

DDDをガラスケースから日々実践するプラクティスへ

DDD 本、IDDD 本と本書を比べると、より実践に寄せた内容となっており、DDD をより身近な普段の開発に適用するために解説されています。

実際の開発では、上手くパターンにあてはめられなかったり、妥協してしまう場面もあるのですが、そのあたりも考慮に入れられています(トランザクションスクリプトも場面によっては有用なパターンである、など)。本書の表現を借りると、ヒーローがいなくても、一般の開発者が DDD 取り組む方法を示した本とも言えます。

それぞれ執筆された時期が異なる(DDD 本(2003年)、IDDD 本(2013年)、本書(2014年))ので、理論から実践、さらに実践した上で現実的な落し込みといった具合に、より現実的な内容に推移しているのが興味深いです。

私は、DDD 本、IDDD 本、本書の順序で読んだのですが、本書が一番理解しやすいと感じました。*1

本書の内容

本書の目次は下記です。

現代の設計手法や開発手法、OOPの基礎(SOLID原則など)やテスト手法からはじまり、DDDで利用されるパターンの解説に入っていきます。

本書で興味深いのは、第6章のプレゼンテーション層がドメインアーキテクチャのすぐ後に来ている点と、CQRS と イベントソーシング(ES)に多くのページが割かれている点です。

■第1部 基礎
第1章 現代のアーキテクトとアーキテクチャ
第2章 成功のための設計
第3章 ソフトウェアの設計原則
第4章 高品質なソフトウェアの作成

■第2部 アーキテクチャの考案
第5章 ドメインアーキテクチャの発見
第6章 プレゼンテーション層
第7章 伝説のビジネス層

■第3部 サポートアーキテクチャ
第8章 ドメインモデルの紹介
第9章 ドメインモデルの実装
第10章 CQRS の紹介
第11章 CQRS の実装
第12章 イベントソーシングの紹介
第13章 イベントソーシングの実装

■第4部 インフラストラクチャ
第14章 永続化レイヤー

ユーザエクスペリエンス(UX)ファースト

ソフトウェアの世界では、バックエンドから取り組むのが慣例となっていました。私たちの多くは、プレゼンテーションを システムのそれほど高尚な部分だと見ておらず、ビジネス層をデータアクセス層が完成してから取り組めばよい、という程度に考えていました。しかし、システムの複雑さを問わず、プレゼンテーションとバックエンドが等しく必要であることに疑問の余地はありません。

Webアプリケーションでもサーバサイドをメインにやっていると、プレゼンテーションの重要性は認識はしていても、重点をサーバ側に置いてしまいがちでした。

しかし、プレゼンテーションはユーザが直接システムに触れるインタフェースであり、ここからシステムのユースケースやタスクを見ていくというのは当然のことです。

自分が設計する際は、プレゼンテーションももちろん考えますが、どちらかと言えば、サーバサイドの設計を主に考えていた節があるので、ハッとさせられました。

CQRS を身近なパターンに

CQRSは、ソフトウェアアーキテクチャにおけるコロンブスの卵です ── すなわち、自明ではない問題に対する驚くほど明確でそつの ないソリューションです。開発者とアーキテクトは、DDDとDomain Modelの階層化アーキテクチャを理解し、正当化し、機械化すること に何年も費やしてきました。特に複雑なビジネスドメインでは、クエリスタックとコマンドスタックの両方をドメインモデルで実現する ことがきわめて難しいという問題と格闘してきました。 CQRSにより、すべてが一夜にして変わってしまい、複雑なビジネスドメインがはるかに管理しやすくなりました。

IDDD 本では、やや特殊なパターンとして解説されていた CQRS が、多くの場合に適用できる身近なパターンとして解説されています。

このあたりは、私もドメインモデルからクエリを分離して、クエリについては、DTOを返すリポジトリを作って実装した経験から、わりと使い勝手が良いパターンではないかという印象を持っていました。

本文では、単なる CRUD パターンでも、CQRS にしてコマンドとクエリの関心事を分けるのは意味があると書かれており、CQRS に興味がある人には参考になる内容でしょう。

イベントソーシング(ES)と似たものは、30年以上前からある

先に挙げたアプリケーションはどれも数十年前から存在しているもので、COBOLやVisual Basic 6でかかれたものさえあります。つまり、イベントソーシングなんて仰々しい名前が付いていても、決してソフトウェアの新しい概念の到来を告げるものではないのです。

新たな名前が付いたものが出てくると、身構えてしまいますが、実は従来からあるものに名前が付いてパターン化したものというのも良くあります。

その最たるものが、GoF でお馴染みのデザインパターンでしょう。イベントソーシングもこうした以前からあるものをパターン化したものです。

現実世界での出来事では、発生した事象が消えてなくなることはなく*2、事象をイベントとして残していくというのは特にユーザからすると理解しやすいものです。

例えば、商品購入を記録する場合、購入情報のドメインモデルを保存し、ステータスが変わる度にそれを書き換えます。そして、イベントを補足情報としてログに記録するというのは良くあるパターンです。

イベントソーシングでは、これを逆転し、イベントを記録する方を主とします。そして、保存したドメインモデルは、あくまでもスナップショットとして扱います。イベントには、必要な情報が全て記録されているので、原理的にはそれらを順に適用していけば、どの時点のドメインモデルでも再現できるからです。

実際にイベントソーシングを実装するとなると、パフォーマンスの問題は無視できないのですが、ここについても実装のアイデアが解説されています。

さいごに

DDD 本を読んで、自分たちが取り組んでいるプロジェクトに DDD を実践してみたい人や、実際に取り組んでみたもののまだしっくり来ない人に手にとって見てもらいたい本です。

ユーモアを交えて、平易な表現で書かれており、理解しやすいので、これから DDD を学ぶ人や OOP の基礎を見直す人にも良さそうです。

実装で具体的なイメージを持った状態で、理論に立ち返るとより理解が深まるので、この本から DDD 本や IDDD 本に戻るのも良いですね。

なお、この本を読んだ後に、Amazon でリコメンドされた「C#実践開発手法 (マイクロソフト公式解説書)」も気になる内容だったので、こちらも読んでみたいと思います。

.NETのエンタープライズアプリケーションアーキテクチャ第2版 .NETを例にしたアプリケーション設計原則

.NETのエンタープライズアプリケーションアーキテクチャ第2版 .NETを例にしたアプリケーション設計原則

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

実践ドメイン駆動設計 (Object Oriented SELECTION)

実践ドメイン駆動設計 (Object Oriented SELECTION)

*1:もちろん、それは前 2 冊を読んで、多少なりとも実践したからこそというのはあります。

*2:購入商品をキャンセルしても、購入した事実は消えない

PHPにおけるhttpoxyの対応

HTTP リクエストに任意の値をセットすることで、Web アプリケーションからの HTTP 通信を傍受したり、中間者攻撃(Man-in-the-Middle)を可能にする脆弱性が見つかっています。

専用サイト

httpoxyという名前が付けられ、専用サイトが立ち上がっています。詳細は、このサイトが詳しいです。

httpoxy.org

攻撃内容

  • アプリケーションからHTTP通信を行う際に、環境変数HTTP_PROXYの値を、HTTPプロキシとして見るライブラリがある。
  • HTTPリクエストにProxyヘッダを付けられると、環境変数HTTP_PROXYにその値がセットされる。(これは、CGIの仕様)
  • つまり、任意のプロキシを外部から指定できてしまうので、通信内容の傍受や偽装ができてしまう。

対象となる PHP アプリケーション

  • HTTP リクエストを受けて動作する PHP アプリケーション
  • アプリケーションから、HTTP 通信を行うもの
  • 利用している HTTP 通信を行うパッケージなりライブラリが、環境変数 HTTP_PROXY の値を HTTP プロキシとして扱う場合

対応方法

1 or 2 のいずれかの方法で対応します。

1. httpdサーバ(アプリケーション以前で)Proxyヘッダを落とす。

  • nginx

www.nginx.com

  • Apache(httpd)

https://www.apache.org/security/asf-httpoxy-response.txt

冒頭の専用サイトでは、Varnish や HAProxy などの対応方法も記載されている。

2. 環境変数HTTP_PROXYをHTTPプロキシとして利用しないようにする。

  • Guzzle

Guzzle がこれに該当していたので、修正版がリリースされている。Guzzle に依存するパッケージでは、6.2.1以上を使う。

Guzzle の対応コミットは以下。SAPIを確認して、cli以外では、HTTP_PROXYを利用しないようになっている。

github.com

Drupal が、Guzzle を利用しており、composer.json の依存バージョンを ~6.2 に変更している。Guzzle を利用したアプリケーションならこの対応が参考になる。

github.com

  • curl

curl は、10年以上前からこの問題を認識しており、大文字のHTTP_PROXYは見ないようにしていた。

Linux では、環境変数は case sensitive なので、HTTP_PROXYhttp_proxy は別ものとなる。

番外. PHP で、HTTP_PROXYの値を落とす対応はオススメしない

下記のようなPHPコードでの対応は、効果が無い *1 ので、上記 2 つでの対応が望ましい。

$_SERVER['HTTP_PROXY'] = ''; // 環境変数には効果無し
putenv('HTTP_PROXY=');       // ヘッダから来た値には無効
  • Using unset($_SERVER['HTTP_PROXY']) does not affect the value returned from getenv(), so is not an effective mitigation
  • Using putenv('HTTP_PROXY=') does not work either (to be precise: it only works if that value is coming from an actual environment variable rather than a header – so, it cannot be used for mitigation) https://httpoxy.org

追記1(2016/07/20)

getenv() は、SAPI ごとにハンドラを登録することができ、定義されていれば、そちらが優先される仕組みになっています。 github.com

例えば、php-fpm であれば、php-fpm 実行中は、fcgi env から値を取得するようになっています。 github.com

追記2(2016/07/20)

PHP 本体には、すでに修正コミットが入っています。

github.com

次のリリース(下記を見る限りは、2016/07/21)に含まれています。

github.com

追記3(2016/07/20)

RHEL / CentOS では、すでに httpd パッケージに修正版が出ています。Proxyヘッダの値を、環境変数HTTP_PROXYの値としてスクリプトに渡さなくなります。

CVE-2016-5387 - Red Hat Customer Portal

CentOS alert CESA-2016:1421 (httpd) [LWN.net] CentOS alert CESA-2016:1421 (httpd) [LWN.net] CentOS alert CESA-2016:1422 (httpd) [LWN.net]

追記4(2016/07/21)

httpoxy 対応版の PHP 7.0.9、5.6.24、5.5.38 がリリースされました。

http://jp2.php.net/downloads.php#v7.0.9

http://jp2.php.net/downloads.php#v5.6.24

http://jp2.php.net/downloads.php#v5.5.38

参考

blog.ichikaway.com

HTTPoxy - CGI "HTTP_PROXY" variable name clash - Red Hat Customer Portal

*1:$_SERVER['HTTP_PROXY']の値をプロキシとするライブラリがあれば効果はあるが...

「ざっくり分かる WordPress サイトのチューニング」を WordCamp 2016 で発表してきました。

2016/07/09、10 に大阪大学 豊中キャンパスで開催された WordCamp Kansai 2016 にて、WordPress サイトのチューニングについて発表しました。

f:id:shin1x1:20160711095229j:plain:w500 https://twitter.com/digitalcube/status/751693759121731584

発表資料

今回は、PHP(WordPress)エンジニアでなくても何か役立ててもらえるようにチューニングの具体的な手法というよりは、実際に手を動かすエンジニアと意思疎通がスムーズになるように基本的な考え方をメインにお話しました。

「推測するな、計測せよ」という格言があるとおり、チューニングにおいても、まず大事なのは「計測する」ということです。計測せずにやみくもにチューニングを行っているケースを見聞きするので、ここからはじめる内容にしました。

後半は、キャッシュを中心に紹介したのですが、最後の Varnish Cache に興味を持った人が多かったのが印象的でした。エンジニアメインの会場なら、こちらをメインに構成しても良いですね。

以前、Varnish について書いたエントリがあるので、参考まで。 http://www.1x1.jp/blog/2013/12/varnish-cache.html

さいごに

昨年に続き、お声がけ頂き、2年連続の登壇となりました。

前回は、β2 の PHP 7 上で WordPress を動かす内容だったのですが、今では、PHP 7 を本番環境で利用しており、1 年でも状況の変化は大きいなと感じたりもしました。

www.1x1.jp

今回も発表の機会を頂き、ありがとうございました。

参考

今回の内容に関連して、参考になるリンクは下記です。

「SOFT SKILLS」を読む前に知っておくと良いこと

ソフトウェア開発者の間で、話題の書籍「SOFT SKILLS」を読みました。ひと通り読んで感じたことなど書いてみます。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

同じ内容の文章を読んでも、それを誰が書いたかによって受ける印象は変わります。

まさに、それを実感した本でした。

ソフトウェア開発者が書いたビジネス書

この本は、「ソフトウェア開発者」が語る「ソフトウェア開発者の人生マニュアル」です。ざっくり言えば、「ソフトウェア開発者向けのビジネス書」ともいえるでしょう。

内容自体は、ビジネス書を数冊読んだことがある人であれば、それほど目新しいというわけではありません。実際、書籍内でも筆者が参考にしているビジネス書(ロバート・キヨサキの「金持ち父さん 貧乏父さん」やデール・カーネギーの「人を動かす」など)が紹介されており、こうした書籍から影響を受けた思考や行動がつづられています。

ビジネス書は、私も嫌いではないので、たまに読みます。やはり、読んだ後は元気が出るので、即効性の高い心のドーピングとして重宝します。

ただ、ビジネス書は、突然、大金持ちに声をかけられて、人生のアドバイスを受けたり、象が成功へのアドバイスをくれたりと現実離れしている面もあり、内容は面白く参考にできるところはあっても、身近な話として飲み込みづらい面もあります。

その点、本書は、「ソフトウェア開発者」が書いているのが大きな特徴です。それぞれの内容は、他のビジネス書と似たものでも、開発者の目線で書かれているので、身近な話として受け止めやすくなっています。(アメリカと日本とで事情は異なるものの)同じソフトウェア開発者が、自分の体験を語りかけてくれているように感じました。

序文からして、ロバート・C・マーティン(アンクル・ボブ)とスコット・ハンセルマンですから、普通のビジネス書では無いことは明らかです。アンクル・ボブの序文は秀逸で、これだけでも一つの章になりますね。ここで、グッと引き込まれて、本書に対する信頼感が上がります。

75のブログエントリ

本書は、テーマごとに7つの部があり、それぞれの部が細かな章に分かれています。全体としては、450ページほどあるのですが、章が71個(+付録4章)あるので、1つの章は数ページ程度です。これだと、空き時間に1章だけ読もうと気軽に読み進めることができます。書籍でも書かれていますが、1章は1ブログエントリを読むくらいの感覚なので、このあたりもソフトウェアエンジニアにチューンされたビジネス書と言えます。

第1部 キャリアを築こう
第2部 自分を売り込め! 
第3部 学ぶことを学ぼう
第4部 生産性を高めよう
第5部 お金に強くなろう
第6部 やっぱり、体が大事
第7部 負けない心を鍛えよう

特に良いなと感じたのは、第2部と第4部です。

コードを書き、ブログを書き、人前で発表して、書籍も書くというのは、自分の活動と重なる部分があって興味深く読めました。

また、生産性の向上については、Rebuild FM #139 を聞いてから、また自分自身の熱が戻ってきており、ちょうどポモドーロ・テクニックをやっていたので、タイムリーでした。書籍で紹介されていた KanbanFlow は便利に使っていますし、最近はスタンドアップで仕事したりしてます。こうした話は気軽に試すことができるので、即効性がありますね。

このあたりまで順調に読み進めていて、会う人にも、この本いいよ!と勧めていました。

ソフトウェア開発者に向けたビジネス書

いつもどおり楽しみに読み進めていたのですが、第5部あたりから印象が変わっていきます。

ここは、お金に関する話ということで、経済的自由をいかに手に入れて、自分の望む生き方をしていくかというビジネス書定番のテーマに入ります。

「第55章 打ち明け話: 私が 33 歳で引退できた理由」がターニングポイントでした。

この章では、筆者がいかに現在の成功を収めるかが自伝として語られています。20代はじめに年15万ドルをソフトウェア開発者として稼ぎ、そこから不動産や株式投資で稼ぐ方法を模索します。そして、オンラインでプログラミング講座を公開していきます。さらに、時折、モデルや俳優もやっていたそうです*1。もちろん、良いことばかりではなく、色々な浮き沈みが語れられており、ハードワークをこなした結果、現在へと行き着いたというわけです。

これは誰もができることはなく、読んでいる自分も、ただただ、すごいなあ、という感想を持ちました。

でも、なぜか、私のテンションは下がっていきました。なんだか、身近な人と感じていたのが、急に遠くの人に感じるような。

同じ内容の文章を読んでも、筆者へのイメージによって、受ける印象は変わります。

このあたりから、よくあるビジネス書を読む感じで進んでいきました。このあたりからは、ソフトウェア開発者はあまり関係無く、一般的な話(お金、体、心のあり方)になるので余計にそうした印象を受けました。

後半におすすめ書籍として、「CODE COMPLETE 第2版 上 完全なプログラミングを目指して」や「Clean Code アジャイルソフトウェア達人の技」、「Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本 *2」が入っていたので、ああソフトウェア開発者なんだと再認識して安心したくらいです。

さいごに

訳者あとがきで、本書を翻訳された長尾さんが「うまいニッチ(第20章)を見つけたもの」と書かれていたのに深く納得しました。まさに、「ソフトウェア開発者に向けたビジネス書」というニッチを上手く狙った書籍というのが読み終えた印象でした。

読み手は文章の内容から勝手に書き手のイメージを作り、そのイメージで書かれた内容を受け止めます。本書では、その受ける印象の違い(変化)を感じることができました。

おそらく本書を手にとる人は、他の開発者から「いいよ!」と勧められて読み始める人が多いでしょう。そこで、このエントリの内容を少し頭に入れた上で読み進めると、また違う捉え方ができるかもしれません。

私もほとぼりが覚めた頃にまた開いてみて、その違いを楽しんでみたいと思います。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

*1:書籍に写真がありますが、マッチョなイケメンです :)

*2:この本は、あまり紹介されないですが、おすすめです。