PHP 的 basename() 函式用來從路徑字串中取出檔名部分,第二個參數可以去掉指定的副檔名。這個函式看起來簡單,但實際使用時有幾個地方容易踩雷。
基本用法
1
2
3
4
5
6
7
8
9
10
|
$path = '/var/www/html/images/photo.jpg';
// 取得完整檔名(含副檔名)
echo basename($path); // photo.jpg
// 取得不含 .jpg 的檔名
echo basename($path, '.jpg'); // photo
// 注意:第二個參數只是「去掉後綴字串」,不是「去掉副檔名」
echo basename($path, '.php'); // photo.jpg(沒有匹配,不會去掉)
|
第二個參數的行為容易誤解:它只是做字串比對,如果路徑的結尾不匹配指定的字串,就完全不做任何處理,而不是智慧判斷副檔名。
Linux 上的中文路徑問題
basename() 在 Linux 環境下處理含有多字節字符(如中文)的路徑時,可能會出現截斷或回傳空字串的問題。原因是 PHP 的 basename() 底層依賴 C 函式庫的 locale 設定,預設 locale 為 C(即 POSIX),不認識 UTF-8 多字節字符。
1
2
3
|
// 在 C locale 下,可能回傳空字串或截斷
$path = '/var/www/html/圖片/照片.jpg';
echo basename($path); // 可能輸出錯誤結果
|
解決方法一:設定 Locale
在使用 basename() 之前,將 locale 設定為支援 UTF-8 的語系:
1
2
3
4
5
6
|
setlocale(LC_ALL, 'zh_TW.UTF-8');
// 或
setlocale(LC_ALL, 'en_US.UTF-8');
$path = '/var/www/html/圖片/照片.jpg';
echo basename($path); // 照片.jpg(正確)
|
注意:這個設定需要伺服器上已安裝對應的 locale。可用 locale -a 確認可用的 locale 列表。
解決方法二:使用 mb_substr 自訂實作
如果無法修改伺服器 locale,可以用 Multibyte String 函式自訂一個 mb_basename:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function mb_basename(string $path, string $suffix = ''): string {
// 統一將路徑分隔符轉為 /
$path = str_replace('\\', '/', $path);
// 取最後一個 / 之後的部分
$basename = mb_substr($path, mb_strrpos($path, '/') + 1);
// 如果指定了 suffix,去掉結尾的 suffix
if ($suffix !== '' && mb_substr($basename, -mb_strlen($suffix)) === $suffix) {
$basename = mb_substr($basename, 0, mb_strlen($basename) - mb_strlen($suffix));
}
return $basename;
}
// 測試
echo mb_basename('/var/www/html/圖片/照片.jpg'); // 照片.jpg
echo mb_basename('/var/www/html/圖片/照片.jpg', '.jpg'); // 照片
|
跨平台路徑分隔符
Windows 使用反斜線 \,Linux 使用正斜線 /。basename() 在不同平台的行為不同,若程式可能跨平台執行,建議在處理路徑前先統一分隔符:
1
2
3
|
// 統一為正斜線
$path = str_replace('\\', '/', $path);
echo basename($path);
|
參考資料