PHP Tips | 文字列処理:URL・パス系 – 配列をクエリ文字列化

PHP PHP
スポンサーリンク

「配列をクエリ文字列化」で何をしたいのか

まず、ゴールのイメージからいきます。

['q' => 'php', 'sort' => 'desc', 'page' => 2]
→ "q=php&sort=desc&page=2"

['tag' => ['php', 'laravel', 'symfony']]
→ "tag%5B0%5D=php&tag%5B1%5D=laravel&tag%5B2%5D=symfony"

['tag' => ['php', 'laravel'], 'page' => 1]
→ "tag%5B0%5D=php&tag%5B1%5D=laravel&page=1"

やりたいことはシンプルです。

「連想配列(パラメータの集合)」を、
URL の ? 以降に付けられる「クエリ文字列」に変換したい。

業務だと、こんな場面でよく使います。

検索条件の配列から検索結果ページの URL を組み立てる。
API に送るパラメータをクエリ文字列にする。
既存の URL に、追加のパラメータをマージして新しい URL を作る。

ここを手作業で "q=".$params['q']."&sort=".$params['sort']... とやり始めると、
すぐにバグとメンテ地獄になります。
PHP には、これを一発でやってくれる関数がちゃんと用意されています。


PHP の基本:http_build_query で配列 → クエリ文字列

http_build_query の基本形

クエリ文字列を組み立てる標準関数は http_build_query です。

string http_build_query(
    array|object $data,
    string $numeric_prefix = "",
    ?string $arg_separator = null,
    int $encoding_type = PHP_QUERY_RFC1738
)
PHP

初心者向けに、まずはこう覚えてください。

「配列からクエリ文字列を作りたいときは、とりあえず http_build_query($params)

これだけでかなり戦えます。

一番シンプルな例

$params = [
    'q'    => 'php',
    'sort' => 'desc',
    'page' => 2,
];

$query = http_build_query($params);

echo $query;
// q=php&sort=desc&page=2
PHP

キーと値が key=value の形になり、& でつながった文字列ができます。

値は自動的に URL エンコードされるので、
スペースや日本語が入っていても安全です。


配列パラメータ(tag[]=php みたいなやつ)もそのままいける

PHP 的な「配列のクエリ表現」

フォームから送られてくる「複数選択」の値などは、
だいたいこういう形になります。

tag[]=php&tag[]=laravel&tag[]=symfony
PHP

これを配列で表現すると、こうです。

$params = [
    'tag' => ['php', 'laravel', 'symfony'],
];
PHP

http_build_query にそのまま渡してみます。

$params = [
    'tag' => ['php', 'laravel', 'symfony'],
];

$query = http_build_query($params);

echo $query;
// tag%5B0%5D=php&tag%5B1%5D=laravel&tag%5B2%5D=symfony
PHP

tag[0][ ] が URL エンコードされて %5B %5D になっているだけで、
意味としては「tag[0]=php&tag[1]=laravel…」です。

ブラウザや PHP 側で解釈するときには、
ちゃんと配列として扱えます。

「見た目を tag[]=php にしたい」場合

もし、どうしても

tag[]=php&tag[]=laravel
PHP

という形にしたい場合は、
配列のキーを明示的に空文字にしておきます。

$params = [
    'tag' => [
        '' => 'php',
        '' => 'laravel', // 実際は上書きされるので、このやり方は現実的ではない
    ],
];
PHP

ただ、これは実用的ではありません。

実務では、

tag%5B0%5D=php&tag%5B1%5D=laravel
PHP

のような形でも問題なく動くことがほとんどなので、
http_build_query に任せる」で割り切ってしまって大丈夫です。


URL と組み合わせて使うユーティリティを作る

ベース URL + パラメータ配列 → 完成 URL

一番よくあるのは、

「ベース URL は決まっていて、そこにパラメータ配列をくっつけたい」

というパターンです。

例えば、

$baseUrl = "https://example.com/search";
$params  = ['q' => 'php', 'sort' => 'desc'];
PHP

から、

https://example.com/search?q=php&sort=desc

を作りたい。

これを毎回文字列連結でやるのは危険なので、
ユーティリティ関数にしてしまいます。

/**
 * ベース URL にクエリパラメータを付けて新しい URL を作る
 * すでにクエリが付いている場合はマージする
 */
