PHPには、URLのエンコード / デコードの関数が2種類あります。urlencode() / urldecode() と rawurlencode() / rawurldecode()。
使う目的は同じなんですが、じゃあどっちを使うの? という話です。
エンコードとは何か? デコードとは何か?
エンコードはプログラミングだけで使う言葉ではありません。通信分野でもよく使います。
ざっくりとかんたんに言えば、エンコードはデータを別のデータに変換することでデコードは復元すること。
URLのエンコードとは、URLをHTMLに埋め込むときに正しくURLとして認識される文字列へ変換することを言います。
(ブラウザの上部に表示されるURLもエンコードされた文字列。)
HTMLにあるURLは、画像を表示したり別ページへ移動したりするのに使いますが、そのためにはURLでは使っちゃいけない文字があり、それを別の文字へ変換しなければなりません。
(たとえば半角スペースや日本語のような全角文字など。)
そのためにエンコード処理を行います。
エンコードとデコード
エンコード(encode)
通信データなどをある規則にしたがって符号化する。
デコード(decode)
エンコード(符号化)されたデータを復元する。
urlencode() / urldecode()
urlencode() は、半角英数と半角記号の3つ(.-_)はそのままにして、それ以外の文字はすべて2桁の16進数表記に変換します。その16進表記には先頭に%を付けて。
ただしひとつ特長があって、半角スペースはプラス(+)に変換します。
なんで + なのか? と言われれば、昔からそうなってるとしか言いようがありません。ルールというより慣習が広まって一般化したといったところでしょう。
PHPの公式ドキュメントでも『歴史的背景により』という言葉で表現されています。
<?php
$url = "https://sample.test/?q='サンプル'";
echo urlencode($url) . PHP_EOL;
$url = "https://sample.test/?q='_-+*'";
echo urlencode($url) . PHP_EOL;
$url = "https://sample.test/?q='sample sample'";
echo urlencode($url) . PHP_EOL;
https%3A%2F%2Fsample.test%2F%3Fq%3D%27%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27_-%2B%2A%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27sample+sample%27
rawurlencode() / rawurldecode()
一方、rawurlencode() は、RFCが策定した仕様文書に基づいた変換方式です。半角スペース以外はurlencode() と同じ。
半角スペースは16進数表記で '%20' に変換します。
あっ、もうひとつあった。そのまま表示する半角記号(.-_)にチルダ(~)が追加されています。
ちがいはこの2つだけ。
RFC (Request for Comments)
IETFが標準化を推し進めるために公開している仕様の文書のこと。
日本語訳文書は、企業や社団法人などの団体が公開しているが、全網羅されたものはない。
また、訳の正確性も担保されてないので、原文(英語)を読むことが推奨されている。
和訳を参考する分には構わないが、そのときは原文のチェックも忘れずに。ただ、今の翻訳機能はかなりのものなので原文を読むほうが早い。
IETF(Internet Engineering Task Force, インターネット技術特別調査委員会)
インターネットに関する仕様の標準化を推し進めているNPO。
<?php
$url = "https://sample.test/?q='サンプル'";
echo urlencode($url) . PHP_EOL;
echo rawurlencode($url) . PHP_EOL . PHP_EOL;
$url = "https://sample.test/?q='_-~+*'";
echo urlencode($url) . PHP_EOL;
echo rawurlencode($url) . PHP_EOL . PHP_EOL;
$url = "https://sample.test/?q='sample sample'";
echo urlencode($url) . PHP_EOL;
echo rawurlencode($url) . PHP_EOL . PHP_EOL;
https%3A%2F%2Fsample.test%2F%3Fq%3D%27%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27_-%7E%2B%2A%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27_-~%2B%2A%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27sample+sample%27
https%3A%2F%2Fsample.test%2F%3Fq%3D%27sample%20sample%27
RFCには仕様によって番号が振られており、URLエンコードの仕様はRFC3986です。
RFC3986はURIの一般的な構文に関する仕様。
どっちを使うべきか?
じゃあどっちを使えばいいんだという話ですが、どっちでもいい。エンコードしたデータを全く同じルールでデコードすれば問題ありません。
答えになってませんか?
一般的には rawurlencode() がいいでしょう。きちんと仕様策定されたものがベターです。
慣習はルールじゃないので守らない自由がありますが、仕様から外れるには確たる理由が必要です。
仕事でも、urlencode() を使うと怒られることはあっても、rawurlencode() で怒られることはないでしょう。
『urlencode() を使って』と言われてないかぎりは。
そうはいってもurlencode() の廃止はなかなか難しいです。世界中にあまたあるサイトのリンクが壊れるのは非現実的なので。
静的コードチェックでは警告が出ることも
PHP_CodeSniffer(phpcs)を使ってWordPressコーディング規約でチェックをするとこのような警告が出ます。
urlencode() should only be used when dealing with legacy applications rawurlencode() should now be used instead. See http://php.net/manual/en/function.rawurlencode.php and http://www.faqs.org/rfcs/rfc3986.html
訳)
urlencode()はレガシーアプリケーションを扱う場合にのみにすべきで、代わりにrawurlencode()を使うべきです。
http://php.net/manual/en/function.rawurlencode.php および http://www.faqs.org/rfcs/rfc3986.html を参照してください。
筆者訳
レガシーアプリケーションとは、遺跡と呼ぶくらい古いということで、もうこれ以上進化はないと意味。
わざわざ、urlencode() を使う理由はないと言ってるのも同然です。
ボクは積極的に警告を消す作業をしています。自分が作ってるものがレガシーではないので。
もちろん、変更による影響はないことの確認は必要です。
urlencode() は非推奨じゃない
WordPressコーディング規約ではエラーではなく警告です。絶対に使っちゃダメにはなっていません。
PHP公式ドキュメントでも、urlencode() に非推奨のメッセージはありません。それだけレガシーコードが世の中に残ってるんでしょう。
最後に大事なことを忘れてました。urlencode() / rawurlencode() でエンコードしたデータの復元関数は対になっているものを使わないといけません。
変換方式がちがうんだから、使い方を間違えると復元コードがおかしくなります。
エンコード (変換) | デコード (復元) |
---|---|
urlencode() | urldecode() |
rawurlencode() | rawurldecode() |
PHP公式ドキュメント