PHP Tips | 文字列処理:URL・パス系 – URL からドメイン取得

PHP PHP
スポンサーリンク

「URL からドメイン取得」で何をしたいのか

まずゴールをはっきりさせます。

"https://example.com/path"              → "example.com"
"https://sub.example.co.jp/page"       → "sub.example.co.jp"
"http://localhost:8080/test"           → "localhost"
"https://192.168.0.10/app"             → "192.168.0.10"

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

「URL 文字列から、ホスト部分(ドメイン)だけを安全に取り出したい」

ということです。

業務・実務では、例えばこんな場面で使います。

外部リンクが「許可されたドメインかどうか」をチェックしたい。
ログに記録された URL から、どのドメインへのアクセスが多いか集計したい。
リダイレクト先 URL が、自社ドメイン配下かどうかを検証したい。

このとき、文字列操作で無理やり https:// を削ったり、/ で explode したりすると、すぐに壊れます。
ここは素直に「URL をちゃんと分解する関数」を使うのが正解です。


PHP の基本:parse_url で URL を分解する

parse_url のイメージ

PHP には、URL を構造的に分解してくれる parse_url があります。

$url = "https://sub.example.co.jp:8443/path/to/page?x=1#top";

$parts = parse_url($url);

var_dump($parts);
PHP

だいたいこんな配列になります。

array(6) {
  ["scheme"]   => "https"
  ["host"]     => "sub.example.co.jp"
  ["port"]     => 8443
  ["path"]     => "/path/to/page"
  ["query"]    => "x=1"
  ["fragment"] => "top"
}
PHP

この中の host が、いわゆる「ドメイン部分」です。

なので、基本形はこうなります。

$host = parse_url($url, PHP_URL_HOST);
PHP

PHP_URL_HOST を第2引数に渡すと、「欲しい要素だけ」を直接返してくれるので便利です。


一番シンプルな「ドメイン取得」ユーティリティ

まずは「ホストをそのまま返す」関数

初心者向けに、まずはこれくらいの関数から始めると分かりやすいです。

/**
 * URL からホスト(ドメイン)を取得する
 * 取得できない場合は null を返す
 */
function getHostFromUrl(string $url): ?string
{
    $host = parse_url($url, PHP_URL_HOST);

    if ($host === null || $host === false || $host === '') {
        return null;
    }

    return $host;
}
PHP

使い方の例です。

echo getHostFromUrl("https://example.com/path");          // example.com
echo getHostFromUrl("https://sub.example.co.jp/page");    // sub.example.co.jp
echo getHostFromUrl("http://localhost:8080/test");        // localhost
echo getHostFromUrl("https://192.168.0.10/app");          // 192.168.0.10
var_dump(getHostFromUrl("not a url"));                    // null
PHP

ここで大事なのは、「失敗したときに null を返す」という設計です。

URL でない文字列が来る可能性は、実務では普通にあります。
そのときに「空文字」や「false」をそのまま返すと、呼び出し側でバグの温床になります。

?string(null 許容)にしておくと、「取れなかった」という状態をはっきり表現できます。


実務向けにもう一歩:小文字にそろえておく

ホスト名は大文字小文字を区別しない

URL のホスト名は、大文字小文字を区別しません。

https://Example.com/path

Example Domain

これらは同じ場所を指します。

なので、「比較」や「ホワイトリストチェック」に使うときは、
小文字にそろえておくと扱いやすくなります。

/**
 * URL からホスト(ドメイン)を取得し、小文字にそろえる
 */
function getNormalizedHostFromUrl(string $url): ?string
{
    $host = parse_url($url, PHP_URL_HOST);

    if ($host === null || $host === false || $host === '') {
        return null;
    }

    return strtolower($host);
}
PHP

使い方のイメージです。

echo getNormalizedHostFromUrl("https://Example.COM/path"); // example.com
PHP

これで、「example.com」と「Example.COM」を同じものとして扱えるようになります。


「許可されたドメインかどうか」を判定する例

ホワイトリストチェックの典型パターン

例えば、「自社ドメイン配下の URL だけリダイレクトを許可したい」というケース。

$allowedHost = 'example.com';

