25

How to, using php, transform relative path to absolute URL?

0

14 Answers 14

41
function rel2abs($rel, $base)
{
    /* return if already absolute URL */
    if (parse_url($rel, PHP_URL_SCHEME) != '') return $rel;

    /* queries and anchors */
    if ($rel[0]=='#' || $rel[0]=='?') return $base.$rel;

    /* parse base URL and convert to local variables:
       $scheme, $host, $path */
    extract(parse_url($base));

    /* remove non-directory element from path */
    $path = preg_replace('#/[^/]*$#', '', $path);

    /* destroy path if relative url points to root */
    if ($rel[0] == '/') $path = '';

    /* dirty absolute URL */
    $abs = "$host$path/$rel";

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
    for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}

    /* absolute URL is ready! */
    return $scheme.'://'.$abs;
}
6
  • 8
    This is good enough most of the time, but it fails a lot of edge cases. Commented Jul 24, 2013 at 23:44
  • 1
    if relative url starts with ? it should remove query part from base before splitting relative and absolute urls
    – qdinar
    Commented Jan 3, 2016 at 15:48
  • 1
    Isn't $rel supposed to be a string with the relative URL? Why are you trying $rel[0]? It just returns an error Uninitialized string offset: 0. Commented Jan 27, 2016 at 18:46
  • 2
    This link is now dead; leads to some illegitimate site (probably malware) Commented Mar 3, 2016 at 22:58
  • 1
    fails when rel starts with "//"
    – dldnh
    Commented Feb 25, 2018 at 14:55
7

I love the code that jordanstephens provided from the link! I voted it up. l0oky inspired me to make sure that the function is port, username, and password URL compatible. I needed it for my project.

function rel2abs( $rel, $base )
{
    /* return if already absolute URL */
    if( parse_url($rel, PHP_URL_SCHEME) != '' )
        return( $rel );

    /* queries and anchors */
    if( $rel[0]=='#' || $rel[0]=='?' )
        return( $base.$rel );

    /* parse base URL and convert to local variables:
       $scheme, $host, $path */
    extract( parse_url($base) );

    /* remove non-directory element from path */
    $path = preg_replace( '#/[^/]*$#', '', $path );

    /* destroy path if relative url points to root */
    if( $rel[0] == '/' )
        $path = '';

    /* dirty absolute URL */
    $abs = '';

    /* do we have a user in our URL? */
    if( isset($user) )
    {
        $abs.= $user;

        /* password too? */
        if( isset($pass) )
            $abs.= ':'.$pass;

        $abs.= '@';
    }

    $abs.= $host;

    /* did somebody sneak in a port? */
    if( isset($port) )
        $abs.= ':'.$port;

    $abs.=$path.'/'.$rel;

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
    for( $n=1; $n>0; $abs=preg_replace( $re, '/', $abs, -1, $n ) ) {}

    /* absolute URL is ready! */
    return( $scheme.'://'.$abs );
}
2
  • but it will return the absolute filesystem path and we want the absolute url
    – siddhesh
    Commented Mar 9, 2015 at 6:30
  • This should return a URL. Is it returning a file path for you? Commented Mar 27, 2015 at 18:34
5

Added support to keep the current query. Helps a lot for ?page=1 and so on...

function rel2abs($rel, $base)
{
    /* return if already absolute URL */
    if (parse_url($rel, PHP_URL_SCHEME) != '')
        return ($rel);

    /* queries and anchors */
    if ($rel[0] == '#' || $rel[0] == '?')
        return ($base . $rel);

    /* parse base URL and convert to local variables: $scheme, $host, $path, $query, $port, $user, $pass */
    extract(parse_url($base));

    /* remove non-directory element from path */
    $path = preg_replace('#/[^/]*$#', '', $path);

    /* destroy path if relative url points to root */
    if ($rel[0] == '/')
        $path = '';

    /* dirty absolute URL */
    $abs = '';

    /* do we have a user in our URL? */
    if (isset($user)) {
        $abs .= $user;

        /* password too? */
        if (isset($pass))
            $abs .= ':' . $pass;

        $abs .= '@';
    }

    $abs .= $host;

    /* did somebody sneak in a port? */
    if (isset($port))
        $abs .= ':' . $port;

    $abs .= $path . '/' . $rel . (isset($query) ? '?' . $query : '');

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
    for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
    }

    /* absolute URL is ready! */

    return ($scheme . '://' . $abs);
}
1
  • 1
    also fails for rel starting with "//"
    – dldnh
    Commented Feb 25, 2018 at 14:57
