18

If it's Path_To_DocumentRoot/a/b/c.php,should always be /a/b.

I use this:

dirname($_SERVER["PHP_SELF"])

But it won't work when it's included by another file in a different directory.

EDIT

I need a relative path to document root .It's used in web application.

I find there is another question with the same problem,but no accepted answer yet.

PHP - Convert File system path to URL

Community
  • 1
  • 1
user198729
  • 61,774
  • 108
  • 250
  • 348

5 Answers5

33

Do you have access to $_SERVER['SCRIPT_NAME']? If you do, doing:

dirname($_SERVER['SCRIPT_NAME']);

Should work. Otherwise do this:

In PHP < 5.3:

substr(dirname(__FILE__), strlen($_SERVER['DOCUMENT_ROOT']));

Or PHP >= 5.3:

substr(__DIR__, strlen($_SERVER['DOCUMENT_ROOT']));

You might need to realpath() and str_replace() all \ to / to make it fully portable, like this:

substr(str_replace('\\', '/', realpath(dirname(__FILE__))), strlen(str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']))));
Alix Axel
  • 151,645
  • 95
  • 393
  • 500
11

PHP < 5.3:

dirname(__FILE__)

PHP >= 5.3:

__DIR__

EDIT:

Here is the code to get path of included file relative to the path of running php file:

    $thispath = explode('\\', str_replace('/','\\', dirname(__FILE__)));
    $rootpath = explode('\\', str_replace('/','\\', dirname($_SERVER["SCRIPT_FILENAME"])));
    $relpath = array();
    $dotted = 0;
    for ($i = 0; $i < count($rootpath); $i++) {
        if ($i >= count($thispath)) {
            $dotted++;
        }
        elseif ($thispath[$i] != $rootpath[$i]) {
            $relpath[] = $thispath[$i]; 
            $dotted++;
        }
    }
    print str_repeat('../', $dotted) . implode('/', array_merge($relpath, array_slice($thispath, count($rootpath))));
Lukman
  • 18,462
  • 6
  • 56
  • 66
5

Here's a general purpose function to get the relative path between two paths.

/**
 * Return relative path between two sources
 * @param $from
 * @param $to
 * @param string $separator
 * @return string
 */
function relativePath($from, $to, $separator = DIRECTORY_SEPARATOR)
{
    $from   = str_replace(array('/', '\\'), $separator, $from);
    $to     = str_replace(array('/', '\\'), $separator, $to);

    $arFrom = explode($separator, rtrim($from, $separator));
    $arTo = explode($separator, rtrim($to, $separator));
    while(count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0]))
    {
        array_shift($arFrom);
        array_shift($arTo);
    }

    return str_pad("", count($arFrom) * 3, '..'.$separator).implode($separator, $arTo);
}

Examples

relativePath('c:\temp\foo\bar', 'c:\temp');              // Result: ../../
relativePath('c:\temp\foo\bar', 'c:\\');                 // Result: ../../../
relativePath('c:\temp\foo\bar', 'c:\temp\foo\bar\lala'); // Result: lala
inquam
  • 12,664
  • 15
  • 61
  • 101
2

I know this is an old question but the solutions suggested using DOCUMENT_ROOT assume the web folder structure reflects the server folder structure. I have a situation where this isn't the case. My solution is as follows. You can work out how a server address maps to a web address if you have an example from the same mapped area folders. As long as the root php file that included this file is in the same mapped area you have an example.

$_SERVER['SCRIPT_FILENAME'] is the server address of this file and $_SERVER['PHP_SELF'] is the web relative version. Given these two you can work out what the web relative version of your file is from its server address (__FILE__) as follows.

function getCurrentFileUrl() {
    $file = __FILE__;
    $script = $_SERVER['SCRIPT_FILENAME'];
    $phpself = $_SERVER['PHP_SELF'];

    // find end of section of $file which is common to $script
    $i = 0;
    while($file[$i] == $script[$i]) {
        $i++;
    }
    // remove end section of $phpself that is the equivalent to the section of $script not included in $file. 
    $phpself = substr($phpself, 0, strlen($phpself)-(strlen($script)-$i));
    // append end section of $file onto result
    $phpself .= substr($file, $i, strlen($file)-$i);

    // complete address 
    return $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_NAME'].$phpself;
}
adamfowlerphoto
  • 2,708
  • 1
  • 11
  • 24
1

I had to create something to what you need so here is the result. By giving a base directory you receive a relative path to a file starting from base directory. Function is pretty fast, 100,000 checks took 0.64s. on my server. And it works for both directories and files. It is linux compatible. Don't even try it on windows :)

     /**
     * Return a relative path to a file or directory using base directory. 
     * When you set $base to /website and $path to /website/store/library.php
     * this function will return /store/library.php
     * 
     * Remember: All paths have to start from "/" or "\" this is not Windows compatible.
     * 
     * @param   String   $base   A base path used to construct relative path. For example /website
     * @param   String   $path   A full path to file or directory used to construct relative path. For example /website/store/library.php
     * 
     * @return  String
     */
    function getRelativePath($base, $path) {
        // Detect directory separator
        $separator = substr($base, 0, 1);
        $base = array_slice(explode($separator, rtrim($base,$separator)),1);
        $path = array_slice(explode($separator, rtrim($path,$separator)),1);

        return $separator.implode($separator, array_slice($path, count($base)));
    }

Usage

You need to get relative path to file /var/www/example.com/media/test.jpg Your base path is /var/www/example.com

Use the function like this:

$relative = getRelativePath('/var/www/example.com','/var/www/example.com/media/test.jpg');

Function will return /media/test.jpg.

If you need only the /media part without a file use it like this:

$relative = dirname(getRelativePath('/var/www/example.com','/var/www/example.com/media/test.jpg'));
Artur Stępień
  • 466
  • 3
  • 18
  • the return needs to be fixed not to include separator in front, otherwise the value is absolute path, not relative path: `return implode($separator, array_slice($path, count($base)));` the relative value is `media/test.jpg`. not `/media/test.jpg`. so your code is correct to OP, but otherwise illogical :) also to make code universal, the code should verify that the given arguments share leading paths. again this not part of the OP. – glen Aug 09 '20 at 07:44