function buildUrlWithQuery(string $baseUrl, array $params): string
{
    $parsed = parse_url($baseUrl);

    $existing = [];
    if (isset($parsed['query']) && $parsed['query'] !== '') {
        parse_str($parsed['query'], $existing);
    }

    // 既存パラメータと新しいパラメータをマージ(新しい方が優先)
    $merged = array_merge($existing, $params);

    $query = http_build_query($merged);

    // クエリなしなら、そのまま返す
    if ($query === '') {
        return $baseUrl;
    }

    // ベース URL から既存のクエリとフラグメントを取り除いて再構築
    $scheme   = $parsed['scheme'] ?? null;
    $host     = $parsed['host'] ?? null;
    $port     = $parsed['port'] ?? null;
    $path     = $parsed['path'] ?? '';
    $fragment = $parsed['fragment'] ?? null;

    $url = '';

    if ($scheme !== null) {
        $url .= $scheme . '://';
    }

    if ($host !== null) {
        $url .= $host;
    }

    if ($port !== null) {
        $url .= ':' . $port;
    }

    $url .= $path;

    $url .= '?' . $query;

    if ($fragment !== null) {
        $url .= '#' . $fragment;
    }

    return $url;
}
PHP

使い方の例です。

$base   = "https://example.com/search";
$params = ['q' => 'php', 'sort' => 'desc'];

echo buildUrlWithQuery($base, $params);
// https://example.com/search?q=php&sort=desc
PHP

すでにクエリが付いている場合も、マージしてくれます。

$base   = "https://example.com/search?page=2";
$params = ['q' => 'php'];

echo buildUrlWithQuery($base, $params);
// https://example.com/search?page=2&q=php
PHP

同じキーがある場合は、新しい方が優先されます。

$base   = "https://example.com/search?page=2";
$params = ['page' => 5, 'q' => 'php'];

echo buildUrlWithQuery($base, $params);
// https://example.com/search?page=5&q=php
PHP

ここまでできると、「URL+配列」の組み立てがかなり楽になります。


エンコードの話を少しだけ深掘りする

http_build_query は自動で URL エンコードしてくれる

http_build_query は、値を自動的に URL エンコードしてくれます。

$params = [
    'q' => 'PHP & URL encode テスト',
];

echo http_build_query($params);
// q=PHP+%26+URL+encode+%E3%83%86%E3%82%B9%E3%83%88
PHP

& やスペース、日本語などがちゃんとエンコードされています。

自分で rawurlencode などをかけてから http_build_query に渡すと、
二重エンコードになってしまうので注意してください。

「配列 → クエリ文字列」のときは、
基本的に http_build_query に丸投げで OK です。

区切り文字を変えたい場合(& 以外)

デフォルトでは & で区切られますが、
arg_separator を指定すると変えられます。

$params = ['a' => 1, 'b' => 2];

echo http_build_query($params, '', '&');  // a=1&b=2
echo http_build_query($params, '', '&'); // a=1&b=2
PHP

HTML の中に直接埋め込むときに & を使いたい場合などに使います。

ただし、初心者のうちは「デフォルトの & のまま」で十分です。


まとめ:今日からの「配列をクエリ文字列化」ユーティリティ

押さえておきたいポイントをコンパクトにまとめると、こうなります。

配列 → クエリ文字列は http_build_query 一択でよい。
値は自動で URL エンコードされるので、自分でエンコードを重ねない。
URL 全体を組み立てたいときは、「ベース URL」と「パラメータ配列」を分けて考え、parse_urlhttp_build_query で組み立てる。
既存のクエリとマージしたいときは、parse_str で既存クエリを配列にしてから array_merge する。

核になるコードは、この2つです。

// 純粋に「配列 → クエリ文字列」
$query = http_build_query($params);

// ベース URL + パラメータ配列 → 完成 URL
function buildUrlWithQuery(string $baseUrl, array $params): string
{
    $parsed = parse_url($baseUrl);

    $existing = [];
    if (isset($parsed['query']) && $parsed['query'] !== '') {
        parse_str($parsed['query'], $existing);
    }

    $merged = array_merge($existing, $params);
    $query  = http_build_query($merged);

    if ($query === '') {
        return $baseUrl;
    }

    $scheme   = $parsed['scheme'] ?? null;
    $host     = $parsed['host'] ?? null;
    $port     = $parsed['port'] ?? null;
    $path     = $parsed['path'] ?? '';
    $fragment = $parsed['fragment'] ?? null;

    $url = '';

    if ($scheme !== null) {
        $url .= $scheme . '://';
    }
    if ($host !== null) {
        $url .= $host;
    }
    if ($port !== null) {
        $url .= ':' . $port;
    }

    $url .= $path;
    $url .= '?' . $query;

    if ($fragment !== null) {
        $url .= '#' . $fragment;
    }

    return $url;
}
PHP

もし、あなたのコードの中で「? 以降を手書きで連結しているところ」があれば、そこがこのユーティリティの差し替えポイントです。

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