Bulgaria PHP Conference 2019

realpath

(PHP 4, PHP 5, PHP 7)

realpath正規化された絶対パス名を返す

説明

realpath ( string $path ) : string

realpath() は、 入力 path のシンボリックリンクをすべて展開し、 /.//../ および / などの参照をすべて解決することにより、正規化された絶対パスを返します。

パラメータ

path

調べたいパス。

注意:

path の指定は必須ですが、空の文字列を指定することもできます。 その場合はカレントディレクトリを指定したものとみなします。

返り値

成功した場合は、正規化された絶対パス名を返します。 返されるパスはシンボリックリンク、/./ および /../ 要素を含みません。 パスの末尾の区切り文字 (\/ など) は削除されます。

realpath() は、 たとえばファイルが存在しないなどの失敗時に FALSE を返します。

注意:

指定した階層にあるすべてのディレクトリに対して、 実行中のスクリプトからの実行権限が必要です。もし権限がなければ realpath()FALSE を返します。

注意:

大文字小文字を区別しないファイルシステムの場合は、realpath() が大文字小文字をどちらかにそろえるかもしれないし、そろえないかもしれません。

注意:

realpath() 関数は、Phar の内部にあるファイルに対しては機能しません。 そのようなパスは、実際のパスではなく仮想パスになるからです。

注意: PHP の数値型は符号付整数であり、 多くのプラットフォームでは 32 ビットの整数を取るため、 ファイルシステム関数の中には 2GB より大きなファイルについては期待とは違う値を返すものがあります。

変更履歴

バージョン 説明
5.3.0 これより前のバージョンでは、*BSD システムでは realpath() は最後の path コンポーネントのみが存在しない場合には失敗となりませんでした。 このバージョン以降では失敗となります。
5.2.1 これより前のバージョンでは、path が空文字列あるいは NULL の場合に realpath()FALSE を返していました。

例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

参考

  • basename() - パスの最後にある名前の部分を返す
  • dirname() - 親ディレクトリのパスを返す
  • pathinfo() - ファイルパスに関する情報を返す

add a note add a note

User Contributed Notes 14 notes

up
55
Sven Arduwie
11 years ago
Because realpath() does not work on files that do not
exist, I wrote a function that does.
It replaces (consecutive) occurences of / and \\ with
whatever is in DIRECTORY_SEPARATOR, and processes /. and /.. fine.
Paths returned by get_absolute_path() contain no
(back)slash at position 0 (beginning of the string) or
position -1 (ending)
<?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);
    }
?>

A test:
<?php
    var_dump
(get_absolute_path('this/is/../a/./test/.///is'));
?>
Returns: string(14) "this/a/test/is"

As you can so, it also produces Yoda-speak. :)
up
40
runeimp at gmail dot com
6 years ago
Needed a method to normalize a virtual path that could handle .. references that go beyond the initial folder reference. So I created the following.
<?php

function normalizePath($path)
{
   
$parts = array();// Array to build a new path from the good parts
   
$path = str_replace('\\', '/', $path);// Replace backslashes with forwardslashes
   
$path = preg_replace('/\/+/', '/', $path);// Combine multiple slashes into a single slash
   
$segments = explode('/', $path);// Collect path segments
   
$test = '';// Initialize testing variable
   
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);
}
?>

Will convert /path/to/test/.././..//..///..///../one/two/../three/filename
to ../../one/three/filename
up
23
Anonymous
7 years ago
Note: If you use this to check if a file exists, it's path will be cached, and returns true even if the file is removed (use file_exists instead).
up
14
David Beck
12 years ago
Here's a function to canonicalize a URL containing relative paths. Ran into the problem when pulling links from a remote page.

<?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); //http://www.example.com/else

?>
up
6
https://stackoverflow.com/users/1397947/
2 years ago
It should probably be expressly noted that tilde expansion is not performed by realpath.
up
7
Jrg Wagner
9 years ago
Please be aware that this function does NOT always strip a trailing slash!:

