2015年6月12日金曜日

PSR7用のヘルパーとレスポンダー

コツコツとフレームワークを作っていて。
ふと、自分が欲しかったのは、もっと簡単なことじゃないか?と思い直して作ったのがTuum/Http」というパッケージ。

【修正:2015/06/26】
Tuum/Responder」に名前を変更しました。

そもそものスタートとして、SlimやStackPHPの簡潔な構造に憧れたところから始まっている。が、実際に使うとなると、API作るには便利だけれど、普通のウェブサイトを構築するには作業が面倒そうだなぁと。

面倒だと思った部分は、レスポンスを返す部分。たとえば前のページに戻ったり、ついでにメッセージやエラー情報を付加したりという部分。同じような処理が多い割には、細かな設定が必要な気がする。

そこで、この部分だけをパッケージにしてみた。


何をするパッケージなのか



大きくヘルパーとレスポンダーからできている。
大事なのはレスポンダーの方。

ヘルパー

Psr7に足りなさそうな機能をスタティックなメソッドとして提供している。見れば一発、簡単なものばかり。例えば、

$bool = ResponseHelper::isRedirect($response);

はリダイレクトレスポンスかどうかをチェックできる。RequestHelperとResponseHelperの2つがある。

レスポンダー

レスポンスを構築するためのヘルパー。
例えば、別パスにリダイレクトしたり、その際にメッセージを付加できる(単にセッション・フラッシュに登録してるだけだが)。

Redirect::forge($request, $response)->withMessage('welcome!')->toPath('jump/to');

// ...now in the subsequent request to a server...
Respond::forge($request, $response)->asView('template'); // with the 'welcome!' message.

次のリクエストの際に、前のメッセージが自動でビューに入ってくる。メソッド名がどこかで見たことのあるのは愛嬌。使いやすいと思ったので。

レスポンダーとしては、RespondRedirect、そしてErrorがある。それぞれ、ビューを返す、リダイレクトを返す、エラーページを返す。

使うのに必要なこと:サービス


レスポンダーの機能を実現するために、ビューやセッションなどのサービスを使っている。これを作っておいて、レスポンダーに渡す必要がある。

セッション用インターフェース

セッションのフラッシュを使ってリクエスト間のデータを受け渡している。そのためのセッションとしてSessionStorageInterfaceという形で定義した。

と行っても、実際はAura.SessionのSegmentと同一のAPI。これを使うのが前提みたいになっている。

use Aura\Session\SessionFactory;

$factory = new SessionFactory();
$session = $factory->newInstance($_COOKIES);
$segment = $session->getSegment('some-name');

ここで作った$segmentがSessionStorageInterfaceと同じオブジェクトになる。

これを$requestに設定する。
use Tuum\Http\RequestHelper;
$request = RequestHelper::withSessionMgr($request, $segment);

あるいは、後で書くようにコンテナに設定しておく方法もある。

ビュー用インターフェース

HTMLを返すにはテンプレートを使ったビューを使いたい。そのために、ビューを展開するViewStreamInterfaceを定義してみた。

テンプレートを展開できるStreamとして扱う。

ViewDataクラス

これは実際のクラス。
ビューやリクエスト間でデータをやりとりするためのデータ転送用オブジェクト。

ビュー用のクラスはViewDataを理解して、実際のレンダラーに渡す必要がある。


コンテナー用インターフェース

サービスを管理するコンテナ。Container-Interopで定義したインターフェースを使った。これに必要なサービスを登録しておいて、$requestに設定する。

次はセッションを設定する方法。

$app = new Container(); // must implement ContainerInterface
$app->set->(SessionStorageInterface::class, $segment);RequestHelper::withApp($request, $app);




2015年5月24日日曜日

PHPの__invokeメソッドの使いドコロは?

PHPにはクロージャー(無名関数)と、クラスを無名関数のように使えるようになるマジックメソッド、__invoke、というのがある。これに関する雑感。

