[PHP]urldecodeとは?URLエンコードされた文字列を安全にデコードする方法を徹底解説

PHP

はじめに

Webアプリケーションを開発していると、URLのクエリパラメータやフォーム送信データの中に、%E3%81%82+(プラス記号)のようなエンコードされた文字列が登場することがあります。これはURLエンコード(正確には application/x-www-form-urlencoded 形式のエンコード)と呼ばれるもので、URLの中で安全に使えない文字(日本語や記号、スペースなど)を別の表現に変換したものです。

このエンコードされた文字列を元に戻す(デコードする)ために使うのが urldecode() 関数です。$_GET$_POST を使う場合はPHPが自動的にデコードしてくれるため意識することは少ないですが、外部APIからのレスポンスや、手動でURLを組み立てて受け渡しする場面などでは、この関数を明示的に使う機会が出てきます。本記事では urldecode() の基本と、混同されがちな rawurldecode() との違い、実践的な活用例までを詳しく解説します。


関数概要

項目内容
関数名urldecode()
所属拡張コア関数(標準で常に利用可能)
シグネチャurldecode(string $string): string
引数$string — デコード対象のURLエンコードされた文字列
戻り値デコードされた文字列
対応バージョンPHP 4以降
対になる関数urlencode()
類似関数rawurldecode()+ をスペースに変換しない点が異なる)
OOP版の有無なし。手続き型関数のみ

デコードの流れ(イメージ図)

  エンコードされた文字列
  "%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF+PHP"
              │
              ▼
       urldecode()
              │
   ┌──────────┴──────────┐
   │ 1. "%XX" を対応するバイトに変換 │
   │ 2. "+" を半角スペースに変換      │
   └──────────┬──────────┘
              ▼
      "こんにちは PHP"

ポイントは、urldecode()+ を半角スペースとして解釈する という点です。これはRFC 1866で定義された application/x-www-form-urlencoded の仕様に基づく挙動であり、通常のパーセントエンコーディング(RFC 3986)とは異なります。この違いを理解していないと、URLパス中の + を誤ってスペースに変換してしまうバグを生むことがあります。


実践サンプル7選

例1:基本的なデコード

<?php

class BasicUrlDecoder
{
    public function decode(string $encoded): string
    {
        // %XX形式と+をそれぞれ元の文字に変換する
        return urldecode($encoded);
    }
}

$decoder = new BasicUrlDecoder();
echo $decoder->decode('%E3%81%82%E3%81%84%E3%81%86') . PHP_EOL; // "あいう"
echo $decoder->decode('hello+world') . PHP_EOL;                  // "hello world"

例2:クエリ文字列を手動でパースするクラス

<?php