5

A web browser uses the page URL or the base tag to resolve relative URLs.

This PHP script is designed to resolve a URL relative to a base URL.

/** 
 * Builds a complete URL from its individual components.
 *
 * @param array $parts An associative array representing the components of a URL (following the parse_url scheme).
 * @return string The constructed URL.
 */
function build_url($parts)
{
    if (empty($parts['user'])) {
        $url = $parts['scheme'] . '://' . $parts['host'];
    } elseif(empty($parts['pass'])) {
        $url = $parts['scheme'] . '://' . $parts['user'] . '@' . $parts['host'];
    } else {
        $url = $parts['scheme'] . '://' . $parts['user'] . ':' . $parts['pass'] . '@' . $parts['host'];
    }

    if (!empty($parts['port'])) {
        $url .= ':' . $parts['port'];
    }

    if (!empty($parts['path'])) {
        $url .= $parts['path'];
    }

    if (!empty($parts['query'])) {
        $url .= '?' . $parts['query'];
    }

    if (!empty($parts['fragment'])) {
        return $url . '#' . $parts['fragment'];
    }

    return $url;
}

/** 
 * Converts a relative path into an absolute path.
 *
 * @param string $path The relative path.
 * @return string The absolute path.
 */
function abs_path($path)
{
    $path_array = explode('/', $path);

    // Solve current and parent folder navigation
    $translated_path_array = array();
    $i = 0;
    foreach ($path_array as $name) {
        if ($name === '..') {
            unset($translated_path_array[--$i]);
        } elseif (!empty($name) && $name !== '.') {
            $translated_path_array[$i++] = $name;
        }
    }

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

/** 
 * Converts a relative URL into an absolute URL using a base URL for reference.
 *
 * @param string $url The relative URL.
 * @param string $base The absolute URL serving as the reference point.
 * @return string The absolute URL.
 */
function abs_url($url, $base)
{
    $url_parts = parse_url($url);
    $base_parts = parse_url($base);

    // Handle the path if it is specified
    if (!empty($url_parts['path'])) {
        // Check if the path should be appended to the base path
        if (substr($url_parts['path'], 0, 1) !== '/') {
            if (substr($base_parts['path'], -1) === '/') {
                $url_parts['path'] = $base_parts['path'] . $url_parts['path'];
            } else {
                $url_parts['path'] = dirname($base_parts['path']) . '/' . $url_parts['path'];
            }
        }

        // Make the path absolute
        $url_parts['path'] = abs_path($url_parts['path']);
    }

    // Use the base URL to populate the unfilled components up to the first filled component
    foreach (['scheme', 'host', 'path', 'query', 'fragment'] as $comp) {
        if (!empty($url_parts[$comp])) {
            break;
        }
        $url_parts[$comp] = $base_parts[$comp];
    }

    // Build and return the absolute URL
    return build_url($url_parts);
}

Test

// A base URL.
$base_url = 'https://example.com/path1/path2/path3/path4/file.ext?field1=value1&field2=value2#fragment';

// Relative URLs (underscore is used to indicate what is derived from a relative URL).
$test_urls = array(
    "http://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment", // URL
    "//_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",      // URL without scheme
    "//_example.com",                                                                        // URL with host only
    "/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",                    // URL without scheme and host
    "_path1/_path2/_file.ext",                                                               // Only path
    "./../../_path1/../_path2/file.ext#_fragment",                                           // URL with path and fragment
    "?_field1=_value1&_field2=_value2#_fragment",                                            // URL with query and fragment
    "#_fragment"                                                                             // Only fragment
);

// The expected results.
$expected_urls = array(
    "http://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://_example.com",
    "https://example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://example.com/path1/path2/path3/path4/_path1/_path2/_file.ext",
    "https://example.com/path1/path2/_path2/file.ext#_fragment",
    "https://example.com/path1/path2/path3/path4/file.ext?_field1=_value1&_field2=_value2#_fragment",
    "https://example.com/path1/path2/path3/path4/file.ext?field1=value1&field2=value2#_fragment"
);

foreach ($test_urls as $i => $url) {
    $abs_url = abs_url($url, $base_url);
    if ( $abs_url == $expected_urls[$i] ) {
        echo  "[OK] " . $abs_url . PHP_EOL;
    } else {
        echo  "[WRONG] " . $abs_url . PHP_EOL;
    }
}

Result

[OK] http://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://_example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://_example.com
[OK] https://example.com/_path1/_path2/_file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://example.com/path1/path2/path3/path4/_path1/_path2/_file.ext
[OK] https://example.com/path1/path2/_path2/file.ext#_fragment
[OK] https://example.com/path1/path2/path3/path4/file.ext?_field1=_value1&_field2=_value2#_fragment
[OK] https://example.com/path1/path2/path3/path4/file.ext?field1=value1&field2=value2#_fragment
4

I updated the function to fix relative URL starting with '//' improving execution speed.

function getAbsoluteUrl($relativeUrl, $baseUrl){

    // if already absolute URL 
    if (parse_url($relativeUrl, PHP_URL_SCHEME) !== null){
        return $relativeUrl;
    }

    // queries and anchors
    if ($relativeUrl[0] === '#' || $relativeUrl[0] === '?'){
        return $baseUrl.$relativeUrl;
    }

    // parse base URL and convert to: $scheme, $host, $path, $query, $port, $user, $pass
    extract(parse_url($baseUrl));

    // if base URL contains a path remove non-directory elements from $path
    if (isset($path) === true){
        $path = preg_replace('#/[^/]*$#', '', $path);
    }
    else {
        $path = '';
    }

    // if realtive URL starts with //
    if (substr($relativeUrl, 0, 2) === '//'){
        return $scheme.':'.$relativeUrl;
    }

    // if realtive URL starts with /
    if ($relativeUrl[0] === '/'){
        $path = null;
    }

    $abs = null;

    // if realtive URL contains a user
    if (isset($user) === true){
        $abs .= $user;

        // if realtive URL contains a password
        if (isset($pass) === true){
            $abs .= ':'.$pass;
        }

        $abs .= '@';
    }

    $abs .= $host;

    // if realtive URL contains a port
    if (isset($port) === true){
        $abs .= ':'.$port;
    }

    $abs .= $path.'/'.$relativeUrl.(isset($query) === true ? '?'.$query : null);

    // replace // or /./ or /foo/../ with /
    $re = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
    for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
    }

    // return absolute URL
    return $scheme.'://'.$abs;

}
1

If you already have Guzzle in your project you can utilize its URL implementation:

use GuzzleHttp\Psr7\UriResolver;
use GuzzleHttp\Psr7\Utils as Psr7Utils;

$baseUri = Psr7Utils::uriFor($baseUrl);
$absoluteUrl = (string) UriResolver::resolve(
    $baseUri, Psr7Utils::uriFor($relativeUrl)
);
0

Wasn't in fact the question about converting path and not url? PHP actually has a function for this: realpath(). The only thing you should be aware of are symlinks.

Example from PHP manual:

chdir('/var/www/');
echo realpath('./../../etc/passwd') . PHP_EOL;
// Prints: /etc/passwd

echo realpath('/tmp/') . PHP_EOL;
// Prints: /tmp
2
  • Could you provide an example of its usage?
    – anon
    Commented Jul 20, 2016 at 1:10
  • 4
    The result of this method is a path and not a URL. The question asks for URL.
    – Moradnejad
    Commented Jun 7, 2017 at 6:41
0

A easy way to do this is using phpUri a small php library for converting relative urls to absolute.

Usage is simple:

require_once 'phpuri.php';
$absolute = phpUri::parse( $base_path )->join( $relative_path );

You don't even need to check that the path passed to join is actually relative. If it is absolute then parse will return it.

1
  • the link is dead
    – jkoop
    Commented Sep 2, 2023 at 15:42
-1

If the relative directory already exists this will do the job:

function rel2abs($relPath, $baseDir = './')
{ 
if ('' == trim($path))
{
    return $baseDir;
    }
    $currentDir = getcwd();
    chdir($baseDir);
    $path = realpath($path);
    chdir($currentDir);
    return $path;
}
1
  • 2
    the parameter is called $relPath but you use $path throughout the function body. Correct one of those...
    – brew
    Commented May 21, 2012 at 13:04
-1

I used the same code from: http://nashruddin.com/PHP_Script_for_Converting_Relative_to_Absolute_URL but I modified It a little bit so If base url contains PORT number it returns the relative URL with port number in it.