クロージャーと言うのは便利なもので、プログラムをデータとして扱える。

$hello = function($world) {
  return 'hello ' . $
world;
};
便利なケースを思いつかなかったので、適当に書いたコード。


一方で、__invokeメソッドの使いドコロは何だろう。

自分が思いつくのは、
1.流行りのファンクショナルな言語のように書ける。
2.メソッド名を考えなくても使える。
3.クロージャーの代わりにオブジェクトを使いたい場合に必要。

最初の1.と2.は冗談なので、実際は3.の場合かなと思える。つまり何かの関数の引数としてクロージャーを想定している場合でも、オブジェクトが使える。

でも3.の理由は、クラス自体でのメリットというより、必要だからという感じがする。

あとPHPの問題として、意外なところでコードのパースが悪いところがある。せっかくコンパクトに書けるはずのクロージャーなのに、かえってコードが複雑になってしまう。

class Hello {
  private $hello;
  function __construct($hello) {
    $this->hello = $hello;
  }
  function hello($world) {
    return $this->hello($world); // 動かない!
  }
}

なんで、こんなことを書いてるかって?

いや、1.と2.の理由で__invoke使って見たら、意外と面倒で。一方で感じたメリットは3.しかなかったので。それで__invokeメソッドを使うメリットは他に何があるか?そんなことを思ったのでブログに書いてみた。

自作フレームワークでのミドルウェア設計

自作中のウェブアプリケーションフレームワークではミドルウェアベースで動きます。基本は「StackPHPとミドルウェア」に書いたとおりですが、実際のインターフェースに落とすまでの過程についてまとめてみます。

と、Blogspotでコードを書くのが面倒なのを思い出したので、gistに書いた。

フレームワークの文章としては、もっと簡潔に必要な箇所だけにまとめる必要があるだろうな。

2015年4月3日金曜日

ミドルウェアベースでPSR7を使った自作フレームワーク

相変わらずフレームワークを自作している。勉強になるし、万が一、いいものができたら実際に使ってみたいと思いながら作っている。今までは、勉強がメインだったが、今回は本当に使えそうな感じになってきた。

名前は「TuumPHP」。

作り始めたきっかけ

StackPHPとLaravel


2014年の夏頃、Laravel4.2で開発をしていて、これは楽に開発できるフレームワークだなぁと関心した。ただ自分の仕事だとアップデートについて行くのは難しいと感じていた。一旦作ったら、作り変えない程度の機能追加がたまにある程度で、数年は運用する必要がある。まぁなんとかなると思うけど。

そんな時、ミドルウェアとStackPHPについて調べてみて、これは素晴らしいと。単純な構造を組み合わせることで複雑なアプリを構築することができて、PHPの方向はこれだと思った。こんなエントリをQiitaにアップしている

StackPHPとミドルウェアについて調べてみた

つまり、

  • StackPHPのような簡潔な構造で、
  • Laravelのように開発しやすいくて、
  • データの流れが自然(ファサードとか使わない)、
  • できればバージョンアップもパッケージ別に対応可、

そんなフレームワークがほしいと思った。

ちょいと探したけれど、いいのが見つからず。じゃ、自分で作ってみるか、というのが始まり。

Psr-7の導入


作ってからしばらくして、標準のHTTPリクエストやレスポンスの規格がもうそろそろ完成するという話が流れてきた。Psr-7という規格。しかも不変オブジェクトが使われている、ときいて早速導入して見た。

その時の経験からQiitaに書いたのが、

Psr7を使ってみた(というか不変オブジェクトを初めて使った感想)


TuumPHPの特徴

余り思いつかないので、徒然と書いてみる。

マイクロフレームワーク+ビュー。
つまりSlimやSilexより少し機能が豊富なフレームワーク。モデル側(DB)などは持ってない。

マジックな機能


