PHPにおける実践的HTTPリクエストメソッド

OPTIONSとか使ってる人いるんですかね。

## HTTPリクエストメソッドとは

簡単に言うと、そのリクエストが何を目的にしているかを表明する取り決めだ。
詳しくは[別記事](http://yohei-y.blogspot.jp/2005/04/rest-5-get-post-put-delete.html)などを参照のこと。
仕様は[RFC2068](https://tools.ietf.org/html/rfc2068)、[RFC7540](https://tools.ietf.org/html/rfc7540)などで定義され、最新のHTTP/2ではGET/POST/PUT/DELETE/HEAD/OPTIONS/TRACE/CONNECTと8種類のHTTPリクエストメソッドが用意されている。
GETならデータを寄こせ、POSTなら登録しろ、DELETEなら削除しろ、とまあそんなかんじになる。

### 本来の姿

[ベストプラクティス](http://qiita.com/mserizawa/items/b833e407d89abd21ee72)では以下のようなURLが理想とされている。
　GET /tickets - チケットのリストを取得する
　GET /tickets/12 - 指定したチケットの情報を取得する
　POST /tickets - 新しいチケットを作成する
　PUT /tickets/12 - チケット #12 を更新する
　PATCH /tickets/12 - チケット #12 を部分的に更新する
　DELETE /tickets/12 - チケット #12 を削除する

まあ概ね同意ではあるのだが、
しかし残念、実はブラウザはGET/POST以外のHTTPリクエストメソッドを送ることができなかったりする。

そもそもHTMLのフォームはPUTやDELETEなどに[対応してない](http://jxck.hatenablog.com/entry/why-form-dosent-support-put-delete)うえ、[TRACE](http://qiita.com/bezeklik/items/1c4145652661cf5b2271)は速攻で無効化され、かと思えば[PATCH](https://tools.ietf.org/html/rfc5789)とかいう謎メソッドが増えてたりと、実のところGET/POST以外のHTTPリクエストメソッドはまともに使える状況ではない。

フレームワークの中には[PUT](http://docs.symfony.gr.jp/symfony2/book/http_fundamentals.html) / [DELETE](http://book.cakephp.org/3.0/ja/development/routing.html#resource-routes) / [PATCH](http://qiita.com/michiomochi@github/items/de19c560bc1dc19d698c)などに対応しているものも存在しているが、実はこれPOSTしつつhiddenで`method=put`とかのパラメータを送ってるだけという代物で、要するにPOSTだ。

## 私的お薦め送信側

普通に画面の要件に従っておけばいいよ。
要するにリンクならGETだしフォームならPOSTだ。

### GET

URLに全パラメータを含めるリクエストだ。
`index.php?a=1&b=2`みたいなやつ。

実はaタグでのリンクは通常GETしか送ることができない[^1]。
つまり、リンクをクリックしたら何かが更新されるような作りは[REST違反](http://kaihooo.com/http-method/)だ。
しかし、だからといって、何かを更新するリンクがあったとして、リンクを踏んだのをJavaScriptで感知してPOSTに変換して送るなんて処理は本末転倒も甚だしい。
パラメータに`?method=put`とか書いて普通にGET送っとけばええやろ。

### POST

パラメータをリクエストボディに含めるリクエスト。
リクエストのサイズ制限がGETよりずっと大きく、またデータがURLに含まれないので覗き見もされにくい[^2]。
formタグに`method="POST"`と書いておけばPOSTになる。
フォームをGETにする意味は全くないので、常にPOSTにしておいてかまわない。
詳細を表示するだけのボタンだってPOSTでいいぞ。
むしろ`ふぉｒｍ`で`<form method="post" enctype="multipart/form-data" action="">`って辞書登録しておくまである。

なお、POSTの場合でもactionに`?id=1`とか書けるし普通に$_GETで受け取れる。

## 私的お薦め受信側

[$_REQUEST](http://php.net/manual/ja/reserved.variables.request.php)一択。

### $_REQUEST

`$_GET`、`$_POST`、`$_COOKIE`をひとまとめで受け取ることができるスーパーグローバル変数[^3]。
送信側がリンクだろうがフォームだろうがAJAXだろうがその他のリクエストだろうがおかまいなしに、受信側は全く同じに書くことができる。
なお、PHPには`$_PUT`や`$_DELETE`などは存在しないし、[普通に受け取ることもできない](http://qiita.com/chiyoyo/items/8314d938cf596b0a90bd)。

各変数で同じキーが重複した場合、[request_order](http://php.net/manual/ja/ini.core.php#ini.request-order)に従って何れかのパラメータが優先される。
`index.php?id=1`というURLに`id=2`をPOSTした場合、request_orderに従って`$_REQUEST['id']=1`になったり`$_REQUEST['id']=2`になったりする。
想定していない方が優先されて困るという場合があるかもしれないが、そもそも`$_GET['id']`と`$_POST['id']`が違うなんて設計する方が悪いので、パラメータを見直すべきである。

なおCookieは[PHPSESSID以外使わない](http://qiita.com/rana_kualu/items/f9d87279036626278e61)から重なることはない。

### 理想と現実

現実はこんなもんだ。

　GET|POST /tickets.php?method=get - チケットのリストを取得する
　GET|POST /tickets.php?method=get&id=12 - 指定したチケットの情報を取得する
　GET|POST /tickets.php?method=post - 新しいチケットを作成する
　GET|POST /tickets.php?method=put&id=12 - チケット #12 を更新する
　GET|POST /tickets.php?method=delete&id=12 - チケット #12 を削除する

HTTPリクエストメソッドは完全に無視し、単にmethodパラメータだけで判断する。
省略したらgetにすると取り決めてもいいし、mod_rewriteで`/tickets/put/12`みたいにするのもいいだろう。
送信側でいちいちHTTPリクエストメソッドを分けたりPUTやDELETEを作り込んだりする必要もないし、はっきりいってこっちのほうが楽。

え？PATCH？PUTでええやん。

## まとめ

全部`$_REQUEST`にする。

ただし、FWを導入しているなら、FWの流儀をあえて外す必要はない。
あくまでルーティングを自作する場合の思想である。
そしてFWを使わずにルーティングを自作する機会など、今時はほとんど無い。

従って、このエントリが役立つことは、あんまり無い。


[^1]: JavaScriptを使えばPOSTでもなんでもできるがまあ。
[^2]: あくまでGETに比べればマシ程度の話であり、本気で隠蔽するならSSLを使わないといけない。
[^3]: 最近は`$_COOKIE`はデフォルトでは外されている。