Modification to the code from:
theoriginalmarksimpson at gmail dot com
Change:
$r .= "(?:(?P<login>\w+):(?P<pass>\w+)@)?";
Replace with:
$r .= "(?:(?P<login>\w+):?(?P<pass>\w+)?@)?";
This will cover the case the only username is present in the url:
http://username@subdomain.domain.com/index.php?arg1=test#anchor
parse_url
(PHP 4, PHP 5)
parse_url — 解析 URL,返回其组成部分
说明
array parse_url
( string $url
)
本函数解析一个 URL 并返回一个关联数组,包含在 URL 中出现的各种组成部分。
本函数不是用来验证给定 URL 的合法性的,只是将其分解为下面列出的部分。不完整的 URL 也被接受,parse_url() 会尝试尽量正确地将其解析。
参数
- url
-
要解析的 URL
返回值
对严重不合格的 URL,parse_url() 可能会返回 FALSE 并发出 E_WARNING。否则会返回一个关联数组,其组成部分为(至少有一个):
- scheme - 如 http
- host
- port
- user
- pass
- path
- query - 在问号 ? 之后
- fragment - 在散列符号 # 之后
范例
Example#1 parse_url() 例子
<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
print_r(parse_url($url));
?>
上例将输出:
Array ( [scheme] => http [host] => hostname [user] => username [pass] => password [path] => /path [query] => arg=value [fragment] => anchor )
注释
Note: 本函数不能用于相对 URL。
Note: parse_url() 是专门用来解析 URL 而不是 URI 的。不过为遵从 PHP 向后兼容的需要有个例外,对 file:// 协议允许三个斜线(file:///...)。其它任何协议都不能这样。
parse_url
pbcomm at gmail dot com
26-Oct-2008 12:55
26-Oct-2008 12:55
vdklah at hotmail dot com
17-Oct-2008 05:53
17-Oct-2008 05:53
Some example that determines the URL port.
When port not specified, it derives it from the scheme.
<?php
function getUrlPort( $urlInfo )
{
if( isset($urlInfo['port']) ) {
$port = $urlInfo['port'];
} else { // no port specified; get default port
if (isset($urlInfo['scheme']) ) {
switch( $urlInfo['scheme'] ) {
case 'http':
$port = 80; // default for http
break;
case 'https':
$port = 443; // default for https
break;
case 'ftp':
$port = 21; // default for ftp
break;
case 'ftps':
$port = 990; // default for ftps
break;
default:
$port = 0; // error; unsupported scheme
break;
}
} else {
$port = 0; // error; unknown scheme
}
}
return $port;
}
$url = "http://nl3.php.net/manual/en/function.parse-url.php";
$urlInfo = parse_url( $url );
$urlPort = getUrlPort( $urlInfo );
if( $urlPort !== 0 ) {
print 'Found URL port: '.$urlPort;
} else {
print 'ERROR: Could not find port at URL: '.$url;
}
?>
gautam at rogers dot com
16-Oct-2008 02:08
16-Oct-2008 02:08
What about using something like this to safely encoding all the values that are passed in the query portion?
Example input:
http://www.example.com/?first=john&last=smith&email=john@smith.com
Result:
http://www.example.com/?first=john&last=smith&email=john%40smith.com
<?php
function safe_url($url) {
// Make sure we have a string to work with
if(!empty($url)) {
// Explode into URL keys
$urllist=parse_url($url);
// Make sure we have a valid result set and a query field
if(is_array($urllist) && isset($urllist["query"])) {
// Explode into key/value array
$keyvalue_list=explode("&",($urllist["query"]));
// Store resulting key/value pairs
$keyvalue_result=array();
foreach($keyvalue_list as $key=>$value) {
// Explode each individual key/value into an array
$keyvalue=explode("=",$value);
// Make sure we have a "key=value" array
if(count($keyvalue)==2) {
// Encode the value portion
$keyvalue[1]=urlencode($keyvalue[1]);
// Add our key and encoded value into the result
array_push($keyvalue_result,implode("=",$keyvalue));
}
}
// Repopulate our query key with encoded results
$urllist["query"]=implode("&",$keyvalue_result);
// Build the the final output URL
$url=(isset($urllist["scheme"])?$urllist["scheme"]."://":"").
(isset($urllist["user"])?$urllist["user"].":":"").
(isset($urllist["pass"])?$urllist["pass"]."@":"").
(isset($urllist["host"])?$urllist["host"]:"").
(isset($urllist["port"])?":".$urllist["port"]:"").
(isset($urllist["path"])?$urllist["path"]:"").
(isset($urllist["query"])?"?".$urllist["query"]:"").
(isset($urllist["fragment"])?"#".$urllist["fragment"]:"");
}
}
return $url;
}
?>
FredLudd at gmail dot com
04-Oct-2008 02:24
04-Oct-2008 02:24
Another shot at trying to find a better parser. I noticed that the laulibrius/theoriginalmarksimpson functions didn't quite handle the URL for the page they were displayed on. For my mirror, ca3, this is
http://ca3.php.net/manual/en/function.parse-url.php
Run it through the function and it parses to
scheme => http
login =>
pass =>
host => ca3.php.net
ip =>
subdomain => ca3
domain => php.
extension => net
port =>
path => /manual/en/function.parse
file => function.parse
that is, the file name gets a bit mangled
Rather than tweak the function's regular expression yet again, I opted to adapt a RegExp that served me well in Javascript:
function j_parseUrl($url) {
$r = "(?:([a-z0-9+-._]+)://)?";
$r .= "(?:";
$r .= "(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9a-f]{2})*)@)?";
$r .= "((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9a-f]{2})*)";
$r .= "(?::(\d*))?";
$r .= "(/(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9a-f]{2})*)?";
$r .= "|";
$r .= "(/?";
$r .= "(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+";
$r .= "(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9a-f]{2})*";
$r .= ")?";
$r .= ")";
$r .= "(?:\?((?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9a-f]{2})*))?";
$r .= "(?:#((?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9a-f]{2})*))?";
preg_match("`$r`i", $url, $match);
$parts = array(
"scheme"=>'',
"userinfo"=>'',
"authority"=>'',
"host"=> '',
"port"=>'',
"path"=>'',
"query"=>'',
"fragment"=>'');
switch (count ($match)) {
case 9: $parts['fragment'] = $match[8];
case 8: $parts['query'] = $match[7];
case 7: $parts['path'] = $match[6];
case 6: $parts['path'] = $match[5] . $parts['path'];
case 5: $parts['port'] = $match[4];
case 4: $parts['host'] = $match[3];
case 3: $parts['userinfo'] = $match[2];
case 2: $parts['scheme'] = $match[1];
}
$parts['authority'] = ($parts['userinfo']?$parts['userinfo']."@":"").
$parts['host'].
($parts['port']?":".$parts['port']:"");
return $parts;
}
This function, when fed "http://ca3.php.net/manual/en/function.parse-url.php", returns
scheme => http
userinfo =>
authority => ca3.php.net
host => ca3.php.net
port =>
path => /manual/en/function.parse-url.php
query =>
fragment =>
which is somewhat closer to my needs.
But everything should be tested against the two examples provided by RFC3986,
/* line too long for this site's commnet handler */
"foo://username:password@example.com:8042".
"/over/there/index.dtb;type=animal?name=ferret#nose"
and
"urn:example:animal:ferret:nose"
Here the native function parse_url() performs admirably on that "urn:" example. Mine fails to pick out the path ("example:animal:ferret:nose") and the laulibrius/theoriginalmarksimpson function can't decipher anything there. On the "foo:" example, both my function and parse_url() get it right, while the other examples on this page don't.
The laulibrius/theoriginalmarksimpson function delivers
scheme => foo
login => username
pass => password
host => example.com
ip =>
subdomain =>
domain => example.
extension => com
port => 8042
path => /over/there/index.dtb
file => index.dtb
As you can see, the query string ("name=ferret") and fragment ("nose") have dropped off, as well as the parameter ("type=animal").
xellisx
01-Oct-2008 11:37
01-Oct-2008 11:37
I need to parse out the query string from the referrer, so I created this function.
<?php
function parse_query($val)
{
/**
* Use this function to parse out the query array element from
* the output of parse_url().
*/
$var = html_entity_decode($var);
$var = explode('&', $var);
$arr = array();
foreach($var as $val)
{
$x = explode('=', $val);
$arr[$x[0]] = $x[1];
}
unset($val, $x, $var);
return $arr;
}
?>
ilja at radusch dot com
26-Sep-2008 07:26
26-Sep-2008 07:26
Here is an update to the glue_url() function.
It can now handle relative URLs if only 'path' is provided.
<?php
function glue_url($parsed) {
if (!is_array($parsed)) {
return false;
}
$uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : '';
$uri .= isset($parsed['user']) ? $parsed['user'].(isset($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : '';
$uri .= isset($parsed['host']) ? $parsed['host'] : '';
$uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
if (isset($parsed['path'])) {
$uri .= (substr($parsed['path'], 0, 1) == '/') ?
$parsed['path'] : ((!empty($uri) ? '/' : '' ) . $parsed['path']);
}
$uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
$uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
return $uri;
}
?>
nospam at spellingcow dot com
09-Sep-2008 05:03
09-Sep-2008 05:03
URL's in the query string of a relative URL will cause a problem
fails:
/page.php?foo=bar&url=http://www.example.com
parses:
http://www.foo.com/page.php?foo=bar&url=http://www.example.com
theoriginalmarksimpson at gmail dot com
05-Sep-2008 03:12
05-Sep-2008 03:12
A rehash of code modified by "laulibrius at hotmail dot com".
This also parses urls for hosts that don't have a domain name and just use an IP as the hostname.
The old code would assume that the IP octets were a subdomain.
So the url "http://255.255.255.255/" would return 255.255 as a subdomain of 255.255.
<?php
parseUrl($url)
{
$r = "^(?:(?P<scheme>\w+)://)?";
$r .= "(?:(?P<login>\w+):(?P<pass>\w+)@)?";
$ip="(?:[0-9]{1,3}+\.){3}+[0-9]{1,3}";//ip check
$s="(?P<subdomain>[-\w\.]+)\.)?";//subdomain
$d="(?P<domain>[-\w]+\.)";//domain
$e="(?P<extension>\w+)";//extension
$r.="(?P<host>(?(?=".$ip.")(?P<ip>".$ip.")|(?:".$s.$d.$e."))";
$r .= "(?::(?P<port>\d+))?";
$r .= "(?P<path>[\w/]*/(?P<file>\w+(?:\.\w+)?)?)?";
$r .= "(?:\?(?P<arg>[\w=&]+))?";
$r .= "(?:#(?P<anchor>\w+))?";
$r = "!$r!"; // Delimiters
preg_match($r, $url,$out);
}
?>
If you need to validate the host IP this is easier than using regex.
<?php
$parsed=parseUrl($url);
if($parsed['ip']) {
if(long2ip(ip2long($parsed['ip']))==$parsed['ip']){//validates IP
echo $parsed['ip']." is a valid host";
}
else {
echo $parsed['ip']." is not a valid host";
}
}
?>
marco panichi
23-Aug-2008 04:47
23-Aug-2008 04:47
my function catch the url written on the browser by the user and does the same thing of parse_url. but better, I think. I don't like parse_url because it says nothing about elements that it doesn't find in the url. my function instead return an empty string.
<?php
function get_url()
{
$arr = array();
$uri = $_SERVER['REQUEST_URI'];
// query
$x = array_pad( explode( '?', $uri ), 2, false );
$arr['query'] = ( $x[1] )? $x[1] : '' ;
// resource
$x = array_pad( explode( '/', $x[0] ), 2, false );
$x_last = array_pop( $x );
if( strpos( $x_last, '.' ) === false )
{
$arr['resource'] = '';
$x[] = $x_last;
}
else
{
$arr['resource'] = $x_last;
}
// path
$arr['path'] = implode( '/', $x );
if( substr( $arr['path'], -1 ) !== '/' ) $arr['path'] .= '/';
// domain
$arr['domain'] = $_SERVER['SERVER_NAME'];
// scheme
$server_prt = explode( '/', $_SERVER['SERVER_PROTOCOL'] );
$arr['scheme'] = strtolower( $server_prt[0] );
// url
$arr['url'] = $arr['scheme'].'://'.$arr['domain'].$uri;
return $arr;
}
?>
PS: I found working with explode is faster than using preg_match (I tryed with getmicrotime function and 'for' cycles).
PPS: I used array_pad to prevent any notice.
andrewtheartist at hotmail dot com
29-Jun-2008 08:28
29-Jun-2008 08:28
Here's the easiest way to get the URL to the path that your script is in (so not the actual script name itself, just the complete URL to the folder it's in)
echo "http://".$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
Cool Coyote
23-Jun-2008 08:35
23-Jun-2008 08:35
based on the "laulibrius at hotmail dot com" function, this work for relatives url only:
<?php
function parseUrl($url) {
$r = "^(?:(?P<path>[\.\w/]*/)?";
$r .= "(?P<file>\w+(?:\.\w+)?)?)\.(?P<extension>\w+)?";
$r .= "(?:\?(?P<arg>[\w=&]+))?";
$r .= "(?:#(?P<anchor>\w+))?";
$r = "!$r!";
preg_match ( $r, $url, $out );
return $out;
}
print_r(parseUrl("../test/f.aq.php?p=1&v=blabla#X1"));
?>
returns:
Array
(
[0] => ../test/faq.php?p=1&v=blabla#X1
[path] => ../test/
[1] => ../test/
[file] => faq
[2] => faq
[extension] => php
[3] => php
[arg] => p=1&v=blabla
[4] => p=1&v=blabla
[anchor] => X1
[5] => X1
)
laulibrius at hotmail dot com
17-Jun-2008 12:31
17-Jun-2008 12:31
There was one thing missing in the function dropped by "to1ne at hotmail dot com" when i tried it : domain and subdomain couldn't have a dash "-". So i add it in the regexp and the function looks like this now :
<?php
function parseUrl($url) {
$r = "^(?:(?P<scheme>\w+)://)?";
$r .= "(?:(?P<login>\w+):(?P<pass>\w+)@)?";
$r .= "(?P<host>(?:(?P<subdomain>[-\w\.]+)\.)?" . "(?P<domain>[-\w]+\.(?P<extension>\w+)))";
$r .= "(?::(?P<port>\d+))?";
$r .= "(?P<path>[\w/]*/(?P<file>\w+(?:\.\w+)?)?)?";
$r .= "(?:\?(?P<arg>[\w=&]+))?";
$r .= "(?:#(?P<anchor>\w+))?";
$r = "!$r!"; // Delimiters
preg_match ( $r, $url, $out );
return $out;
}
?>
Btw, thanks for the function, it helps me a lot.
to1ne at hotmail dot com
14-Jun-2008 02:01
14-Jun-2008 02:01
Based on the idea of "jbr at ya-right dot com" have I been working on a new function to parse the url:
<?php
function parseUrl($url) {
$r = "^(?:(?P<scheme>\w+)://)?";
$r .= "(?:(?P<login>\w+):(?P<pass>\w+)@)?";
$r .= "(?P<host>(?:(?P<subdomain>[\w\.]+)\.)?" . "(?P<domain>\w+\.(?P<extension>\w+)))";
$r .= "(?::(?P<port>\d+))?";
$r .= "(?P<path>[\w/]*/(?P<file>\w+(?:\.\w+)?)?)?";
$r .= "(?:\?(?P<arg>[\w=&]+))?";
$r .= "(?:#(?P<anchor>\w+))?";
$r = "!$r!"; // Delimiters
preg_match ( $r, $url, $out );
return $out;
}
print_r ( parseUrl ( 'me:you@sub.site.org:29000/pear/validate.html?happy=me&sad=you#url' ) );
?>
This returns:
Array
(
[0] => me:you@sub.site.org:29000/pear/validate.html?happy=me&sad=you#url
[scheme] =>
[1] =>
[login] => me
[2] => me
[pass] => you
[3] => you
[host] => sub.site.org
[4] => sub.site.org
[subdomain] => sub
[5] => sub
[domain] => site.org
[6] => site.org
[extension] => org
[7] => org
[port] => 29000
[8] => 29000
[path] => /pear/validate.html
[9] => /pear/validate.html
[file] => validate.html
[10] => validate.html
[arg] => happy=me&sad=you
[11] => happy=me&sad=you
[anchor] => url
[12] => url
)
So both named and numbered array keys are possible.
It's quite advanced, but I think it works in any case... Let me know if it doesn't...
jbr at ya-right dot com
03-May-2008 11:24
03-May-2008 11:24
This function never works the way you think it should...
Example....
<?php
print_r ( parse_url ( 'me:you@sub.site.org/pear/validate.html?happy=me&sad=you#url' ) );
?>
Returns...
Array
(
[scheme] => me
[path] => you@sub.site.org/pear/validate.html
[query] => happy=me&sad=you
[fragment] => url
)
Here my way of doing parse_url
<?php
function parseUrl ( $url )
{
$r = '!(?:(\w+)://)?(?:(\w+)\:(\w+)@)?([^/:]+)?';
$r .= '(?:\:(\d*))?([^#?]+)?(?:\?([^#]+))?(?:#(.+$))?!i';
preg_match ( $r, $url, $out );
return $out;
}
print_r ( parseUrl ( 'me:you@sub.site.org/pear/validate.html?happy=me&sad=you#url' ) );
?>
Returns...
Array
(
[0] => me:you@sub.site.org/pear/validate.html?happy=me&sad=you#url
[1] =>
[2] => me
[3] => you
[4] => sub.site.org
[5] =>
[6] => /pear/validate.html
[7] => happy=me&sad=you
[8] => url
)
Where as...
out[0] = full url
out[1] = scheme or '' if no scheme was found
out[2] = username or '' if no auth username was found
out[3] = password or '' if no auth password was found
out[4] = domain name or '' if no domain name was found
out[5] = port number or '' if no port number was found
out[6] = path or '' if no path was found
out[7] = query or '' if no query was found
out[8] = fragment or '' if no fragment was found
Nicolas Merlet - admin(at)merletn.org
14-Mar-2008 11:05
14-Mar-2008 11:05
Please note that parse_url seems not to produce always the same results when passing non-standard urls.
Eg. I was using this code since 2005 (both under PHP 4.3.10 and PHP 5.2.3) :
<?php
$p = parse_url ( 'http://domain.tld/tcp://domain2.tld/dir/file' ) ;
$d2 = parse_url ( $p['path'] ) ;
echo $d2 ; // returns '/dir/file'
?>
Of course my example is very specific, as URL is not really correct. But using parse_url was a great trick to split URL easily (without using regular expressions).
Unfortunately under PHP 5.2.0-8 (+etch10), parse_url will fail as it does not accept the slash (/) at the beginning of URL.
Here is a possible patch :
<?php
$p = parse_url ( 'http://domain.tld/tcp://domain2.tld/dir/file' ) ;
$d2 = parse_url ( substr ( $p['path'] , 1 ) ) ;
echo $d2 ; // returns '/dir/file'
?>
However this last code is not optimized at all, and should be replaced by a regular expression to split URL (so that parse_url would be no longer used).
So you should use parse_url very carefully, and verify that you pass only standard URLs...
Nick Smith
05-Sep-2007 05:32
05-Sep-2007 05:32
Note that older versions of PHP (e.g., 4.1) returned an blank string as the path for URLs without any path, such as http://www.php.net
However more recent versions of PHP (e.g., 4.4.7) don't set the path element in the array, so old code will get a PHP warning about an undefined index.
Michael Muryn
27-Aug-2007 11:51
27-Aug-2007 11:51
Another update to the glue_url function: applied the "isset" treatment to $parsed['pass'].
<?php
function glue_url($parsed)
{
if (!is_array($parsed)) return false;
$uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '' : '//') : '';
$uri .= isset($parsed['user']) ? $parsed['user'].(isset($parsed['pass']) ? ':'.$parsed['pass'] : '').'@' : '';
$uri .= isset($parsed['host']) ? $parsed['host'] : '';
$uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
if(isset($parsed['path']))
{
$uri .= (substr($parsed['path'], 0, 1) == '/') ? $parsed['path'] : ('/'.$parsed['path']);
}
$uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
$uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
return $uri;
}
?>
stevenlewis at hotmail dot com
13-Aug-2007 06:08
13-Aug-2007 06:08
an update to the glue url function.
you are able to put a host and a path without a slash at the beginning of the path
<?php
function glue_url($parsed)
{
if (! is_array($parsed)) return false;
$uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '':'//'): '';
$uri .= isset($parsed['user']) ? $parsed['user'].($parsed['pass']? ':'.$parsed['pass']:'').'@':'';
$uri .= isset($parsed['host']) ? $parsed['host'] : '';
$uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
if(isset($parsed['path']))
{
$uri .= (substr($parsed['path'],0,1) == '/')?$parsed['path']:'/'.$parsed['path'];
}
$uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
$uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
return $uri;
}
?>
spam at paulisageek dot com
09-Aug-2007 03:05
09-Aug-2007 03:05
In reply to adrian,
Thank you very much for your function. There is a small issue with your relative protocol function. You need to remove the // when making the url the path. Here is the new function.
function resolve_url($base, $url) {
if (!strlen($base)) return $url;
// Step 2
if (!strlen($url)) return $base;
// Step 3
if (preg_match('!^[a-z]+:!i', $url)) return $url;
$base = parse_url($base);
if ($url{0} == "#") {
// Step 2 (fragment)
$base['fragment'] = substr($url, 1);
return unparse_url($base);
}
unset($base['fragment']);
unset($base['query']);
if (substr($url, 0, 2) == "//") {
// Step 4
return unparse_url(array(
'scheme'=>$base['scheme'],
'path'=>substr($url,2),
));
} else if ($url{0} == "/") {
// Step 5
$base['path'] = $url;
} else {
// Step 6
$path = explode('/', $base['path']);
$url_path = explode('/', $url);
// Step 6a: drop file from base
array_pop($path);
// Step 6b, 6c, 6e: append url while removing "." and ".." from
// the directory portion
$end = array_pop($url_path);
foreach ($url_path as $segment) {
if ($segment == '.') {
// skip
} else if ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
array_pop($path);
} else {
$path[] = $segment;
}
}
// Step 6d, 6f: remove "." and ".." from file portion
if ($end == '.') {
$path[] = '';
} else if ($end == '..' && $path && $path[sizeof($path)-1] != '..') {
$path[sizeof($path)-1] = '';
} else {
$path[] = $end;
}
// Step 6h
$base['path'] = join('/', $path);
}
// Step 7
return unparse_url($base);
}
christian at resource-it dot dk
04-Aug-2007 03:57
04-Aug-2007 03:57
I searched for an implementation of rfc3986, which is a newer version of rfc 2392. I may find it here : <http://www.chrsen.dk/fundanemt/files/scripter/php/misc/rfc3986.php> - read the rfc at <http://rfc.net/rfc3986.html>
adrian-php at sixfingeredman dot net
26-Jul-2007 05:58
26-Jul-2007 05:58
Here's a function which implements resolving a relative URL according to RFC 2396 section 5.2. No doubt there are more efficient implementations, but this one tries to remain close to the standard for clarity. It relies on a function called "unparse_url" to implement section 7, left as an exercise for the reader (or you can substitute the "glue_url" function posted earlier).
<?php
/**
* Resolve a URL relative to a base path. This happens to work with POSIX
* filenames as well. This is based on RFC 2396 section 5.2.
*/
function resolve_url($base, $url) {
if (!strlen($base)) return $url;
// Step 2
if (!strlen($url)) return $base;
// Step 3
if (preg_match('!^[a-z]+:!i', $url)) return $url;
$base = parse_url($base);
if ($url{0} == "#") {
// Step 2 (fragment)
$base['fragment'] = substr($url, 1);
return unparse_url($base);
}
unset($base['fragment']);
unset($base['query']);
if (substr($url, 0, 2) == "//") {
// Step 4
return unparse_url(array(
'scheme'=>$base['scheme'],
'path'=>$url,
));
} else if ($url{0} == "/") {
// Step 5
$base['path'] = $url;
} else {
// Step 6
$path = explode('/', $base['path']);
$url_path = explode('/', $url);
// Step 6a: drop file from base
array_pop($path);
// Step 6b, 6c, 6e: append url while removing "." and ".." from
// the directory portion
$end = array_pop($url_path);
foreach ($url_path as $segment) {
if ($segment == '.') {
// skip
} else if ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
array_pop($path);
} else {
$path[] = $segment;
}
}
// Step 6d, 6f: remove "." and ".." from file portion
if ($end == '.') {
$path[] = '';
} else if ($end == '..' && $path && $path[sizeof($path)-1] != '..') {
$path[sizeof($path)-1] = '';
} else {
$path[] = $end;
}
// Step 6h
$base['path'] = join('/', $path);
}
// Step 7
return unparse_url($base);
}
?>
Antti Haapala
17-Jul-2007 04:42
17-Jul-2007 04:42
Actually the behaviour noticed by the previous poster is quite correct. When the URI scheme is not present, it is plain wrong to assume that something starting with www. is a domain name, and that the scheme is HTTP. Internet Explorer does it that way, sure, but it does not make it any more correct. The documentation says that the function tries to decode the URL as well as it can, and the only sensible and standards-compliant way to decode such URL is to expect it to be a relative URI.
Elliott Brueggeman
04-Jun-2007 06:59
04-Jun-2007 06:59
Note that if you pass this function a url without a scheme (www.php.net, as opposed to http://www.php.net), the function will incorrectly parse the results. In my test case it returned the domain under the ['path'] element and nothing in the ['host'] element.
Marc-Antoine Ross
15-Mar-2007 12:10
15-Mar-2007 12:10
Do not look for the fragment in $_SERVER['QUERY_STRING'], you will not find it. You should read the fragment in JavaScript for example.
alistair at 21degrees dot com dot au
24-Oct-2006 10:21
24-Oct-2006 10:21
Heres a simple function to add the $component option in for PHP4. Haven't done exhaustive testing, but should work ok.
<?php
## Defines only available in PHP 5, created for PHP4
if(!defined('PHP_URL_SCHEME')) define('PHP_URL_SCHEME', 1);
if(!defined('PHP_URL_HOST')) define('PHP_URL_HOST', 2);
if(!defined('PHP_URL_PORT')) define('PHP_URL_PORT', 3);
if(!defined('PHP_URL_USER')) define('PHP_URL_USER', 4);
if(!defined('PHP_URL_PASS')) define('PHP_URL_PASS', 5);
if(!defined('PHP_URL_PATH')) define('PHP_URL_PATH', 6);
if(!defined('PHP_URL_QUERY')) define('PHP_URL_QUERY', 7);
if(!defined('PHP_URL_FRAGMENT')) define('PHP_URL_FRAGMENT', 8);
function parse_url_compat($url, $component=NULL){
if(!$component) return parse_url($url);
## PHP 5
if(phpversion() >= 5)
return parse_url($url, $component);
## PHP 4
$bits = parse_url($url);
switch($component){
case PHP_URL_SCHEME: return $bits['scheme'];
case PHP_URL_HOST: return $bits['host'];
case PHP_URL_PORT: return $bits['port'];
case PHP_URL_USER: return $bits['user'];
case PHP_URL_PASS: return $bits['pass'];
case PHP_URL_PATH: return $bits['path'];
case PHP_URL_QUERY: return $bits['query'];
case PHP_URL_FRAGMENT: return $bits['fragment'];
}
}
?>
dawalama at gmail dot com
05-Oct-2006 12:48
05-Oct-2006 12:48
With few modifications
/**
* source: http://us2.php.net/manual/en/function.parse-url.php#60237
* Edit the Query portion of an url
*
* @param string $action ethier a "+" or a "-" depending on what action you want to perform
* @param mixed $var array (+) or string (-)
* @param string $uri the URL to use. if this is left out, it uses $_SERVER['PHP_SELF']
* @version 1.0.0
*/
function change_query($action, $var = NULL, $uri = NULL) {
if (($action == "+" && !is_array($var)) || ($action == "-" && $var == "") || $var == NULL) {
return FALSE;
}
if (is_null($uri)) { //Piece together uri string
$beginning = $_SERVER['PHP_SELF'];
$ending = (isset ($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
} else {
$qstart = strpos($uri, '?');
if ($qstart === false) {
$beginning = $uri; //$ending is '' anyway
$ending = "";
} else {
$beginning = substr($uri, 0, $qstart);
$ending = substr($uri, $qstart);
}
}
$vals = array ();
$ending = str_replace('?', '', $ending);
parse_str($ending, $vals);
switch ($action) {
case '+' :
$vals[$var[0]] = $var[1];
break;
case '-' :
if (isset ($vals[$var])) {
unset ($vals[$var]);
}
break;
default :
break;
}
$params = array();
foreach ($vals as $k => $value) {
$params[] = $k."=".urlencode($value);
}
$result = $beginning . (count($params) ? '?' . implode("&", $params) : '');
return $result;
}
kjensen at nospam dot iaff106 dot com
27-Sep-2006 07:21
27-Sep-2006 07:21
Here is a simple extended version of ParseURL().
I needed to make a link that will be saved off site but point to different file
than the one creating the link.
So I needed to get the path without the file name so I could change the
file name.
Here it is:
<?php
function ParseURLplus($url){
$URLpcs = (parse_url($url));
$PathPcs = explode("/",$URLpcs['path']);
$URLpcs['file'] = end($PathPcs);
unset($PathPcs[key($PathPcs)]);
$URLpcs['dir'] = implode("/",$PathPcs);
return ($URLpcs);
}
$url = 'http://username:password@hostname/path/directory/file.php?arg=
value#anchor';
$URLpcs = ParseURLplus($url);
print_r($URLpcs);
?>
Now I can change the $URLpcs['file'] and then glue itback together to make
a new url.
corgilabs at SPAM_NO_THANK_YOUgmail dot com
14-Jul-2006 02:59
14-Jul-2006 02:59
I hope this is helpful! Cheers!
-eo
<?
# Author: Eric O
# Date: July 13, 2006
# Go Zizou!! :O)
# Creating Automatic Self-Redirect To Secure Version
# of Website as Seen on Paypal and other secure sites
# Changes HTTP to HTTPS
#gets the URI of the script
$url = $_SERVER['SCRIPT_URI'];
#chops URI into bits BORK BORK BORK
$chopped = parse_url($url);
#HOST and PATH portions of your final destination
$destination = $chopped[host].$chopped[path];
#if you are not HTTPS, then do something about it
if($chopped[scheme] != "https"){
#forwards to HTTP version of URI with secure certificate
header("Location: https://$destination");
exit();
}
?>
php dot net at NOSPAM dot juamei dot com
09-May-2006 07:18
09-May-2006 07:18
Modfied version of glue_url to avoid error messages if the error_reporting is set high.
function glue_url($parsed)
{
if (! is_array($parsed)) return false;
$uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '':'//'): '';
$uri .= isset($parsed['user']) ? $parsed['user'].($parsed['pass']? ':'.$parsed['pass']:'').'@':'';
$uri .= isset($parsed['host']) ? $parsed['host'] : '';
$uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
$uri .= isset($parsed['path']) ? $parsed['path'] : '';
$uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
$uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
return $uri;
}
TheShadow
31-Dec-2004 04:36
31-Dec-2004 04:36
You may want to check out the PEAR NET_URL class. It provides easy means to manipulate URL strings.
http://pear.php.net/package/Net_URL
matt at cryptography dot com
10-May-2004 04:36
10-May-2004 04:36
Modified version of glue_url()
Cox's,Anonimous fucntion
<?php
function glue_url($parsed) {
if (! is_array($parsed)) return false;
$uri = $parsed['scheme'] ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '':'//'): '';
$uri .= $parsed['user'] ? $parsed['user'].($parsed['pass']? ':'.$parsed['pass']:'').'@':'';
$uri .= $parsed['host'] ? $parsed['host'] : '';
$uri .= $parsed['port'] ? ':'.$parsed['port'] : '';
$uri .= $parsed['path'] ? $parsed['path'] : '';
$uri .= $parsed['query'] ? '?'.$parsed['query'] : '';
$uri .= $parsed['fragment'] ? '#'.$parsed['fragment'] : '';
return $uri;
}
?>