つまりLaravelから拝借した機能、ということですね。

  • フォームの入力エラーで、フォームにリダイレクトで戻るときに、入力値(withInputs)やバリデーションエラー(withErrors)などを一緒に返せる。
  • フォームに値を表示するときに、リダイレクトの戻り値(withInputs)を簡単に利用できる。
  • APIやメソッド名を参考に。

意外と少なかった。

ミドルウェア


なんでもミドルウェア。

ルーティングもミドルウェアなので、いくつでも追加できる。ルーティングの前のプリ・ルーティングも、ルーティング後のコントローラ内のポスト・ルーティングもできる。

ルーティングの後のディスパッチャーもミドルウェア。これも交換可能。

もちろんコントローラーもミドルウェアの一つ、ので、単純な継承でコントローラークラスを作れる。というか継承する必要は無いはず。

コントローラ内で簡易ルーティングができる。貧者のアノテーション・ルーティングですね。

状態


まだアルファ。
APIも含めて色々と変わる可能性は高い。

独立したコンポーネントを作っていて、こちらはドキュメントとテストはそこそこあるが、フレームワークとしてはドキュメントもテストもほぼ未着手。

その代わり、こんな文章を書いている。フレームワークの使い方ではなくて、どうしてこうなった?の理由を書いたほうが勉強になるかなと思ったから。


今後


すでにZend FrameworkもSlim Framework も次のバージョンでPsr-7でミドルウェアに対応すると表明している。。Laravel 5もすでにミドルウェアベースになっているし、次にはPsr-7を使ってくるのでは無いかと思う。

注目したいのは、今後のPHPで


よく使われるミドルウェアAPI


はどれか、という点。APIが同じなら互換性が高いため、コードやオブジェクトを共通して使える。例えばOAuthとかのクラスを使いまわせるかどうか、というのが大事。

もしフレームワークを自作するなら、APIの目安がついてからのほうがいいと思う。

2014年12月18日木曜日

Software Designの2015年1月号「ソフトウェア開発の未来」

久々に本屋でIT関連の雑誌を買った。
ランチの後に、近くの本屋に入って手にとって見たのが、

Software Designの2015年1月号

タイトルは「Vim使い事始め」だけど、目に止まったのは「ソフトウェア開発の未来」のほう。特にサブタイトル「請負・受託開発は変わるべきか?」が決定的だった。

内容は、渋い。
目新しいことや、びっくりするようなことは書いてない。
知ってた、みたいな話も多い。

でも、こういう普通の話を聞く機会というのは少ないので、タメになる。

そして考えがスッキリした気がする。
モヤモヤと思ってたことを、整頓した感じ。

これからもSIという名前の請負案件はなくならない。日本のIT業界内でのシェアは減ってゆくと思う。が、絶対的な金額としては、特に中小規模は減らないのではないかと思う。

そして自分はお客さんと話をして要件を聞いたりまとめたりするのは好きだ。二次三次の下請けにならない限り、請負仕事は続けてゆくと思う。ただ直接お客さんと話せない状態になったらわからないかな。

今日のポイント。

設計力は大事。
シンプルな技術で長期間のサポート。
そしてお客様の困っていることを解決すること。

2014年11月13日木曜日

フレームワーク自作したい病が発生した

あまりに内容が薄いメモなので、自分のブログで書くことに。下書きみたいなものかと思われる。

StackPHPを参考にフレームワーク自作を始めた。自作すると大変、と言うのは経験済みだが、自作したくなってしまったのは仕方がない。

さて、大事な方向性は。

  1. StackPHPのように「ミドルウェアのスタック」でウェブアプリを構築する。ただし、StackPHPは使わずに自作することに(理由は後述)
  2. できるだけミドルウェア側に処理を寄せること。
  3. ミドルウェアはできるだけ疎結合であること。
  4. 既存のライブラリを有効に利用すること。
  5. ウェブサーバーのフロントに特化。DBなどのインフラ周りは含まない。
  6. ひとまずDIコンテナは利用しない。
書いてみたら真っ当な内容で、特に目新しさが感じられない。でも、調べたところではStackPHPを使ったフルフルなフレームワークが見つからなかった。