$url  = $_GET['redirect'] ?? '';
$host = getNormalizedHostFromUrl($url);

if ($host === null) {
    // URL としておかしい → 拒否
}

if ($host !== $allowedHost) {
    // 許可されていないドメイン → 拒否
}

// OK ならリダイレクト
header('Location: ' . $url);
exit;
PHP

ここでのポイントは二つです。

一つ目は、「URL として解釈できないものは即座に拒否」していること。
二つ目は、「ホスト名を小文字にそろえてから比較」していること。

これだけで、「Example.com」「EXAMPLE.COM」などの表記ゆれに悩まされずに済みます。


「サブドメインも許可したい」場合の考え方

example.com とその配下(sub.example.com など)を許可する

よくある要件として、

「example.com と、そのサブドメイン(sub.example.com など)も許可したい」

というものがあります。

その場合は、こういう判定になります。

/**
 * URL が、指定ドメイン配下かどうかを判定する
 * 例: example.com → example.com, sub.example.com を許可
 */
function isUrlUnderDomain(string $url, string $allowedDomain): bool
{
    $host = getNormalizedHostFromUrl($url);
    if ($host === null) {
        return false;
    }

    $allowedDomain = strtolower($allowedDomain);

    if ($host === $allowedDomain) {
        return true;
    }

    // 末尾が ".example.com" なら OK
    return str_ends_with($host, '.' . $allowedDomain);
}
PHP

使い方の例です。

var_dump(isUrlUnderDomain("https://example.com/page", "example.com"));        // true
var_dump(isUrlUnderDomain("https://sub.example.com/page", "example.com"));    // true
var_dump(isUrlUnderDomain("https://evil-example.com/page", "example.com"));   // false
PHP

ここでの重要ポイントは、「末尾が .example.com かどうか」で判定していることです。

evil-example.com のような「紛らわしい別ドメイン」を誤って許可しないために、
'.' . $allowedDomain でチェックしているのがミソです。


注意点:URL っぽいけどスキームがない文字列

parse_url は「スキームなし」の文字列もそれっぽく分解してしまう

例えば、こういう文字列。

$url = "example.com/path";
$host = parse_url($url, PHP_URL_HOST);
var_dump($host); // null
PHP

スキーム(http://https://)がないと、parse_url は「ホスト」として認識しません。

実務では、ユーザーが「https:// を付けずに URL を入力してくる」ことがよくあります。

その場合は、「足りないスキームを補ってから parse_url する」という前処理を入れることもあります。

function getHostFromMaybeUrl(string $value): ?string
{
    // すでにスキームが付いているかチェック
    if (!preg_match('#^[a-zA-Z][a-zA-Z0-9+.-]*://#', $value)) {
        // なければ https:// を仮で付ける
        $value = 'https://' . $value;
    }

    return getHostFromUrl($value);
}
PHP

これで、

echo getHostFromMaybeUrl("example.com/path");        // example.com
echo getHostFromMaybeUrl("https://example.com/a");   // example.com
PHP

のように扱えるようになります。

ただし、「本当に URL として扱っていい文字列かどうか」の判断は別問題なので、
この関数を使う場面は慎重に選んでください。


まとめ:今日からの「URL からドメイン取得」ユーティリティ

押さえておきたいポイントをコンパクトにまとめます。

URL からドメインを取るときは、文字列操作ではなく parse_url を使う。
parse_url($url, PHP_URL_HOST) でホストだけを取り出し、失敗したら null を返す設計にする。
比較やホワイトリストチェックに使うなら、小文字にそろえたホスト名を使う。
サブドメインも許可したい場合は、「$host === $domain または str_ends_with($host, '.' . $domain)」という形で判定する。

核になるコードは、このくらいです。

function getHostFromUrl(string $url): ?string
{
    $host = parse_url($url, PHP_URL_HOST);
    if ($host === null || $host === false || $host === '') {
        return null;
    }
    return $host;
}

function getNormalizedHostFromUrl(string $url): ?string
{
    $host = getHostFromUrl($url);
    return $host === null ? null : strtolower($host);
}
PHP

もし、あなたのプロジェクトの中で「外部リダイレクト先 URL をそのまま信じて使っているところ」があれば、そこがこのユーティリティの出番です。

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