LINUX (tested with PHP 5.2.11):
---
realpath('.')
: string = "/myhttpdfolder"
realpath('./')
: string = "/myhttpdfolder"
realpath('fileadmin')
: string = "/myhttpdfolder/fileadmin"
realpath('fileadmin/')
: string = "/myhttpdfolder/fileadmin"

WINDOWS (tested with PHP 5.2.5):
---
realpath('.')
: string = "C:\\myhttpdfolder"
realpath('./')
: string = "C:\\myhttpdfolder\\"
realpath('fileadmin')
: string = "C:\\myhttpdfolder\\fileadmin"
realpath('fileadmin/')
: string = "C:\\myhttpdfolder\\fileadmin\\"
up
5
pulstar at ig dot com dot br
14 years ago
Sometimes you may need to refer to the absolute path of a file in your website instead of a relative path, but the realpath() function returns the path relative to the server's filesystem, not a path relative to your website root directory.

For example, realpath() may return something like this:

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

You can't use this in an HTML document, because the web server will not find the file. To do so, you can use:

<?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>';

?>

It will return something like:

<img src="/dir1/relative/path/to/file.ext" border=1>
up
8
imagiro
7 years ago
Here is a small and handy method to calculate the relative path from $from to $to. Note: On Windows it does not work when $from and $to are on different drives.

<?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);
}
?>
up
4
Vincent Par
4 years ago
Beware of relative symbolic links like this one (ext4 file system on Ubuntu) :

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

In this case, realpath may return 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)
?>

But you can fix it by clearing the realpath cache, this way :

<?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"
?>
up
2
eion at robbmob dot com
3 years ago
Be aware that realpath() doesn't work with hidden Windows UNC paths, eg  \\servername\share$\folder\blah.txt but other PHP file-functions can access that file fine.
up
5
php at keith tyler dot com
7 years ago
Note that under Windows, a slash-rooted path will resolve on the local drive, and *not* necessarily C:\.

For example:

M:\>php -r "print realpath('/AUTOEXEC.BAT');"
[prints nothing, because there is no M:\AUTOEXEC.BAT]

But:

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

Same script, different response depending on current drive.

I'm inclined to argue that this function *should* use the value of %SystemDrive% as the "slash root" base.
up
1
Lars Scheithauer <l dot scheithauer at gmx dot de>
14 years ago
This function is also nice to test for security-breaches. You can forbid the script to access files below a certain directory to prevent "../../../etc/shadow" and similar attacks:

<?php

// declare the basic directory for security reasons
// Please do NOT attach a "/"-suffix !
$basedir = '/var/www/cgi-bin/scriptfolder';

// compare the entered path with the basedir
$path_parts = pathinfo($_REQUEST['file_to_get']);
if (
realpath($path_parts['dirname']) != $basedir) {
   
/* appropriate action against crack-attempt*/
   
die ('coding good - h4x1ng bad!');
}

?>

The url "script.php?file_to_get=../../../etc/shadow" will now result in an error.
up
1
Leonard Challis
6 years ago
When using realpath (and similar functions) remember that PHP will take in to account open_basedir restrictions. So, if you do something like:

<?php
// test.php in httpdocs folder
$path = realpath(dirname(__FILE__) . '/../application');
?>

where your open_basedir setting is set to the httpdocs folder and tmp, this will return false. You must set it to the level above (or off) for this to work.
up
1
plamen at dragiyski dot org
5 months ago
realpath() is just a system/library call to actual realpath() function supported by OS. It does not work on a path as a string, but also resolves symlinks. The resulting path might significantly differs from the input even when absolute path is given. No function in this notes resolves that.

The suggestion on the realpath man page is to look for an existing parent directory. Here is an example:
<?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;
    }
?>

The result will retrieve absolute path for non-existing relative path. Even if a path does not exists, there should be existing directory somewhere, for which the realpath could be obtained. If this is not within the relative path (i.e. even current working directory does not exists), getcwd() will retrieve absolute path, so some absolute path is returned (although in that case the PHP process could have huge problems).
To Top