StackPHPを使わなかった理由

StackPHPのミドルウェアとして次の3つを満たす必要がある。
  1. SymfonyのHttpKernelInterfaceを実装すること。
  2. コンストラクターの第一引数として次のミドルウェアを受け取ること。
  3. (必要であれば)handleメソッドの中で、次のミドルウェアを呼び出して結果を返すこと。
疑問に感じたのは、ミドルウェアの中に、「実際の処理」と「次の処理を呼び出す」、という2つの責務を持っていること。多すぎるんじゃないか?!

例えば認証とかフィルターを考えてみる。
使い方としては、ミドルウェアとしてスタックの中に放り込んで、全アクセスで必ず認証をさせる。あるいは認証する条件(URLなど)をつける。そしてbeforeなど特別な場所から認証フィルターを掛けてみる。
などなど、いろんな使い方が想定できる。

つまり、次のミドルウェアを呼ばない使い方も多いようにみえる。そうなら、このミドルウェアは処理部分とスタックの部分を分離するほうがよくない?と思った。


で作ったのがStackableというクラス。これにHttpKernelInterfaceを実装した処理オブジェクトをpushしてあげると、次のスタックになる。スタックの考えが単純なので、コード自体は簡単だ。

DIコンテナは?

今後、PHPでもDIコンテナは重要な、いや使って当然な技術になってゆくと思う。が、今はいろんなコンテナが乱立しているし、標準もないし、使ってみると「遅いし」。

なので、ひとまずDIコンテナなしで作ってみることに。ただし依存性の注入は行うことにして、単純なfactory methodを使う方向で作ってます。

ミドルウェア作成してみた感想

実際にミドルウェアを作ってみると、意外と疎結合しないことがわかった。不便なままならいいけれど、やっぱり楽に開発したいと思うとマジックが必要になる。

例えばResponseを返すミドルウェアが複数存在する。一方、ここはフレームワークで統一したい(というか簡単にしたい)と思ったとする。結局、リスポンスオブジェクトを作るファクトリをアプリの中に持っておいて、必要に応じて取り出しては使う、みたいなコードがあちこちに散乱している。

データも共有することが多い。例えばリダイレクトしたら、ちょっとしたデータを一緒にセッションで送りたい。つまりリダイレクト部とセッションでデータを共有する必要がある。後CSRFトークンとかね。


こういう疎結合しながら共有するパターンといえばイベント(Event AggregatorやMediatorパターン)らしい。

しかしながらですよ、ミドルウェアは順番に実行するものですよね。勝手にpushするイベント系は、スタックとは相性が悪いのでは?。なので、「ひとまず」データやオブジェクトをアプリにpush/pullをするようにして、必要に応じて共有することにしてみた。もう少し他のFWの実装を調べてみる予定。


ライブラリについて

今、使っているライブラリ。意外と少なかった。
  • symfony/http-foundation
    スタックの基本なので、絶対外せない。
  • symfony/routing
    ルーティングに使う予定。もっと簡単なのを探してます。
  • league/flysystem
    設定ファイルを管理するためのファイルシステム。これにUnionManagerというのをかぶせることで、環境(productionやlocal)による設定を処理させる予定。
  • league/plates, aura/view
    生PHPベースのテンプレート。どちらを使うかは検討中。
  • wscore/form
    自作のHTMLフォーム生成コンポーネント。遅延評価でFluentにフォームが書けます。


2014年4月21日月曜日

LaravelのEloquent ORMについてQiitaに書いてみた

最近LaravelフレームワークのEloquent ORMを触ってます。まだ始めたばかりですが、2つほどQiitaに気がついたことをまとめてみました。

Eloquentのモデル生成法比較:newとcreate

Eloquentでリレーションを作成する方法


やはりMarkdown記法でかけるのがいいな。
特にソースコードの記述が楽だ。githubのお陰です。

Bloggerでも使えるようになるといいのだけど。