PHP Tips | 文字列処理:ランダム・生成 – 短縮 ID 生成

PHP PHP
スポンサーリンク

「短縮 ID 生成」で何をしたいのかイメージする

まず、どんな場面で「短い ID」が欲しくなるかをイメージしてみましょう。

URL 短縮サービスの「短いコード」
人に読み上げやすい予約番号や受付番号
画面上に表示する「問い合わせ ID」

こういうときに、UUID のような長い文字列ではなく、

a9F3kL0z
Xy12AbCd

といった「そこそこ一意で、そこそこ短い ID」が欲しくなります。

ここで大事なのは、「短くした瞬間に、完全な一意性は捨てている」という感覚です。
つまり、UUID ほどの“絶対ぶつからない感”はない代わりに、「実務上ほぼ困らない程度の衝突確率」で割り切る、という設計になります。


短縮 ID を設計するときに考えること

どのくらいの長さにするか

長さが短いほど、衝突(同じ ID が二度出る)の確率は上がります。
例えば、英数字 62 種類を使うとします。

長さ 6 文字だと、62^6 ≒ 56 億通り
長さ 8 文字だと、62^8 ≒ 2.18 兆通り

「何件くらいのデータに対して使うのか」「衝突したらどう扱うのか」を考えながら、長さを決めます。
業務でよくあるのは、8〜12 文字くらいです。

どんな文字を使うか

URL に載せることが多いなら、英数字だけにしておくと扱いやすいです。
ここでは、次の 62 文字を使う前提で進めます。

0-9
a-z
A-Z

これで「短くて、URL にも載せやすい ID」が作れます。


コアとなる考え方:ランダム英数字列を「短縮 ID」として使う

短縮 ID の一番シンプルな作り方は、「ランダムな英数字列をそのまま ID として使う」ことです。

やることは三つだけです。

  1. 使う文字集合を決める(英数字 62 文字など)
  2. その中からランダムに文字を選んで、指定した長さ分つなげる
  3. 乱数には必ず random_int() を使う

ここで random_int() を使うのが重要です。
mt_rand() などではなく、暗号論的に安全な乱数を使うことで、「推測されにくい ID」になります。


実装:短縮 ID 生成ユーティリティ

シンプルな短縮 ID 生成関数

まずは、英数字だけを使った短縮 ID 生成関数を作ります。

/**
 * 短縮 ID を生成する(英数字のみ)
 *
 * @param int $length 生成する ID の長さ
 * @return string
 */
function generate_short_id(int $length = 8): string
{
    if ($length <= 0) {
        throw new InvalidArgumentException('length must be positive');
    }

    // 使う文字集合(記号なし)
    $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charsLength = strlen($chars);

    $result = '';

    for ($i = 0; $i < $length; $i++) {
        // 0〜$charsLength-1 の範囲で、安全な乱数を取得
        $index = random_int(0, $charsLength - 1);
        $result .= $chars[$index];
    }

    return $result;
}
PHP

使い方の例です。

echo generate_short_id();       // デフォルト8文字 例: "a9F3kL0z"
echo generate_short_id(10);     // 10文字 例: "Xy12AbCdE3"
echo generate_short_id(12);     // 12文字 例: "G7k2Pq9sL0xZ"
PHP

ここでの重要ポイントは、「長さを引数で変えられるようにしておく」ことです。
用途によって「6文字でいい」「いや 12 文字欲しい」と変わるので、ユーティリティとして柔軟にしておくと再利用しやすくなります。


UUID v4 を「短縮 ID」に変換する、という発想もある

UUID をそのまま使うのは長い、でも一意性は欲しい

すでにシステムの中で UUID v4 を使っている場合、
「内部では UUID、外に見せるのは短縮 ID」という設計もよくあります。

このときの発想はこうです。

  1. 内部 ID として UUID v4 を生成する
  2. UUID を 16進数として扱い、それを 62 進(英数字)に変換する
  3. その結果の一部、または全部を「短縮 ID」として使う

