PHP Conference Japan 2024

realpath

(PHP 4, PHP 5, PHP 7, PHP 8)

realpath返回正規化的絕對路徑名稱

說明

realpath(字串 $path): 字串|false

realpath() 會展開所有符號連結,並解析輸入 path 中對 /.//../ 和額外 / 字元的參照,然後返回正規化的絕對路徑名稱。

參數

path

正在檢查的路徑。

注意事項:

雖然必須提供路徑,但值可以是空字串。在這種情況下,該值會被解譯為目前的目錄。

回傳值

如果成功,則返回正規化的絕對路徑名稱。產生的路徑將不包含符號連結、/.//../ 組件。結尾的定界符,例如 \/,也會被移除。

如果失敗,例如檔案不存在,realpath() 會返回 false

注意事項:

執行腳本必須對階層中所有目錄具有執行權限,否則 realpath() 將返回 false

注意事項:

對於不區分大小寫的檔案系統,realpath() 可能會也可能不會正規化字元大小寫。

注意事項:

realpath() 函式不適用於 Phar 檔案中的檔案,因為此類路徑是虛擬路徑,而不是真實路徑。

注意事項:

在 Windows 上,目錄的接合點和符號連結只會展開一層。

注意 由於 PHP 的整數類型是有符號的,且許多平台使用 32 位元整數,因此某些檔案系統函式對於大於 2GB 的檔案可能會返回非預期的結果。

範例

範例 #1 realpath() 範例

<?php
chdir
('/var/www/');
echo
realpath('./../../etc/passwd') . PHP_EOL;

echo
realpath('/tmp/') . PHP_EOL;
?>

以上範例將輸出

/etc/passwd
/tmp

範例 #2 Windows 上的 realpath()

在 Windows 上,realpath() 會將 Unix 樣式路徑變更為 Windows 樣式。

<?php
echo realpath('/windows/system32'), PHP_EOL;

echo
realpath('C:\Program Files\\'), PHP_EOL;
?>

以上範例將輸出

C:\WINDOWS\System32
C:\Program Files

另請參閱

新增註釋

使用者貢獻的註釋 17 則註釋

Sven Arduwie
16 年前
因為 realpath() 不適用於不存在的檔案,
所以我寫了一個可以使用的函式。
它會將(連續)出現的 / 和 \\ 替換為
無論 DIRECTORY_SEPARATOR 中是什麼,都能夠正確處理 /. 和 /.. 。
get_absolute_path() 返回的路徑不包含
在位置 0(字串開頭)或
位置 -1(結尾)的反斜線 (backslash)
<?php
function get_absolute_path($path) {
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array();
foreach (
$parts as $part) {
if (
'.' == $part) continue;
if (
'..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
return
implode(DIRECTORY_SEPARATOR, $absolutes);
}
?>

測試
<?php
var_dump
(get_absolute_path('this/is/../a/./test/.///is'));
?>
返回:string(14) "this/a/test/is"

如您所見,它也會產生 Yoda 語法。 :)
runeimp at gmail dot com
11 年前
我需要一個方法,可以正規化虛擬路徑,並且能處理 `..` 即使它指向超過初始資料夾的範圍。所以我寫了以下程式碼。
<?php

function normalizePath($path)
{
$parts = array();// 建立一個陣列來存放有效的路徑片段
$path = str_replace('\\', '/', $path);// 將反斜線取代為正斜線
$path = preg_replace('/\/+/', '/', $path);// 將多個連續的斜線合併成一個斜線
$segments = explode('/', $path);// 將路徑分割成片段
$test = '';// 初始化測試變數
foreach($segments as $segment)
{
if(
$segment != '.')
{
$test = array_pop($parts);
if(
is_null($test))
$parts[] = $segment;
else if(
$segment == '..')
{
if(
$test == '..')
$parts[] = $test;

if(
$test == '..' || $test == '')
$parts[] = $segment;
}
else
{
$parts[] = $test;
$parts[] = $segment;
}
}
}
return
implode('/', $parts);
}
?>

會將 /path/to/test/.././..//..///..///../one/two/../three/filename
轉換成 ../../one/three/filename
moreau dot marc dot web at gmail dot com
5 年前
<?php

namespace MockingMagician\Organic\Helper;

class
Path
{
/**
* There is a method that deal with Sven Arduwie proposal https://php.dev.org.tw/manual/en/function.realpath.php#84012
* And runeimp at gmail dot com proposal https://php.dev.org.tw/manual/en/function.realpath.php#112367
* @param string $path
* @return string
*/
public static function getAbsolute(string $path): string
{
// Cleaning path regarding OS
$path = mb_ereg_replace('\\\\|/', DIRECTORY_SEPARATOR, $path, 'msr');
// Check if path start with a separator (UNIX)
$startWithSeparator = $path[0] === DIRECTORY_SEPARATOR;
// Check if start with drive letter
preg_match('/^[a-z]:/', $path, $matches);
$startWithLetterDir = isset($matches[0]) ? $matches[0] : false;
// Get and filter empty sub paths
$subPaths = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'mb_strlen');

$absolutes = [];
foreach (
$subPaths as $subPath) {
if (
'.' === $subPath) {
continue;
}
// if $startWithSeparator is false
// and $startWithLetterDir
// and (absolutes is empty or all previous values are ..)
// save absolute cause that's a relative and we can't deal with that and just forget that we want go up
if ('..' === $subPath
&& !$startWithSeparator
&& !$startWithLetterDir
&& empty(array_filter($absolutes, function ($value) { return !('..' === $value); }))
) {
$absolutes[] = $subPath;
continue;
}
if (
'..' === $subPath) {
array_pop($absolutes);
continue;
}
$absolutes[] = $subPath;
}

return
((
$startWithSeparator ? DIRECTORY_SEPARATOR : $startWithLetterDir) ?
$startWithLetterDir.DIRECTORY_SEPARATOR : ''
).implode(DIRECTORY_SEPARATOR, $absolutes);
}

/**
* Examples
*
* echo Path::getAbsolute('/one/two/../two/./three/../../two'); => /one/two
* echo Path::getAbsolute('../one/two/../two/./three/../../two'); => ../one/two
* echo Path::getAbsolute('../.././../one/two/../two/./three/../../two'); => ../../../one/two
* echo Path::getAbsolute('../././../one/two/../two/./three/../../two'); => ../../one/two
* echo Path::getAbsolute('/../one/two/../two/./three/../../two'); => /one/two
* echo Path::getAbsolute('/../../one/two/../two/./three/../../two'); => /one/two
* echo Path::getAbsolute('c:\.\..\one\two\..\two\.\three\..\..\two'); => c:/one/two
*
*/
}
David Beck
18 年前
這是一個用於正規化包含相對路徑的 URL 的函式。在從遠端頁面擷取連結時遇到這個問題。

<?php

function canonicalize($address)
{
$address = explode('/', $address);
$keys = array_keys($address, '..');

foreach(
$keys AS $keypos => $key)
{
array_splice($address, $key - ($keypos * 2 + 1), 2);
}

$address = implode('/', $address);
$address = str_replace('./', '', $address);
}

$url = 'http://www.example.com/something/../else';
echo
canonicalize($url); //<a href="http://www.example.com/else" rel="nofollow" target="_blank">http://www.example.com/else

?>
plamen at dragiyski dot org
5 年前
realpath() 函式只是呼叫作業系統支援的 realpath() 系統/函式庫函式。它不只作用於字串路徑,還會解析符號連結。即使給定絕對路徑,結果路徑也可能與輸入路徑明顯不同。此處提到的函式皆無法解決此問題。

