PHP Tips | 文字列処理:URL・パス系 – 拡張子除去

PHP PHP
スポンサーリンク

「拡張子除去」で何をしたいのかイメージする

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

"/var/www/html/report/sales.csv"        → "/var/www/html/report/sales"
"/var/www/html/image/photo.jpeg"       → "/var/www/html/image/photo"
"archive.tar.gz"                       → "archive.tar"
"README"                               → "README"(拡張子なしなのでそのまま)
"https://example.com/files/doc.pdf"    → "https://example.com/files/doc"

やりたいことはこうです。

ファイルパスや URL から「拡張子だけを取り除いた文字列」を取り出したい。

業務だと、ファイル名から「拡張子を除いたベース名」を使って別名を作る、
拡張子を変えて派生ファイルを作る(report.csvreport.log など)、
URL のパスから「拡張子なしのルーティングキー」を取りたい、などでよく使います。

ここを自前で「最後の . より左を取る」みたいに書き始めると、
複数ドット、拡張子なし、URL などで簡単に壊れます。
PHP には、これを安全にやってくれる関数がすでにあります。


基本の主役:pathinfo の filename を使う

pathinfo の「filename」と「extension」

前回の「拡張子取得」と同じく、ここでも pathinfo が主役です。

$info = pathinfo('/var/www/html/report/sales.csv');

var_dump($info);
PHP

結果はこうでした。

array(4) {
  ["dirname"]   => "/var/www/html/report"
  ["basename"]  => "sales.csv"
  ["extension"] => "csv"
  ["filename"]  => "sales"
}
PHP

ここで注目したいのが filename です。

「拡張子を除いたファイル名部分」

がすでに取れている、ということです。

つまり、

$filename = pathinfo('/var/www/html/report/sales.csv', PATHINFO_FILENAME);

echo $filename; // sales
PHP

これが「拡張子除去」の基本になります。


ファイルパスから「拡張子を除いたパス」を作る

ディレクトリ込みで「拡張子だけ除去」したい

多くの場合、「ファイル名だけ」ではなく「パス全体から拡張子だけを取りたい」ことが多いです。

"/var/www/html/report/sales.csv"
→ "/var/www/html/report/sales"

これを安全にやるには、

  1. dirnamefilenamepathinfo で取る
  2. それを / でつなぎ直す

という形にするとスッキリします。

/**
 * ファイルパスから「拡張子を除いたパス」を取得する
 * 例: /path/to/file.csv → /path/to/file
 */
function stripExtensionFromPath(string $path): string
{
    $info = pathinfo($path);

    $dir  = $info['dirname']   ?? '';
    $name = $info['filename']  ?? '';

    if ($dir === '' || $dir === '.' || $dir === DIRECTORY_SEPARATOR) {
        // ディレクトリ情報がない(カレントディレクトリ扱い)場合は、ファイル名だけ返す
        return $name;
    }

    return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
}
PHP

使い方の例です。

echo stripExtensionFromPath('/var/www/html/report/sales.csv');
// /var/www/html/report/sales

echo stripExtensionFromPath('sales.csv');
// sales

echo stripExtensionFromPath('README');
// README(拡張子がないのでそのまま)
PHP

ここでのポイントは、「拡張子がない場合も安全に動く」ことです。
pathinfofilename は、拡張子がなくても「そのままの名前」を返してくれます。


URL から「拡張子を除いた URL」を作る

URL 全体をそのまま pathinfo に渡すのは危ない

URL をそのまま pathinfo に渡すと、
クエリやフラグメントまで含めて「パス」として扱われてしまうことがあります。

$info = pathinfo('https://example.com/files/doc.pdf?download=1');

var_dump($info['filename']); // doc.pdf?download=1 みたいな変な結果になる可能性
PHP

なので、URL の場合は必ず

  1. parse_url で「パス部分」だけを取り出す
  2. そのパスに対して pathinfo を使う
  3. 最後に URL 全体を組み立て直す

という流れにします。

パス部分の拡張子だけを除去した URL を作る

/**
 * URL の「パス部分の拡張子」だけを除去した URL を返す
 * 例: https://example.com/files/doc.pdf?x=1
 *   → https://example.com/files/doc?x=1
 */
function stripExtensionFromUrl(string $url): string
{
    $parts = parse_url($url);

    if ($parts === false) {
        // URL として解釈できない場合は、そのまま返すか、空文字にするかは設計次第
        return $url;
    }

    $scheme   = $parts['scheme']   ?? null;
    $host     = $parts['host']     ?? null;
    $port     = $parts['port']     ?? null;
    $path     = $parts['path']     ?? '';
    $query    = $parts['query']    ?? null;
    $fragment = $parts['fragment'] ?? null;
    $user     = $parts['user']     ?? null;
    $pass     = $parts['pass']     ?? null;

    // パスから拡張子を除去
    if ($path !== '') {
        $info = pathinfo($path);
        $dir  = $info['dirname']  ?? '';
        $name = $info['filename'] ?? '';

        if ($dir === '' || $dir === '/' || $dir === '.') {
            $path = '/' . $name;
        } else {
            $path = rtrim($dir, '/') . '/' . $name;
        }
    }

    // URL を再構築
    $result = '';

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

    if ($user !== null) {
        $result .= $user;
        if ($pass !== null) {
            $result .= ':' . $pass;
        }
        $result .= '@';
    }

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

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

    $result .= $path;

    if ($query !== null && $query !== '') {
        $result .= '?' . $query;
    }

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

    return $result;
}
PHP

