tech.chakapoko.com
Home / PHP / ファイル操作

[PHP]ファイルパスを正規化する('.' や '..' を取り除く)

パス文字列の中の '.' や '..' を取り除いたり、シンボリックリンクを展開してパスを正規化するには realpath 関数が使えます。

echo realpath('/etc/./passwd') . "\n"; // => /etc/passwd

ただし、 realpath 関数はファイルが存在しない場合には FALSE を返します。

ファイルが存在しない場合にもパスを正規化するには自前で関数を定義します。

以下のコードはファイルの存在有無に関わらずパスを正規化するサンプルコードです。シンボリックリンクの展開には対応していませんが、'.' や '..' は取り除いてくれます。

<?php
function normalize_path($path)
{
    // 絶対パスでなければカレントディレクトリとつなげる
    if (strpos($path, '/') !== 0) {
        $path = getcwd() . '/' . $path;
    }

    // パスを '/' で区切って分けた配列
    $fragments = [];
    // パースのための一時変数
    $fragment = '';
    for ($i = 0, $l = strlen($path); $i < $l; $i++) {
        $c = substr($path, $i, 1);

        // '\' はエスケープ文字
        // 見つけたら次の文字が '/' でもパスの区切り文字とは見なさない
        if ($c === '\\') {
            $fragment .= $c;
            $i++;
            $c = substr($path, $i, 1);
            $fragment .= $c;
            continue;
        }

        // '/' を見つけたとき
        if ($c === '/') {

            // './' や '/' の連続は無視する
            if ($fragment === '' || $fragment === '.') {
                $fragment = '';
                continue;
            }

            // '..' だったらディレクトリをさかのぼる
            if ($fragment === '..') {
                array_pop($fragments);
                $fragment = '';
                continue;
            }

            // 配列に追加する
            $fragments[] = $fragment;
            $fragment = '';
            continue;
        }

        $fragment .= $c;
    }
    $fragments[] = $fragment;

    return '/' . implode('/', $fragments);
}


echo normalize_path('/var/www/html') . "\n";
echo normalize_path('/var/www/./html') . "\n";
echo normalize_path('/var/www/../html') . "\n";
echo normalize_path('/var/www/../../html') . "\n";

このコードを実行すると次のような出力となります。

/var/www/html
/var/www/html
/var/html
/html