realpath 手冊頁上的建議是尋找現有的父目錄。以下是一個範例
<?php
function resolvePath($path) {
if (
DIRECTORY_SEPARATOR !== '/') {
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
}
$search = explode('/', $path);
$search = array_filter($search, function($part) {
return
$part !== '.';
});
$append = array();
$match = false;
while (
count($search) > 0) {
$match = realpath(implode('/', $search));
if (
$match !== false) {
break;
}
array_unshift($append, array_pop($search));
};
if (
$match === false) {
$match = getcwd();
}
if (
count($append) > 0) {
$match .= DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $append);
}
return
$match;
}
?>

即使是針對不存在的相對路徑,這個函式也會取得絕對路徑。即使路徑不存在,也應該存在某個目錄可以透過 realpath 取得實際路徑。如果相對路徑中沒有這樣的目錄(例如,甚至目前的工作目錄也不存在),getcwd() 會取得絕對路徑,因此會回傳某個絕對路徑(雖然在這種情況下,PHP 程序可能會出現很大的問題)。
匿名
12 年前
注意:如果您使用此函式來檢查檔案是否存在,則檔案的路徑會被快取,即使檔案被移除也會回傳 true(請改用 file_exists)。
pulstar at ig dot com dot br
19 年前
有時您可能需要參考網站中檔案的絕對路徑,而不是相對路徑,但 realpath() 函式會回傳相對於伺服器檔案系統的路徑,而不是相對於網站根目錄的路徑。

例如,realpath() 可能會回傳如下內容

/home/yoursite/public_html/dir1/file.ext

您不能在 HTML 文件中使用這個路徑,因為網路伺服器找不到該檔案。您可以使用以下方法:

<?php

function htmlpath($relative_path) {
$realpath=realpath($relative_path);
$htmlpath=str_replace($_SERVER['DOCUMENT_ROOT'],'',$realpath);
return
$htmlpath;
}

echo
'<img src="',htmlpath('../../relative/path/to/file.ext'),'" border=1>';

?>

它會回傳類似以下的內容:

<img src="/dir1/relative/path/to/file.ext" border=1>
Jrg Wagner
14 年前
請注意,這個函式並不會總是移除尾端的斜線!

LINUX (使用 PHP 5.2.11 測試)
---
realpath('.')
:字串 = "/myhttpdfolder"
realpath('./')
:字串 = "/myhttpdfolder"
realpath('fileadmin')
:字串 = "/myhttpdfolder/fileadmin"
realpath('fileadmin/')
:字串 = "/myhttpdfolder/fileadmin"

WINDOWS (使用 PHP 5.2.5 測試)
---
realpath('.')
:字串 = "C:\\myhttpdfolder"
realpath('./')
:字串 = "C:\\myhttpdfolder\\"
realpath('fileadmin')
:字串 = "C:\\myhttpdfolder\\fileadmin"
realpath('fileadmin/')
:字串 = "C:\\myhttpdfolder\\fileadmin\\"
https://stackoverflow.com/users/1397947/
7 年前
應該明確指出,realpath 並不會執行波浪號 (~) 展開。
Vincent Par
9 年前
小心像這樣的相對符號連結(Ubuntu 上的 ext4 檔案系統)

vincent@vincent:~/Bureau/dirscan$ readlink sandbox/roulant/voiture/cabriolet/ln-loop-relative
../..

在這種情況下,realpath 可能會返回 false

<?php
var_dump
(realpath('sandbox/roulant/voiture/cabriolet/ln-loop-relative'));
// => string(44) "/home/vincent/Bureau/dirscan/sandbox/roulant"
var_dump(realpath('sandbox/roulant/voiture/cabriolet/ln-loop-relative/moto'));
// => bool(false)
?>

但您可以透過清除 realpath 快取來修復它,方法如下:

<?php
var_dump
(realpath('sandbox/roulant/voiture/cabriolet/ln-loop-relative'));
clearstatcache(true);
var_dump(realpath('sandbox/roulant/voiture/cabriolet/ln-loop-relative/moto'));
// => string(49) "/home/vincent/Bureau/dirscan/sandbox/roulant/moto"
?>
imagiro
13 年前
這是一個簡便的方法,用於計算從 $from 到 $to 的相對路徑。注意:在 Windows 上,當 $from 和 $to 位於不同的磁碟機時,此方法無效。