使い方の例です。

echo stripExtensionFromUrl('https://example.com/files/doc.pdf');
// https://example.com/files/doc

echo stripExtensionFromUrl('https://example.com/files/doc.pdf?download=1');
// https://example.com/files/doc?download=1

echo stripExtensionFromUrl('https://example.com/files/');
// https://example.com/files (拡張子がないのでそのまま)

echo stripExtensionFromUrl('https://example.com');
// https://example.com
PHP

ここでの重要ポイントは、「クエリやフラグメントはそのまま残す」ことです。
あくまで「パスの最後の要素の拡張子だけ」を削っています。


「複数ドット」のファイル名をどう扱うか

archive.tar.gz → archive.tar にするのが基本

pathinfofilename は、「最後のドットより左」を返します。

$info = pathinfo('archive.tar.gz');

echo $info['filename'];   // archive.tar
echo $info['extension'];  // gz
PHP

つまり、「拡張子除去」を filename ベースでやると、

"archive.tar.gz" → "archive.tar"

になります。

実務では、これで十分なことが多いです。

  • .gz を外して、.zip に変えたい」
  • .gz を外して、ログファイル名にしたい」

など、「最後の拡張子だけを意識したい」ケースが多いからです。

もし、「.tar.gz まで含めて一気に外したい」などの要件があるなら、
前回の「複合拡張子」の話のように、別途ルールを決めて処理する必要があります。


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

拡張子だけ変えて派生ファイル名を作る

例えば、CSV ファイルからログファイル名を作りたいケース。

$csvPath = '/var/www/html/report/sales_2025.csv';

$base = stripExtensionFromPath($csvPath);

$logPath = $base . '.log';

echo $logPath;
// /var/www/html/report/sales_2025.log
PHP

「拡張子だけ変えたい」という処理は、
業務バッチやレポート生成でよく出てきます。

URL から「ルーティングキー」を取りたい

例えば、こんな URL をルーティングに使っているとします。

/entry/123.html
/entry/456.html

ここから「123」「456」だけ取りたい。

$url  = '/entry/123.html';
$path = 'https://dummy' . $url; // parse_url 用のダミー

$noExtUrl = stripExtensionFromUrl($path);
// https://dummy/entry/123

$noExtPath = parse_url($noExtUrl, PHP_URL_PATH);
// /entry/123

$id = basename($noExtPath);
// 123
PHP

少しステップは多いですが、「拡張子を気にせず ID を取りたい」ときに、
こういう組み合わせ方もできます。


まとめ:今日からの「拡張子除去」ユーティリティ

大事なところだけ、ぎゅっとまとめます。

拡張子除去は、pathinfo($path, PATHINFO_FILENAME) を使うのが基本。
パス全体から拡張子だけを除去したいときは、「dirnamefilename をつなぎ直す」。
URL の場合は、parse_url でパス部分だけを取り出してから pathinfo を使い、最後に URL を再構築する。
複数ドットのファイル名は、「最後の拡張子だけを外す(archive.tar.gz → archive.tar)」のが現実的な落としどころ。

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

function stripExtensionFromPath(string $path): string
{
    $info = pathinfo($path);
    $dir  = $info['dirname']  ?? '';
    $name = $info['filename'] ?? '';

    if ($dir === '' || $dir === '.' || $dir === DIRECTORY_SEPARATOR) {
        return $name;
    }

    return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
}

function stripExtensionFromUrl(string $url): string
{
    $parts = parse_url($url);
    if ($parts === false) {
        return $url;
    }

    $path = $parts['path'] ?? '';

    if ($path !== '') {
        $info = pathinfo($path);
        $dir  = $info['dirname']  ?? '';
        $name = $info['filename'] ?? '';

        if ($dir === '' || $dir === '/' || $dir === '.') {
            $path = '/' . $name;
        } else {
            $path = rtrim($dir, '/') . '/' . $name;
        }
    }

    $scheme   = $parts['scheme']   ?? null;
    $host     = $parts['host']     ?? null;
    $port     = $parts['port']     ?? null;
    $query    = $parts['query']    ?? null;
    $fragment = $parts['fragment'] ?? null;
    $user     = $parts['user']     ?? null;
    $pass     = $parts['pass']     ?? null;

    $result = '';

    if ($scheme !== null) {
        $result .= $scheme . '://';
    }
    if ($user !== null) {
        $result .= $user;
        if ($pass !== null) {
            $result .= ':' . $pass;
        }
        $result .= '@';
    }
    if ($host !== null) {
        $result .= $host;
    }
    if ($port !== null) {
        $result .= ':' . $port;
    }

    $result .= $path;

    if ($query !== null && $query !== '') {
        $result .= '?' . $query;
    }
    if ($fragment !== null && $fragment !== '') {
        $result .= '#' . $fragment;
    }

    return $result;
}
PHP

もし、あなたのコードの中で「substrstrrpos('.') で拡張子を削っているところ」があれば、そこがこのユーティリティの差し替えポイントです。

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