PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。

725 views
1,102 views

Published on

PHPカンファレンス福岡2016『HTTPメッセージ - PHPで扱う場合の再入門』

http://psr7.net/sasezaki/phpconfuk2016/

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
725
On SlideShare
0
From Embeds
0
Number of Embeds
458
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。

  1. 1. PHP、おまえだったのか。 いつもHTTPメッセージを 運んでくれたのは。 PHPカンファレンス福岡2016 HTTPメッセージ – PHPであつかう場合の再入門 @sasezaki
  2. 2. ● ※ PHPカンファレンス福岡2016にて利用したスライド です。http://psr7.net/sasezaki/phpconfuk2016/ も 参照ください ● 概要 PHPがWebのために開発され利用され20年。直観的な ものであったPHPのAPIも複雑性を生み出す一因となっ てしまいました。抽象化の理解とその導入が煩雑さを 避けるために必要となります。HTTPメッセージ利用に ついて、PHP自体の機能とPHPプロジェクトでのアー キテクチャの変遷を踏まえつつ、開発者が意識してお きたい点を話したいと思います。SAPI・ストリーム・ 出力バッファリング・Middlewareについて考えていき ます。
  3. 3. 4.0 よりちょっと前の頃の話 (~2000年)
  4. 4. CGI Common Gateway Interface
  5. 5. use CGI qw(:standard); print header; print start_html('Form Example'), h1('My Example Form'), start_form, "Name:", textfield('name'), p, "Age:", textfield('age'), p, submit, end_form; if (param()) { print "Hi ", em(param('name')), "You are ", em(param('age')), "years old "; } print end_html; HTMLフォーム (Perl CGI.pm利用) 『例解PHP』(2001年) 序文より (Rasmus Lerdorf)
  6. 6. 『当時筆者が感じたことと言えば、Perlインター プリタを毎回起動するオーバーヘッドが馬鹿に ならないことと、CGIプログラムをCで書くのは とても退屈な作業だということでした。しか し、それでもいろいろなプログラムをCで書いて るうちに、同じコードを何度も繰り返し書いて いることに気付きました。つまり、CGIスクリプ トのHTML部分とCコードを分離する簡単なラッ パーさえあれば、HTMLを変更してもCコードを 再コンパイルしなくて済む悟ったわけです。』 『例解PHP』(2001年) 序文より (Rasmus Lerdorf)
  7. 7. <HTML><header><title>Form Example</title></header> <body><h1>My Example Form</h1> <form action="<?echo $PHP_SELF?>" method="POST"> Name : <input type="text" name="name"> Age : <input type="text" name="age"> <input type="submit"> </form> <?if ($name):?> Hi <?echo $name?>, you are <?echo $age?> years old <?endif?> </body></html> HTMLフォーム (PHP) 『例解PHP』(2001年) 序文より (Rasmus Lerdorf)
  8. 8. <HTML><header><title>Form Example</title></header> <body><h1>My Example Form</h1> <form action="<?echo $PHP_SELF?>" method="POST"> Name : <input type="text" name="name"> Age : <input type="text" name="age"> <input type="submit"> </form> <?if ($name):?> Hi <?echo $name?>, you are <?echo $age?> years old <?endif?> </body></html> HTMLフォーム (PHP) 『例解PHP』(2001年) 序文より (Rasmus Lerdorf) XSS!
  9. 9. PHPにはその昔、 リクエスト値をとことん楽に 扱える機能に register globalsや マジッククオートといった ものがありました。。
  10. 10. 忘れろ!
  11. 11. HTTPメッセージを平文で 渡してやってもええんやで。
  12. 12. サーバーリクエスト (Incoming Request ) Webサーバ HTTPリクエスト PHP スクリプトコード ・ $_SERVER,$_GET,$_POST,$_COOKIE,$_FILES ・ apache_request_headers() ・ php://input
  13. 13. サーバーリクエスト Webサーバ HTTPリクエスト PHP スクリプトコード ・ $_SERVER,$_GET,$_POST,$_COOKIE,$_FILES ・ apache_request_headers() ・ php://input request startup php_output_activate sapi activate zend_compile zend_activate zend_activate_modules
  14. 14. かくして、PHPはHTTPリクエ ストと末永く暮らしていきま したとさ。 めでたし。めでた.....
  15. 15. Warning: POST Content-Length of ... bytes exceeds the limit of ヒー
  16. 16. PHP、おまえだったのか。 いつもpost_max_sizeから かばってくれていたのは
  17. 17. HTTPリクエスト Webサーバ HTTPリクエスト PHP スクリプトコード ・ $_SERVER,$_GET,$_POST,$_COOKIE,$_FILES ・ apache_request_headers() ・ php://input スクリプト実行前に、ini値の post_max_sizeを チェックし、最大値をこえる場合 は、$_POST・$_FILESの値は空 sapi activate
  18. 18. 最大サイズ・最大長などの制限を設けてる ● HashDosで知られるmax_input_varsの導入はPHP 5.3.9から ● もちろん、サーバソフト側でも制限設定項目はあり ● ApacheのLimitRequestBody ディレクティブなど POSTメソッドでの場合の$_POSTへの変換 ● sapi_activateでのsapi_read_post_dataのコールにて取得 php-src/main/SAPI.c 参照 ● php.ini のenable_post_data_reading にて$_POST や $_FILESへ の格納を無効化可能 (PHP 5.4より) $_SERVERとリクエストヘッダー ● ヘッダーが"HTTP_”プリフィクスなどはCGIの環境変数由来 ● Just In Timeでの利用時でのグローバル変数としての評価 http://php.net/ini.core#ini.auto-globals-jit “有効にした場合、SERVER および ENV 変数はスクリプトの開始時ではなく、 最 初に使用された時 (Just In Time)に作成される。”( php_variables.cのphp_startup_auto_globals など参照) サーバーリクエストとスーパーグローバル
  19. 19. PHPのひょうじゅんAPI つらぽよ
  20. 20. E_WARNING: Cannot modify header information - headers already sent by (output started at ...
  21. 21. スーパーグローバルを直に触るのはつらい header()を先に呼ばなきゃいけないのがつらい ファイル配列操作がつらい リクエストURIの操作がつらい 『PSR-7: HTTP Message Meta Document』から意訳&抜粋
  22. 22. アプリケーション実装者が行いたいことは、 リクエストを受け取って、レスポンスを生成する
  23. 23. function dispatch($request,$response);
  24. 24. HTTP リクエスト ディスパッチャ ( index.php ) ブートストラップ コントローラ ルーティング リクエスト レスポンス View / テンプレート リクエストルーター利用でのプロジェクト構成(例)
  25. 25. かくして、PHP開発者はHTTP メッセージと末永く暮らしていき ましたとさ。 めでたし。めでた.....
  26. 26. Fatal error: Allowed memory size of 6291456 bytes exhausted (tried to allocate 2097153 bytes) ぶー
  27. 27. PHP、おまえだったのか。 出力バッファリング制御をお こなってくれてたのは
  28. 28. メッセージボディは、 文字列で決定やな。 * * @return string */ public function getContent() { HTTPメッセージコンポーネント設計者 ・・・なんや問題あるんか?
  29. 29. 何が問題?   メモリを制限なく消費する可能性 → ヘッダやボディに最大長についてRFC規定あっただろうか? 例えばApacheの場合、LimitRequestBodyにて許可バイト数設定 開発者は諦めてアクションでecho → getContent()にてコールバックを許容した場合、  戻り値の方が一致しない Streamリソース利用の発想が抜けている → HTTPクライアントとして、ストリームは利用しているのに...
  30. 30. SAPIでの出力データの流れ(概略イメージ) request_shut_down php_output_end_all php_output_stack_pop php_output_write
  31. 31. レスポンスボディの出力・フラッシュ 標準の設定では、echoがあれば直ちに送信や文字列すべてを出力 はしない パフォーマンスのためにデフォルトのphp.iniでは output_buffering = 4096に設定されている。※ CLIは除く ob_start() コールバック関数 ob_start() での引数、またはphp.iniでのoutput_handlerの指定 により、出力バッファの内容を操作できる。 ob_gzhandler()関数など テンプレートエンジン / Viewレンダラーでの応用 出力内容を文字列として取得するために、ob_start() ob_get_contents()を行っている。 ここでもメモリ使用量増大の可能性 出力出力バッファリング制御制御
  32. 32. せや、streamや! * * @return stream */ public function getContent() { HTTPメッセージコンポーネント設計者
  33. 33. 抽象化 I/O PHP 4.3から登場したresourceオブジェクト 普段のファイルシステムやhttpなどのスキーマは、デフォルトの ラッパーにすぎない 入出力ストリーム php://temp により、メモリならびにテンポラリファイルへの読 み書きが行える ストリームフィルタ ストリームはカスタムフィルタを作成し、登録できる PHPPHPにおにおけるStreamStream
  34. 34. レスポンス作成時の例外・エラー処理レスポンス作成時の例外・エラー処理  ストリームや出力バッファリング制御 の利用によ り、メモリ利用量を抑えレスポンス出力時に処理を 実行することは可能でしょう。  ただし、レスポンスボディ作成時の例外(DBコネク ションエラーなど)などを考慮すると一度テンポラリーに 書き出しておき、そのストリームリソースを再度渡 すなどの対応も考慮すべきでは。
  35. 35. HTTPメッセージ コンポーネントが おおすぎる
  36. 36. • CakeNetwork{Request,Response} • CI_Input, CI_Output • NetteHttp{Request,Response} • PHPixieHTTP{Request,Responses} • SymfonyComponentHttpFoundation{Request,Response} • yiiweb{Request,Response} • ZendHttp{Request,Response} Rob Allen 『HTTP, PSR-7 and Middleware』から
  37. 37. 依存を抑え、 リクエスト・レスポンスを扱う処理 を相互運用するには?
  38. 38. HTTP レスポンス ディスパッチャ ブートストラップ コントローラ ルーティング リクエスト レスポンス View / テンプレート セッション(独自の) Authentication ミドルウェア・ ランナー MiddlewareMiddleware Middleware Middleware ミドルウェア利用でのプロジェクト構成例 while (! $stream->eof()) { echo $stream->read(8192); }
  39. 39. リクエストを受け取って、レスポンスを合成し、 次の処理へ渡す
  40. 40. function __invoke( ServerRequestInterface $request, ResponseInterface $response, callable $next);
  41. 41. psr-7はHTTPメッセージの値についてのinterfaceを定 義している 従来のレスポンスクラスでは、send()メソッドなんて用意していた サーバーリクエストには、アプリケーションでの相互 運用のために attributesプロパティが用意されている ミドルウェアシグネチャでの議論・検討の余地 従来のEventManagerを利用したプラグインとの使い分けは? HTTPクライアントでのミドルウェアはどうあるべきか? インターフェイスなどにて別途psrを定義すべきでは? PSR-7 とミドルウェア
  42. 42. PSR-7PSR-7 に対する批判に対する批判
  43. 43. 最後に ● HTTPリクエストに対し、どうHTTPレスポンスを返す か?データの流れはどのような工夫が必要だったか? PHP内部と周辺プロジェクトの状況を簡単に俯瞰して みました。 ● 普段のコーディングでは内部の動作についてはあまり 意識していないかも知れません。しかし、現実的な問 題(アプリケーションロジック)に専念するためにも、再 度振り返っていただければと思います。
  44. 44. おわりです ご清聴ありがとうございました
  45. 45. イラスト素材 ● http://www.wanpug.com/ ● http://www.irasutoya.com/ ● http://hiyokoyarou.com/
  46. 46. 参考文献 ● php と sapi と zendengine2 と.. http://www.slideshare.net/do_aki/php-and-sapi-and-zendengine2-and ● PHP による hello world入門 http://tech.respect-pal.jp/php-helloworld/ ● PHP output buffer in deep http://jpauli.github.io/2014/12/19/php-output-buffer-in-deep.html ● PSR-7: HTTP message interfaces http://www.php-fig.org/psr/psr-7/ ● PSR-7: HTTP Message Meta Document http://www.php-fig.org/psr/psr-7/meta/ ● A Case for Higher Level PHP Streams in PSR-7 http://mtdowling.com/blog/2014/07/03/a-case-for-higher-level-php-streams/
  47. 47. For Further Reading - HTTP - ● 『ハイパフォーマンス ブラウザネットワーキング――ネットワーク アプリケーションのためのパフォーマンス最適化』 https://www.oreilly.co.jp/books/9784873116761/ ● HTTP/2 Frequently AskedQuestions http://http2.info/faq.html ● なぜH2Oを作るのか 〜HTTP/2の未踏性〜 https://www.youtube.com/watch?v=ykp0fZigChs ● 新しいHTTPの話をしよう (HTTP/1.1 RFCs) http://blog.hmm.jp/entry/new-http1.1-rfcs ● メタ変数群 HTTP_* (CGI) https://wiki.suikawiki.org/n/HTTP_$1022
  48. 48. For Further Reading - PHP - ● The Php Life Cycle http://www.slideshare.net/laruence/the-php-life-cycle ● Phpをいじり倒す10の方法 http://www.slideshare.net/moriyoshi/php10 ● php.ini-recommendedで、variables_orderがGPCSである理由 と、PHP5のauto_globals_jit http://d.hatena.ne.jp/i_ogi/20071217/1197912203
  49. 49. For Further Reading - PHPのストリーム - ● ZF-6736: Allow writing the response body into a stream http://framework.zend.com/issues/browse/ZF-6736 ● PHP Iterators and Streams are awesome http://fabien.potencier.org/php-iterators-and-streams-are-awesome.html ● Writing and using php streams and sockets http://www.slideshare.net/auroraeosrose/writing-and-using-php-streams-and- sockets-zendcon-2011 ● PHP stream for beginners http://hnw.jp/pdf/phpcon-20100925.pdf ● Good Parts of PHP and theUNIX Philosophys http://www.slideshare.net/taketyan/good-parts-of-php-and-the-unix-philosophy ● Prototype for an object oriented streams API in PHP https://github.com/DaveRandom/php-streams
  50. 50. For Further Reading - ミドルウェア - ● 次世代の Rack や WSGI を考えてみる http://qiita.com/kwatch/items/67657fef43666479bb99 ● HttpKernel middlewares https://igor.io/2013/02/02/http-kernel-middlewares.html ● beberlei/http-client-middleware https://github.com/beberlei/http-client-middleware ● Overview and Features - Expressive https://zendframework.github.io/zend-expressive/getting-started/features/

×