<?php
function relativePath($from, $to, $ps = DIRECTORY_SEPARATOR)
{
$arFrom = explode($ps, rtrim($from, $ps));
$arTo = explode($ps, rtrim($to, $ps));
while(
count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0]))
{
array_shift($arFrom);
array_shift($arTo);
}
return
str_pad("", count($arFrom) * 3, '..'.$ps).implode($ps, $arTo);
}
?>
eion at robbmob dot com
8 年前
請注意,`realpath()` 無法處理隱藏的 Windows UNC 路徑,例如 \\servername\share$\folder\blah.txt,但其他 PHP 檔案函式可以正常存取該檔案。
Lars Scheithauer <l dot scheithauer at gmx dot de>
19 年前
這個函式也適用於測試安全性漏洞。您可以禁止腳本存取特定目錄下的檔案,以防止 "../../../etc/shadow" 和類似的攻擊。

<?php

// 基於安全考量,宣告基本目錄
// 請勿附加「/」後綴!
$basedir = '/var/www/cgi-bin/scriptfolder';

// 將輸入的路徑與基本目錄比較
$path_parts = pathinfo($_REQUEST['file_to_get']);
if (
realpath($path_parts['dirname']) != $basedir) {
/* 採取適當的措施來應對破解企圖 */
die ('coding good - h4x1ng bad!');
}

?>

現在,網址 "script.php?file_to_get=../../../etc/shadow" 將會導致錯誤。
php at keith tyler dot com
13 年前
請注意,在 Windows 系統下,以斜線開頭的路徑會在本地磁碟機上解析,而不一定是 C:\。

例如

M:\>php -r "print realpath('/AUTOEXEC.BAT');"
[沒有印出任何內容,因為沒有 M:\AUTOEXEC.BAT]

但是

M:\>C
C:\>php -r "print realpath('/AUTOEXEC.BAT');"
C:\AUTOEXEC.BAT

相同的腳本,根據目前的磁碟機產生不同的回應。

我傾向於認為這個函式*應該*使用 %SystemDrive% 的值作為「斜線根目錄」的基底。
Leonard Challis
11 年前
使用 `realpath`(以及類似的函式)時,請記住 PHP 會考慮 `open_basedir` 的限制。因此,如果您執行以下操作:

<?php
// test.php 位於 httpdocs 資料夾中
$path = realpath(dirname(__FILE__) . '/../application');
?>

如果您的 open_basedir 設定為 httpdocs 資料夾和 tmp,則此函數將返回 false。您必須將其設定為上一層(或關閉)才能使其正常運作。
belisoful at icloud dot com
1 年前
Sven Arduwie 的答案未經測試,且並未完全複製 realpath 的行為,但算是一個近似的解決方案。

這段程式碼已經過單元測試,盡可能地接近 realpath 的行為,但不需要路徑實際存在於系統中。

它會正確處理相對路徑,並基於實際的當前工作目錄來解析它們,但一切都可以是虛擬的。例如: "../someVirtualDir/./virtualFile.jpg"

<?php

public static function virtualpath($path): string
{
$path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
$len = strlen($path);
$relative = strpos($path, DIRECTORY_SEPARATOR);
if (!
$len || ($len > 0 && $path[0] == '.') || $relative !== 0) {
$path = getcwd() . DIRECTORY_SEPARATOR . $path;
}
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = [];
foreach (
$parts as $part) {
if (
'.' == $part) {
continue;
}
if (
'..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
return
DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $absolutes);
}

?>
Enzo dot Barbaguelatta at alma dot cl
2 年前
請注意,`realpath` 返回 false 的原因之一可能是路徑不存在、權限問題,或者甚至是您的作業系統的安全模組(例如 SELinux)阻止您檢查該路徑。

如果您像我一樣,在檢查 `realpath` 返回 false 的原因時不太順利,請先檢查這些重點。
To Top