class QueryStringParser
{
    /**
     * "key1=value1&key2=value2" 形式の文字列を
     * 連想配列にデコードして返す
     */
    public function parse(string $queryString): array
    {
        $result = [];
        $pairs = explode('&', $queryString);

        foreach ($pairs as $pair) {
            if ($pair === '') {
                continue;
            }
            [$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
            // キーと値それぞれをurldecode()でデコード
            $result[urldecode($key)] = urldecode($value);
        }

        return $result;
    }
}

$parser = new QueryStringParser();
print_r($parser->parse('name=%E5%A4%AA%E9%83%8E&hobby=tennis+%26+golf'));
// 出力例: ['name' => '太郎', 'hobby' => 'tennis & golf']

例3:外部APIレスポンスに含まれるエンコード済みURLを復元する

<?php

class ApiResponseNormalizer
{
    /**
     * APIレスポンス中のエンコードされたリダイレクトURLを
     * 人間が読める形に戻す
     */
    public function normalizeRedirectUrl(string $rawUrl): string
    {
        // 例: "https://example.com/search?q=%E6%9D%B1%E4%BA%AC"
        return urldecode($rawUrl);
    }
}

$normalizer = new ApiResponseNormalizer();
echo $normalizer->normalizeRedirectUrl('https://example.com/search?q=%E6%9D%B1%E4%BA%AC') . PHP_EOL;
// "https://example.com/search?q=東京"

例4:urldecodeとrawurldecodeの挙動比較

<?php

class DecodeComparator
{
    /**
     * urldecode()とrawurldecode()の違いを
     * "+"の扱いに注目して比較する
     */
    public function compare(string $encoded): array
    {
        return [
            'urldecode'    => urldecode($encoded),
            'rawurldecode' => rawurldecode($encoded),
        ];
    }
}

$comparator = new DecodeComparator();
print_r($comparator->compare('a+b%2Bc'));
// urldecode:    "a b+c"  ← "+" はスペースに、"%2B" は "+" に変換される
// rawurldecode: "a+b+c"  ← "+" はそのまま、"%2B" のみ "+" に変換される

例5:Cookie値に含まれるエンコードデータの復元

<?php

class CookiePayloadReader
{
    /**
     * URLエンコードされた状態で保存されているCookie値を
     * デコードしてJSONとしてパースする
     */
    public function readJsonPayload(string $rawCookieValue): ?array
    {
        $decoded = urldecode($rawCookieValue);
        $data = json_decode($decoded, true);

        return json_last_error() === JSON_ERROR_NONE ? $data : null;
    }
}

$reader = new CookiePayloadReader();
$encoded = urlencode(json_encode(['theme' => 'dark', 'lang' => 'ja']));
print_r($reader->readJsonPayload($encoded));
// 出力例: ['theme' => 'dark', 'lang' => 'ja']

例6:二重エンコードされた文字列を検出して安全にデコードする

<?php

class DoubleEncodeSafeDecoder
{
    /**
     * 二重にurlencodeされている可能性がある文字列を
     * 安全に1回だけデコードする(多重デコードによる事故を防ぐ)
     */
    public function decodeOnce(string $value): string
    {
        $decoded = urldecode($value);

        // 再エンコードした結果が元の値と一致する場合のみ、
        // 単純な1回のエンコードだったと判断する
        if (urlencode($decoded) === $value) {
            return $decoded;
        }

        // 一致しない場合は元の複雑な構造を保持するため、そのまま返す
        return $value;
    }
}

$decoder = new DoubleEncodeSafeDecoder();
echo $decoder->decodeOnce('%25E3%2581%2582') . PHP_EOL; // 二重エンコードの例
echo $decoder->decodeOnce('%E3%81%82') . PHP_EOL;        // 通常のエンコードの例

例7:ログファイルからURLエンコードされたパラメータを抽出・復元する

<?php

class AccessLogUrlExtractor
{
    /**
     * アクセスログの1行から "GET /path?key=value" 形式を抜き出し、
     * クエリパラメータをデコードして返す
     */
    public function extractDecodedQuery(string $logLine): array
    {
        if (!preg_match('/\?([^\s"]+)/', $logLine, $matches)) {
            return [];
        }

        $result = [];
        foreach (explode('&', $matches[1]) as $pair) {
            [$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
            $result[urldecode($key)] = urldecode($value);
        }

        return $result;
    }
}

$extractor = new AccessLogUrlExtractor();
$log = '192.168.1.1 - - [30/Jul/2026] "GET /search?q=%E6%97%A5%E6%9C%AC%E8%AA%9E&page=2 HTTP/1.1" 200';
print_r($extractor->extractDecodedQuery($log));
// 出力例: ['q' => '日本語', 'page' => '2']

関連関数との比較

関数役割urldecodeとの違い
urldecode()URLエンコードされた文字列をデコード本記事の対象。+ を半角スペースに変換する
urlencode()文字列をURLエンコードするurldecode() の対になるエンコード関数(スペースを+にする)
rawurldecode()RFC 3986準拠のデコード+ をスペースに変換せず、そのまま+として扱う
rawurlencode()RFC 3986準拠のエンコードスペースを%20にエンコードする(+にはしない)
http_build_query()配列からクエリ文字列を生成エンコード処理を内包しており、デコードとは逆方向の処理

よくある落とし穴(注意点)

  1. + の扱いを誤解する urldecode()+ を半角スペースに変換します。URLのパス部分に含まれる本来の + 記号(数式や電話番号の国番号表記など)をデコードする際は、意図せずスペースに変わってしまう可能性があるため、rawurldecode() を使うべきかどうか検討が必要です。
  2. $_GETや$_POSTは既にデコード済み PHPは $_GET$_POST に格納する時点で自動的にURLデコードを行っています。これらの値に対して重ねて urldecode() を呼び出すと、意図しない二重デコードが発生することがあります。
  3. 不正な % エンコーディングへの耐性 % の後ろに正しい16進数が続かない不正な文字列を渡した場合、PHPのバージョンによって挙動が異なることがあるため、外部から受け取った信頼できない文字列をデコードする際はバリデーションを併用することが望ましいです。
  4. マルチバイト文字のエンコーディングに注意 デコード後の文字列がUTF-8であることを前提とした処理をする場合、実際のエンコーディングが異なると文字化けの原因になります。必要に応じて mb_check_encoding() などで確認しましょう。
  5. セキュリティ上の考慮 デコードした文字列をそのままHTMLに出力すると、XSS(クロスサイトスクリプティング)の原因になります。urldecode() の結果を画面に表示する際は、必ず htmlspecialchars() などでエスケープしてください。

まとめ

観点まとめ
何をする関数かURLエンコードされた文字列(%XX形式や+)を元の文字列にデコードする
主な用途クエリ文字列の手動パース、外部APIレスポンスの復元、Cookie値の読み取りなど
特徴的な挙動+ を半角スペースに変換する(rawurldecode()との最大の違い)
対になる関数urlencode()(エンコード)、rawurldecode()(RFC 3986準拠のデコード)
注意点二重デコード、+の誤変換、デコード後のXSS対策を忘れないこと

urldecode() は一見単純な関数に見えますが、+ の扱いや二重デコードのリスクなど、実務で注意すべきポイントがいくつも存在します。用途に応じて rawurldecode() との使い分けを意識しながら、安全にURLデータを扱っていきましょう。

タイトルとURLをコピーしました