Strangeこの広告は、90日以上更新していないブログに表示しています。PSR-7は普段PHPにてHTTPメッセージを扱うインターフェイスとしてそこそこ十分に機能する。メインユースケースの8割は満たすだろうが、PHPのポテンシャルの5割にも満たないかもしれない。Recommendations)のステータスがレビュー段階に入った。https://github.com/php-fig/fig-standards/blob/master/index.mdそこでこのエントリではPSR-7のインターフェイスが実際のフレームワークとアプリケーション間での利用の際に上手く機能するかについて考察する。OOPとしての正しさについては深く言及しない(ヘッダーについてのデメテルの法則や、イミュータブル性などだ。PSR-7の仕様は、現在以下のプロポーザルディレクトリ下に置かれてる。可決されれば、acceptedフォルダへ移動されるだろう。interfaceshttps://github.com/php-fig/fig-standards/blob/master/proposed/http-message.mdもしあなたがPSR-7についての議論の過程を知りたいなら、PHP-FIGの該当のMLを参照してほしい。言うと私はMLの内容はほとんど追っていない。Interfacehttps://groups.google.com/forum/?fromgroups#!topic/php-fig/rQgQmEMtcdkまた、いくつかのPSR-7についての記事をあらかじめ紹介する。O'Phinney(mwop)の記事。PSR-7http://mtdowling.com/blog/2014/07/03/a-case-for-higher-level-php-streams/もともとPSR-7としてプロポーズしたmtdowlingによる記事。Servicesのメンバーである。注目してほしいのはメッセージボディにストリームを使う理由についてなのだが、後半に追って追及したい。Psr7を使ってみた(というか不変オブジェクトを初めて使った感想)http://qiita.com/asaokamei/items/b86e384867bcf0379cea共有インターフェイスは喜びか悲しみかPHPにおけるインターフェイス今更だがPHPにはインターフェイスがある。なんたらノート第三期ベータhttp://tanakahisateru.hatenablog.jp/entry/20120228/1330431278現在活発に機能追加について議論が進んでるPHPではあるが、近い将来インターフェイスの立ち位置が変わるかもしれない。立ち消えとなったようだ。ついてGoスタイルのインターフェイスもサポートしていることについてMLで述べてたね。インターフェイスについて主に扱っていこう。フレームワーク・ライブラリをまたいだインターフェイスについて考えてみよう。以下のコラムを書いている。Goodhttp://blog.astrumfutura.com/2011/10/interfacing-the-php-world-would-be-good/共有インターフェイスの話とは別にフレームワーク間での"依存"の話というとpmjonesのDe-Coupledについての主張など、10年代においてオブジェクトの取り扱いについてはPHP開発者でも重要な関心事だ。ついてひとこと言っておくかPHP-FIGについて断わっておくと、一部にはPHP-FIGは標準化団体なので開発者はPSRに強制的に従うべきだと考えてる人が昨今いるようだが、個人的にはなぜそんな息苦さを強いろうとしてるのか理解できない。そもそもなぜそのルールはあるのだろうか?PHPのフレームワークのあらましまず、PHP-FIG誕生前のフレームワークについての状況を俯瞰しよう。単純なリリース時期については、pmjonesが先月年表を公開していたのでそちらを参照いただきたい。日本語コミュニティ周辺に特化したものは私の方で個別に作成した。リリース時期を確認したところで何もインパクト度合が分からない。Frameworkについては4年ほどまえに記事を書いたが、あなたの"お好きな"フレームワークについてはあなたが一番ご存知のはずだ。PHP第一世代フレームワークブームがあったということだけ踏まえよう。PSR-0PSR-0今ならクラスオートローディングについての恩恵についてはみんな十分に理解してるだろう。(5年ほど前ならXフレームワークの仕組みを使うとクラス読み込みが楽という主張の記事も見られたのだが。略で、誕生は6年前に遡る。誕生期の個人的な印象は、「まさにフレームワーク間の相互運用のための最低限の取決め」それだけだった。正直な感想としては「PEAR由来のクラスローディングにみんな沿ってくれる」だった。したらこの記事を読んでる人には、開始当初はnews.php.net内にてやりとりが交わされていたのを記憶にある方もいるだろう。このMLはすぐに使われなくなり、googleグループならびにgithubへメインを映していく。数年後にlangspecのほうにML乗っ取られているが。口酸っぱく行っている。当時の発表時の反応としてはPSR-0についてはあまり知られてなかったが、composerの普及などもあり割と知れ渡っているだろう(この発表当時は、composerはcreate-project的な役割のツールとして作成されるだけだと思っていたので、現在の普及のされ方は予想もしなかったが。)PSR-1,2コーディング規約はより本質的な点にフォーカスを当てるためのものとして捉えている。参加者が盲目的に順守するだけに留まっていればあまり効果は期待できない気がする。基本コーディング規約とコーディングガイドについてのルールだ。PSR-0の項で述べた通り私はPSRの相互運用の最低限の取り組みについてのみ感銘を受けているだけなので、これが本当にフレームワーク間の相互運用に本当に必要なのか今でも懐疑的だ。皮肉的立場としてPHP-FG(figではない/FGだ)の存在もあるように。この記事ではインターフェイスについて着目したいので、PSR-2について確認したがInterfaceサフィックスについての命名規約はないようだ。Boggiano)が推し進めたものだ。PSRで初めてインターフェイス定義(コードファイル)を必要とするものが誕生した。#60https://github.com/php-fig/fig-standards/pull/60MLでの初めのスレッドhttps://groups.google.com/forum/?fromgroups=#!searchin/php-fig/LoggerInterface$20Proposal$20/php-fig/yL_wDyiT6GY/2vk9DWXGVgEJ日本語での紹介は、hirakuさんが発表記事を書いている。Framework2(ZF2)では後方互換性の観点からPSR-3は導入しない。このLoggerインターフェイスについてmwopのブログエントリがあるのそちらを見てもらおう。Interfaceshttps://mwop.net/blog/2012-12-20-on-shared-interfaces.htmlちなみにmwopはこの記事執筆時期にPHP-FIGのメンバから抜け代わりにpadraicbがZF側メンバとしてPHP-FIGに参画している。(あれちょっとまってよ。PHP-FIGhttps://mwop.net/blog/2013-02-27-resigned-from-php-fig.htmlPSR-3のロガーインターフェイスとZF2でのロガーインターフェイスの大きな違いはここだ。work?』PSR-3を初めて知った時の自分の反応は、「おいおいなんでarrayでタイプヒンティングしてんだよ?」だった。acceptされてれば考えはとっくに変わってた?..いやそれとも。キャッシュインターフェイスの泥沼PSR-3にてインターフェイスを定義する際のちょっとした問題点については分かっていただけただろう。そもそもロギングレベルについてはRFC5424という指針があったからこそ、議論は紛糾せず受け入れられたとも考えられる。紛糾?そうロガーよりも先にキャッシュについては先に提案があったがまだ決着は見えていない。PHP-FIG」というタイトルにて、次のような提言を行っている。http://blog.ircmaxell.com/2014/10/an-open-letter-to-php-fig.html50%の問題を解決するAPIを標準化しろとの主張がある。冒頭では「メインユースケースの8割は満たすだろうが、PHPのポテンシャルの5割」と書いたが、PSR-7では風呂敷を広げないように努めているようにも思う。PHPとHTTPここから先はPHPとHTTPについて立ち位置を確認していこう。PHPとHTTPの密接な関係PHPはサーバーサイドでの稼働を強く意識した言語だ。Apacheモジュールでの組み込みにて00年代前半に広まっていったのはご承知の通りだろう。Interface)にCLIが導入されたPHPのヴァージョンは4.3だ。思われた方はPHPのアーキテクチャについて、moriyoshiさんの過去のスライドの図が分かりやすいのでそちらを参照いただきたい(9-13ページの箇所が該当)。ほかには、do_akiさんのSAPIについての発表資料も参考になる。KoizumiPHPの処理原則としてHTTPが背後にある。Zeev曰く、「全てがリクエストの後に真っ更になる」が最もたる例だろう。並行処理するとして」の項参照)また、例えばPHPのソースコードでは$_FILESの扱うコードがmainフォルダにあるのもPHPがサーバーサイド言語たるところだろう。プログラムファイル名もそのままrfc1867.cだ。ついては以下の該当コードに処理がある。https://github.com/php/php-src/blob/PHP-5.6/main/rfc1867.c#L1036PHPでリクエストを扱う。PHPは、Webアプリケーションを作りやすい言語として良くも悪くもよく知られている。レスポンス文字列には、HTMLをそのままだったり言語構造のechoを使えばよい。ヘッダーを設定する際にはheader関数をコールすればよい。出力の前にコールする必要があるっていうのはマニュアルに覚えておいてって言ってるから皆知ってるよね?(え、エラーメッセージを何十回も見ることによって覚えたって?)。リクエストを扱う際には、スーパーグローバルによってパースされた値を取得することができる。ちなみに、スーパーグローバルが導入されたのはバージョン4.1.0だ。(PHP3のころから使い始めた人は一体どんな苦労をしてたんだろう?)しかしながら、グローバル変数は一般に好ましくない実践だと考えられている。いやいや、スーパーグローバルを使っていてもテストは書けるんだ。..と、ここまで書かなくても多くの人には、HTTPメッセージクラスが必要とされた動機が分かっていただけただろうか?それでは次に、いくつかのHTTPメッセージクラス/インターフェイスについて確認していこう。1ZF1のRequest/Responseはクソッタレな実装の一例だ。(今ではそう見えてしまう)。2つほどダメな点を挙げよう。getActionName()などControllerに特化したメソッドが用意されてしまっている。HTTPクライアントのReponseクラスは、Controller_Requestとはインターフェイスが別物。存在意義・メリットがあったことだけは注記しておきたい。1PHPにそもそもHTTPメッセージクラスが提供されている可能性はなかったのだろうか?そんな疑問に答えるのが、pecl_httpとの長い長いコア行きの話となる。少なくともその議題は2008年まで遡る。https://marc.info/?l=php-internals&m=122210992911766extension上の名前ではhttpだが、紛らわしいのでpecl_httpという名称も使われている。http://php.net/manual/ja/book.http.php上記マニュアルを見ればクラス構成が分かるだろう。RequestクラスとReponseクラスを備えていたのだ。HTTPメッセージに類しそうなHttpMessageクラスはそれらの基底クラスとはなってない。昔話をしてしまうとPEARのメンバが参画していたOrchestra.ioが(おっと、今ではこのドメインはengineyardに飛ぶようだ)始動し始めた前後で開発されていたREST用フレームワークfrapiでは、pecl_httpが必要だった。pecl_httpの現行ヴァージョンは2だが、ドキュメントは作者のmike(m6w6)のサイトにある。2ことHttpFoundationについては、Symfony(c)fabpotのDrupalへのアプローチなどもあり、今日様々なプロジェクトで使われている。(どのプロジェクトで利用されているかは、Packanalystを確認するとよい)。BrowserKitについてはGuzzleやBehat/Minkなどにても使われている。個人的には、BrowserKitコンポーネントのRequest/ResponseがHttpFoundationと別個に作成されているのが、ZF1の過ちを繰り返してるように見えて仕方がないが。amphpPHPで30分以内に簡単なHTTPサーバー建てられないような奴がPHP使えるって言ってるとものすごく滑稽に見えます。2013node.jsのブームを受け登場したのが、ReactPHPだ。PHPがコンセプトなプロジェクトだが、HTTPサーバーの実装としてstream_socket_serverを利用したプロジェクトとしては一番有名だろう。該当のコード行はここだなおReactPHPとは別にrdlowreyを中心としたメンバーによるamphpというプロジェクトがあることもご紹介しておきたい。2015appserverはpthreadsを用いたマルチスレッドサーバーだ。conduitHTTPメッセージが共有化されるようになるとどのような利点が生まれるだろう。いや、アプリケーションをシンプルに保っていくにはどうすればいいだろう。StackPHPは、その疑問への回答を差し示している。StackPHPはSilexの開発者としても知られるigorwほかの開発者が立ち上げたプロジェクトだ。HttpFoundationのそれが採用されている。StackPHPはRackの影響を受けている。もとにphly/conduitを作成している。PSR-7https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.htmlもし、PHPにてPSR-7が普及すればmwopが夢見ているように一つのアプリケーション内でモジュールごとに各フレームワークを導入することも可能だろう。(アプリケーションのランナーをミドルウェアに移行すればよい。)未来予測をする気はないのでミドルウェアについてはここまでとする。bufferここまででフレームワーク・ライブラリのHTTPメッセージについての取り組みを見てきた。今日PHPでアプリケーションを作成する側はレスポンスを返す際はMVCでのViewを意識している。今日のフレームワークを利用したアプリケーションでは、コントローラ・アクション内で、View変数・Viewモデルを生成しフレームワーク側へ返却したり、Responseオブジェクトを返却するのがよく見られる傾向だろう。ViewテンプレートとしてPHPそのものを利用したりテンプレートエンジンを利用している。View機構としてコード量の少ない例を見てみよう。やってレスポンスボディを生成しているだろう。dietcakehttps://github.com/dietcake/dietcake/blob/ebf0714c4df5ce3db12cb0a95949a4f0069d3707/core/view.php#L35spindle-viewhttps://github.com/spindle/spindle-view/blob/1.0.2/src/View.php#L149ほかにも有名なView機構・テンプレートエンジンのほとんどは、ob_get_contents()を利用し一旦コントローラ・レスポンスへと押し込めている。レンダーの際に、ob_get_contents()の結果をメモリに格納することの目的は以下が考えられる。内部的なキャッシュしかし、メモリ格納には巨大なレスポンスサイズでさえメモリに貯めてしまうという懸念点がある。以下のサンプルコードで確認しよう。4oh...Spindle\Viewを槍玉にあげてしまったが、ob_get_contents()を使ってる限りほとんどのフレームワーク・ライブラリはメモリを食っていくということだ。え、HTMLでここまでデカいレスポンスボディは作らないって?数百MBのCSVも画像ファイルもどうやってレスポンスボディとして表現する?PSR-7としての回答はストリームの利用だ。Stream入門あらためてPHPのストリームについて振り返ろう。hnwさんが5年前に懸念してるようにPHPのストリームの知名度は低いように感じる。しなくてもいんだろうけど。Smithのスライドがとてもよくまとまっている。socketshttp://www.slideshare.net/auroraeosrose/writing-and-using-php-streams-and-sockets-zendcon-2011Viewでの考察さし戻って、View・テンプレートエンジンでストリームの恩恵にあずかれるだろうか?spindle-viewをもとに簡単なViewコンポーネントを作成してみた。レイアウト処理については、includeをstackに積む形にすることで対応している。$temp);rewind($fp);fpassthru($fp);やった動くぞー。俺はmemory_limitの限界を超えたんだ!。(↑のコードを見て、お前ob_flush入れてるやんけ!とツッコミがあるだろうが、fpassthruされるまで出力されないという所でご理解いただきたい。)まあ、正直なところある程度複雑化されたものをStreamとして表現するのも難しい場合があるだろう。テンプレートエンジンの場合にはプリコンパイル型など事前に単純なechoの積み重ねを生成するようにするなど回避策もありうる。ストリームの利用によって、メモリ制約解決の道筋は見えてきた。含まれるServerクラスの実装を見てみよう。ここで私は以下のような質問を投げてみた。https://github.com/phly/http/issues/38あ!そうですか。いうことで、ここからはPHPの出力制御部分について確認していこう。Pauliの昨年12月のブログ記事が大変秀逸なのでそちらを参照してほしい。バイトということに注意を払ってもらいたい。fpassthru()の場合、jpauliの記事でソースが指示されたphp_output_opには↓のように掘っていけるのだろう。https://github.com/php/php-src/blob/PHP-5.6.6/ext/standard/file.c#L1422https://github.com/php/php-src/blob/PHP-5.6.6/main/streams/streams.c#L1390https://github.com/php/php-src/blob/PHP-5.6.6/main/php_output.h#L165https://github.com/php/php-src/blob/PHP-5.6.6/main/output.c#L256https://github.com/php/php-src/blob/PHP-5.6.6/main/output.c#L1053ここら辺については、また後でデバッガで確認しようとは思う。前項ではfpassthru(あるいはstream_copy_to_stream)などストリーム全体を出力することに固執して見てしまっていたが、Content-Rangeの利用などもあるわけだ。ここらへんについては、prolicのzf2用モジュールがきめ細かい対応を行っている。prolic/HumusStreamResponseSenderhttps://github.com/prolic/HumusStreamResponseSender/繰り替えし言うと、SymfonyのBinaryFileResponseのようにコンテンツ形式によってフレームワーク呼び出し側がレスポンスオブジェクトを明示的に指定してフレームワーク側へ伝えるというのはまだまだ有効だろうということだ。フレームワークへ(?)実は、mwop自体は4年ほど前にfig-libraryというプロジェクトを作りfabpotやAgaviのリーダーへ打診していた過去がある。当時のことについて記憶が確かならPSR-0が発表された程度で現在のようなレビュー・ステータスは確立されていなかったが。report?http://zend-framework-community.634137.n4.nabble.com/new-ZF-2-0-status-report-td3072742.htmlweierophinney/fig-libraryhttps://github.com/weierophinney/fig-libraryその後、bebeliのHTTPクライアントの提案の時期を経て、前述したようにmtdowlingのdraftをmwopが引き継いでいくことになる。リリース時期について発表され、PSR-7の採用・ミドルウェアアプローチについても言及された。3.0でもPSR-7が採用されることが発表された。Frameworkhttp://www.slimframework.com/2015/02/11/whats-up-with-version-3.htmlこのまま一斉に各フレームワークがPSR-7を採用することにより未来は薔薇色?。実際の普及率には、有名フレームワークの次期バージョンでの採用動向次第だろう。そもそもなぜPSR-7は成立したのか利用者側が理解していなければ意味がない。OOPは銀の弾丸ではないのだし、採用した/されただけで満足していはいけない。最後にPSR-7はPHPにとって"まっとう"なインターフェイスで、共有化されていることによるメリットもある。このインターフェス(ツール)を使うことにこだわり過ぎるのもおかしい。今はPHPとHTTPメッセージについて見つめなおす良いチャンスだ。