function rel2abs($rel, $base)
{
    /* return if already absolute URL */
    if (parse_url($rel, PHP_URL_SCHEME) != '') return $rel;

    /* queries and anchors */
    if ($rel[0]=='#' || $rel[0]=='?') return $base.$rel;

    /* parse base URL and convert to local variables:
       $scheme, $host, $path */
    extract(parse_url($base));

    /* remove non-directory element from path */
    $path = preg_replace('#/[^/]*$#', '', $path);

    /* destroy path if relative url points to root */
    if ($rel[0] == '/') $path = '';

    /* dirty absolute URL // with port number if exists */
    if (parse_url($base, PHP_URL_PORT) != ''){
        $abs = "$host:".parse_url($base, PHP_URL_PORT)."$path/$rel";
    }else{
        $abs = "$host$path/$rel";
    }
    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
    for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}

    /* absolute URL is ready! */
    return $scheme.'://'.$abs;
}

Hope this helps someone!

-1

This function will resolve relative URL's to a given current page url in $pgurl without regex. It successfully resolves:

/home.php?example types,

same-dir nextpage.php types,

../...../.../parentdir types,

full http://example.net urls,

and shorthand //example.net urls

//Current base URL (you can dynamically retrieve from $_SERVER)
$pgurl = 'http://example.com/scripts/php/absurl.php';

function absurl($url) {
 global $pgurl;
 if(strpos($url,'://')) return $url; //already absolute
 if(substr($url,0,2)=='//') return 'http:'.$url; //shorthand scheme
 if($url[0]=='/') return parse_url($pgurl,PHP_URL_SCHEME).'://'.parse_url($pgurl,PHP_URL_HOST).$url; //just add domain
 if(strpos($pgurl,'/',9)===false) $pgurl .= '/'; //add slash to domain if needed
 return substr($pgurl,0,strrpos($pgurl,'/')+1).$url; //for relative links, gets current directory and appends new filename
}

function nodots($path) { //Resolve dot dot slashes, no regex!
 $arr1 = explode('/',$path);
 $arr2 = array();
 foreach($arr1 as $seg) {
  switch($seg) {
   case '.':
    break;
   case '..':
    array_pop($arr2);
    break;
   case '...':
    array_pop($arr2); array_pop($arr2);
    break;
   case '....':
    array_pop($arr2); array_pop($arr2); array_pop($arr2);
    break;
   case '.....':
    array_pop($arr2); array_pop($arr2); array_pop($arr2); array_pop($arr2);
    break;
   default:
    $arr2[] = $seg;
  }
 }
 return implode('/',$arr2);
}

Usage Example:

echo nodots(absurl('../index.html'));

nodots() must be called after the URL is converted to absolute.

The dots function is kind of redundant, but is readable, fast, doesn't use regex's, and will resolve 99% of typical urls (if you want to be 100% sure, just extend the switch block to support 6+ dots, although I've never seen that many dots in a URL).

Hope this helps,

0
-1
function url_to_absolute($baseURL, $relativeURL) {  
    $relativeURL_data = parse_url($relativeURL);

    if (isset($relativeURL_data['scheme'])) {
        return $relativeURL;
    }

    $baseURL_data = parse_url($baseURL);

    if (!isset($baseURL_data['scheme'])) {
        return $relativeURL;
    }

    $absoluteURL_data = $baseURL_data;

    if (isset($relativeURL_data['path']) && $relativeURL_data['path']) {
        if (substr($relativeURL_data['path'], 0, 1) == '/') {
            $absoluteURL_data['path'] = $relativeURL_data['path'];
        } else {
            $absoluteURL_data['path'] = (isset($absoluteURL_data['path']) ? preg_replace('#[^/]*$#', '', $absoluteURL_data['path']) : '/') . $relativeURL_data['path'];
        }

        if (isset($relativeURL_data['query'])) {
            $absoluteURL_data['query'] = $relativeURL_data['query'];
        } else if (isset($absoluteURL_data['query'])) {
            unset($absoluteURL_data['query']);
        }
    } else {
        $absoluteURL_data['path'] = isset($absoluteURL_data['path']) ? $absoluteURL_data['path'] : '/';

        if (isset($relativeURL_data['query'])) {
            $absoluteURL_data['query'] = $relativeURL_data['query'];
        } else if (isset($absoluteURL_data['query'])) {
            $absoluteURL_data['query'] = $absoluteURL_data['query'];
        }
    }

    if (isset($relativeURL_data['fragment'])) {
        $absoluteURL_data['fragment'] = $relativeURL_data['fragment'];
    } else if (isset($absoluteURL_data['fragment'])) {
        unset($absoluteURL_data['fragment']);
    }

    $absoluteURL_path = ltrim($absoluteURL_data['path'], '/');
    $absoluteURL_path_parts = array();

    for ($i = 0, $i2 = 0; $i < strlen($absoluteURL_path); $i++) {
        if (isset($absoluteURL_path_parts[$i2])) {
            $absoluteURL_path_parts[$i2] .= $absoluteURL_path[$i];
        } else {
            $absoluteURL_path_parts[$i2] = $absoluteURL_path[$i];
        }

        if ($absoluteURL_path[$i] == '/') {
            $i2++;
        }
    }

    reset($absoluteURL_path_parts);

    while (true) {
        if (rtrim(current($absoluteURL_path_parts), '/') == '.') {
            unset($absoluteURL_path_parts[key($absoluteURL_path_parts)]);

            continue;
        } else if (rtrim(current($absoluteURL_path_parts), '/') == '..') {
            if (prev($absoluteURL_path_parts) !== false) {
                unset($absoluteURL_path_parts[key($absoluteURL_path_parts)]);
            } else {
                reset($absoluteURL_path_parts);
            }

            unset($absoluteURL_path_parts[key($absoluteURL_path_parts)]);

            continue;
        }

        if (next($absoluteURL_path_parts) === false) {
            break;
        }
    }

    $absoluteURL_data['path'] = '/' . implode('', $absoluteURL_path_parts);

    $absoluteURL = isset($absoluteURL_data['scheme']) ? $absoluteURL_data['scheme'] . ':' : '';
    $absoluteURL .= (isset($absoluteURL_data['user']) || isset($absoluteURL_data['host'])) ? '//' : '';
    $absoluteURL .= isset($absoluteURL_data['user']) ? $absoluteURL_data['user'] : '';
    $absoluteURL .= isset($absoluteURL_data['pass']) ? ':' . $absoluteURL_data['pass'] : '';
    $absoluteURL .= isset($absoluteURL_data['user']) ? '@' : '';
    $absoluteURL .= isset($absoluteURL_data['host']) ? $absoluteURL_data['host'] : '';
    $absoluteURL .= isset($absoluteURL_data['port']) ? ':' . $absoluteURL_data['port'] : '';
    $absoluteURL .= isset($absoluteURL_data['path']) ? $absoluteURL_data['path'] : '';
    $absoluteURL .= isset($absoluteURL_data['query']) ? '?' . $absoluteURL_data['query'] : '';
    $absoluteURL .= isset($absoluteURL_data['fragment']) ? '#' . $absoluteURL_data['fragment'] : '';

    return $absoluteURL;
}
1
  • There are other answers that provide the OP's question, and they were posted many years ago. When posting an answer, please make sure you add either a new solution, or a substantially better explanation, especially when answering older questions. Commented May 7, 2019 at 18:10
-1

You can use this composer package to do that. https://packagist.org/packages/wa72/url

composer require wa72/url

  • Parse URL strings to objects

  • add and modify query parameters

  • set and modify any part of the url

  • test for equality of URLs with query parameters in a PHP-fashioned way

  • supports protocol-relative urls

  • convert absolute, host-relative and protocol-relative urls to relative and vice versa

-1

This make suit of @jordansstephens's answer that doesn't support absolute url begins with '//'.

function rel2abs($rel, $base)
{
    /* return if already absolute URL */
    if (parse_url($rel, PHP_URL_SCHEME) != '') return $rel;

    /* Url begins with // */
    if($rel[0] == '/' && $rel[1] == '/'){
        return 'https:' . $rel;
    }

    /* queries and anchors */
    if ($rel[0]=='#' || $rel[0]=='?') return $base.$rel;

    /* parse base URL and convert to local variables:
       $scheme, $host, $path */
    extract(parse_url($base));

    /* remove non-directory element from path */
    $path = preg_replace('#/[^/]*$#', '', $path);

    /* destroy path if relative url points to root */
    if ($rel[0] == '/') $path = '';

    /* dirty absolute URL */
    $abs = "$host$path/$rel";

    /* replace '//' or '/./' or '/foo/../' with '/' */
    $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
    for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}

    /* absolute URL is ready! */
    return $scheme.'://'.$abs;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.