PHP

PHPの$_GETと$_POSTは、格納時にURLデコードまでしてくれるらしい

PHP-アイキャッチ

こんにちは!ゆーたろうです。

今年も上半期最後の月になりました。

とても早いですね。

コロナが始まった1月末がそんなに前とは思えないほど、あっという間でした。

さて、そんな今日は、PHPの仕様について分かったことがあったので、その備忘録です。

URLリクエストでHTTP400エラーとなった

前の記事でも書きましたが、

現在開発中のツール、日本語住所変換ツールの作成中にこんなエラーに直面しました。

エラー内容

Warning: file_get_contents(https://translation.googleapis.com/language/translate/v2?target=en&key=APIキー&q=ピソブランコ・藤橋 506号室): failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request in 

※ピソブランコの住所は、テストデータ・ジェネレータで作成したダミー住所です。本物ではありません。

エラー内容を見ると Bad Request となっているので、リクエスト構文がおかしい様子。

その時のソースコードがこれです。


$q= $_GET['q'];

$url = "https://translation.googleapis.com/language/translate/v2?target=en&key=APIキー&q=" . $q;

echo file_get_conetnts($url);

この処理の流れとしては、まずアプリからGETでリクエストを投げて、PHP側で受け取ります。

受けとった変換文字列をTranslate APIに渡して、file_get_contentsでJSONデータを取る流れです。

※本来はチェック処理は重要なので入れてますが、この記事で残したいことではないので割愛しています。

ですが、前日はうまくいったのに今日試したら失敗していました。

例え過去成功していても「現在」動作不良なら解決しなければいけないので、調査開始です。

原因調査

調査1:PHPへ投げるアプリ側を疑う

まず、おおもとのリクエスト構文を疑い、送信元のアプリが作成しているリクエストURLを確認してみました。

→これはうまくいっていて、URLに張り付けると、ちゃんと変換されました。

調査2:エラーが出た文字列をアドレスバーに貼り付けてみる

Translate API側でどんなエラーが出ているのかを見るために、file_get_contentsがエラーとなっている「https://translation.googleapis.com/language/translate/v2?target=en&key=APIキー&q=ピソブランコ・藤橋 506号室」をアドレスバーにペーストしてみました。

結果:

{
  "data": {
    "translations": [
      {
        "translatedText": "Piso Branco, Fujihashi-Room 506",
        "detectedSourceLanguage": "ja"
      }
    ]
  }
}

なんと成功するではないですか!!

もしかしたらエンコードされてないのが原因かなと思いましたが、2バイト文字をアドレスバーに入れても問題なく通りました。

なので、ちょっと混乱しましたね。

調査3:とりあえず、q=のパラメータをURLエンコードしてみる。

思いつきで「$q= urlencode($_GET[‘q’]);」としてGETしたものをエンコードしてみました。

※このPHPにパラメータを送った、送り元であるアプリ側でももちろんURLエンコードしてGETリクエストを出しています。

すると、なんとうまくいくではありませんか!!

↑のJSONが取得できました。

原因:エンコードされていなかったから(+勝手にデコードされることを知らなかったから)

直接の原因は、エンコードされてなくてマルチバイトでリクエストを出していた

2バイト文字のままTranslate APIにアクセスしていたことが原因でした。

しかし、ちゃんとエンコードしているのになぜデコードされているのか気になりました。

だって、PHPファイル内で urldecode している記述なんてありませんもん。

しかし、現実はデコードされている。

となると、あとは怪しいのは $_GET しかありません。

早速「PHP URLエンコード $_GET 戻る」検索ワードで調べてみました。

そしたら、知恵袋に答えがありました。

根本的な原因は、$_GETがデコードまでしていることを知らなかったから

PHPで$_GETを参照する時点では、すでにURLエンコードされたデータはデコードされた…

urlencodeされて送られてくるのが自明なので、phpシステム側でurldecodeまで面倒を見てくれます。

RTFM.
http://php.net/manual/ja/reserved.variables.get.php
———- 引用ここから
注意:
GET 変数は urldecode() 関数を介して渡されます。
———- 引用ここまで

まさか、$_GET内でデコードまでしてくれているなんて、初知りでした。

私は、そうとは知らず「アプリ側でエンコードしているのだから、$_GETにも、エンコード文字列が入るよね。それをそのままTranslate APIに渡せばOK♬」

と思っていたのですが、$_GETされた時点でデコードされていたようです。

ちなみに、$_POSTもデコードまでしてくれる

$_GETがデコードまでしてくれるとなれば、$_POSTもそうかな?と思い調べてみたら、$_POSTもデコードしてくれるようです。

現在のURLがエンコードされたURLかどうかを判定する方法

PHPとしても、$_GET$_POSTなどの変数にはデコードされた状態で入っている

まとめ

原因が分かり、対策するまで約20分ほどでしたが、やはり不具合は冷や冷やしますね💦

でも、不具合の数だけ、自分のスキルが上がるのが実感できるので、失敗してくれるほうが個人的にはありがたいです。

記事と同じで、技術ノウハウがたまっていきますからね。

それにしても、HTTP通信は奥深いです。