ここまでやると少し実装が重くなるので、初心者向けには「ランダム英数字列をそのまま短縮 ID として使う」方が分かりやすいです。

ただ、「内部 ID と外部 ID を分ける」という考え方自体は、実務ではかなりよく出てきます。


実務での使いどころのイメージ

URL 短縮サービス風のコード

例えば、URL 短縮サービスのようなものを作るとします。

$originalUrl = 'https://example.com/very/long/url/...';

$shortId = generate_short_id(8);

// DB に保存
$stmt = $pdo->prepare('INSERT INTO short_urls (short_id, url) VALUES (:short_id, :url)');
$stmt->execute([
    ':short_id' => $shortId,
    ':url'      => $originalUrl,
]);

$shortUrl = 'https://s.example.com/' . $shortId;
// ユーザーにはこの短い URL を見せる
PHP

アクセス時は、/a9F3kL0z のようなパスを受け取って、
short_urls テーブルから元の URL を引き当ててリダイレクトします。

問い合わせ番号や受付番号

問い合わせフォームで送信された内容に対して、
ユーザーに「問い合わせ番号」を返したい場面を考えてみます。

$ticketId = generate_short_id(10);

$stmt = $pdo->prepare('INSERT INTO tickets (ticket_id, user_id, subject, body) VALUES (:ticket_id, :user_id, :subject, :body)');
$stmt->execute([
    ':ticket_id' => $ticketId,
    ':user_id'   => $userId,
    ':subject'   => $subject,
    ':body'      => $body,
]);

echo 'お問い合わせ番号: ' . $ticketId;
PHP

ユーザーはサポートに連絡するときに、この短縮 ID を伝えれば、
オペレーター側は tickets テーブルからすぐに該当データを探せます。


衝突(同じ ID が出る)とどう付き合うか

短縮 ID は、UUID ほどの一意性はありません。
理論上は、同じ ID が二度生成される可能性があります。

実務では、次のような対策を組み合わせます。

ID を保存するときに「ユニーク制約」を張っておく(UNIQUE インデックス)。
もし INSERT 時にユニーク制約エラーが出たら、「もう一度 ID を生成し直す」。

コードのイメージはこうです。

function create_unique_short_id(PDO $pdo, int $length = 8): string
{
    while (true) {
        $id = generate_short_id($length);

        try {
            $stmt = $pdo->prepare('INSERT INTO something (short_id) VALUES (:id)');
            $stmt->execute([':id' => $id]);
            return $id;
        } catch (PDOException $e) {
            // ユニーク制約違反なら再トライ(エラーコードは環境に応じて判定)
            // それ以外のエラーなら投げ直す
        }
    }
}
PHP

こうしておけば、「衝突したらやり直す」という形で、
短縮 ID の“弱点”を現実的にカバーできます。


まとめ:今日からの「短縮 ID 生成」ユーティリティ

ポイントを整理すると、こうなります。

短縮 ID は、「完全な一意性」ではなく「実務上ほぼ困らない一意性」と「短さ」のトレードオフ。
英数字 62 文字から random_int() でランダムに選んで並べるのが、シンプルで安全な実装。
衝突の可能性はゼロではないので、DB のユニーク制約と「衝突したら再生成」の仕組みをセットで考える。

核になる関数は、これです。

function generate_short_id(int $length = 8): string
{
    if ($length <= 0) {
        throw new InvalidArgumentException('length must be positive');
    }

    $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charsLength = strlen($chars);

    $result = '';

    for ($i = 0; $i < $length; $i++) {
        $index = random_int(0, $charsLength - 1);
        $result .= $chars[$index];
    }

    return $result;
}
PHP

もし、あなたのコードの中で「mt_rand() を使って短いコードを作っている」箇所があれば、そこがこのユーティリティに差し替えるべきポイントです。
その一箇所を置き換えるだけで、「短くて、推測されにくくて、実務で使いやすい ID」が手に入ります。

PHPPHP
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました