PHPのお勉強!

PHP TOP

parse_url

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

parse_urlURL を解釈し、その構成要素を返す

説明

parse_url(string $url, int $component = -1): int|string|array|null|false

この関数は、URL の様々な構成要素のうち特定できるものに関して 連想配列にして返します。 連想配列に含まれる要素の値は、URLデコード されません

この関数は、指定された URL が有効かどうかを調べるためのもの ではなく、単に URL を以下で示す 要素に分解するだけのものです。不完全、かつ不正な URL であっても受け入れますし、 そのような場合でも parse_url() は可能な限り 正しく解析しようとします。

警告

この関数は相対 URL、 または不正な URL に対しては正しい結果を返さないかもしれません。 また、この関数の返す結果は、 HTTP クライアントの一般的な挙動とも異なるかもしれません。 信頼できない入力から得られる URL をパースする必要がある場合、 たとえば filter_var() 関数を FILTER_VALIDATE_URL と一緒に使うなどして、 追加の検証を行うことが必須です。

パラメータ

url

パースする URL。

component

PHP_URL_SCHEMEPHP_URL_HOSTPHP_URL_PORTPHP_URL_USERPHP_URL_PASSPHP_URL_PATHPHP_URL_QUERY あるいは PHP_URL_FRAGMENT のうちのいずれかを指定し、 特定の URL コンポーネントのみを文字列 (PHP_URL_PORT を指定した場合だけは整数値) で取得するようにします。

戻り値

完全におかしな形式の URL については、parse_url()false を返します。

component を省略した場合は、連想配列を返します。 連想配列の中には少なくともひとつの要素が含まれます。 この配列に含まれる可能性のある要素は次のとおりです。

  • scheme - 例: http
  • host
  • port
  • user
  • pass
  • path
  • query - クエスチョンマーク ? 以降
  • fragment - ハッシュマーク # 以降

component が指定されている場合、 parse_url() は配列ではなく文字列 (PHP_URL_PORT の場合は整数値) を返します。 要求したコンポーネントが指定した URL の中にない場合は null を返します。 PHP 8.0.0 以降では、 parse_url() は、query と fragment について、 存在しないことと値が空であることを以下のように区別します:

http://example.com/foo → query = null, fragment = null
http://example.com/foo? → query = "",   fragment = null
http://example.com/foo# → query = null, fragment = ""
http://example.com/foo?# → query = "",   fragment = ""

8.0.0 より前のバージョンでは、上記の場合は query も fragment も共に null になっていました。

URL の各要素(コンポーネント) に含まれる制御文字 (ctype_cntrl() も参照ください) は、 アンダースコア (_) に置き換えられる点に注意して下さい。

変更履歴

バージョン 説明
8.0.0 parse_url() は、query と fragment について、 存在しないことと値が空であることを区別するようになりました。

例1 parse_url() の例

<?php
$url
= 'http://username:password@hostname:9090/path?arg=value#anchor';

var_dump(parse_url($url));
var_dump(parse_url($url, PHP_URL_SCHEME));
var_dump(parse_url($url, PHP_URL_USER));
var_dump(parse_url($url, PHP_URL_PASS));
var_dump(parse_url($url, PHP_URL_HOST));
var_dump(parse_url($url, PHP_URL_PORT));
var_dump(parse_url($url, PHP_URL_PATH));
var_dump(parse_url($url, PHP_URL_QUERY));
var_dump(parse_url($url, PHP_URL_FRAGMENT));
?>

上の例の出力は以下となります。

array(8) {
  ["scheme"]=>
  string(4) "http"
  ["host"]=>
  string(8) "hostname"
  ["port"]=>
  int(9090)
  ["user"]=>
  string(8) "username"
  ["pass"]=>
  string(8) "password"
  ["path"]=>
  string(5) "/path"
  ["query"]=>
  string(9) "arg=value"
  ["fragment"]=>
  string(6) "anchor"
}
string(4) "http"
string(8) "username"
string(8) "password"
string(8) "hostname"
int(9090)
string(5) "/path"
string(9) "arg=value"
string(6) "anchor"

例2 parse_url() でスキームを省略した例

<?php
$url
= '//www.example.com/path?googleguy=googley';

// 5.4.7 より前のバージョンでは、パスを "//www.example.com/path" のように表示していました
var_dump(parse_url($url));
?>

上の例の出力は以下となります。

array(3) {
  ["host"]=>
  string(15) "www.example.com"
  ["path"]=>
  string(5) "/path"
  ["query"]=>
  string(17) "googleguy=googley"
}

注意

注意:

parse_url() は URL をパースするための関数であり、 URI をパースするものではありません。しかし、PHP の後方互換性を満たすため、 例外として file:// スキームについては 3 重スラッシュ(file:///...) が認められています。他のスキームにおいては、これは無効な形式となります。

参考

add a note

User Contributed Notes 38 notes

up
193
thomas at gielfeldt dot com
13 years ago
[If you haven't yet] been able to find a simple conversion back to string from a parsed url, here's an example:

<?php

$url
= 'http://usr:pss@example.com:81/mypath/myfile.html?a=b&b[]=2&b[]=3#myfragment';
if (
$url === unparse_url(parse_url($url))) {
print
"YES, they match!\n";
}

function
unparse_url($parsed_url) {
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return
"$scheme$user$pass$host$port$path$query$fragment";
}

?>
up
44
lauris () lauris ! lv
10 years ago
Here is utf-8 compatible parse_url() replacement function based on "laszlo dot janszky at gmail dot com" work. Original incorrectly handled URLs with user:pass. Also made PHP 5.5 compatible (got rid of now deprecated regex /e modifier).

<?php

/**
* UTF-8 aware parse_url() replacement.
*
* @return array
*/
function mb_parse_url($url)
{
$enc_url = preg_replace_callback(
'%[^:/@?&=#]+%usD',
function (
$matches)
{
return
urlencode($matches[0]);
},
$url
);

$parts = parse_url($enc_url);

if(
$parts === false)
{
throw new
\InvalidArgumentException('Malformed URL: ' . $url);
}

foreach(
$parts as $name => $value)
{
$parts[$name] = urldecode($value);
}

return
$parts;
}

?>
up
41
jerome at chaman dot ca
9 years ago
It may be worth reminding that the value of the #fragment never gets sent to the server. Anchors processing is exclusively client-side.
up
3
nospam at spellingcow dot com
16 years ago
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
up
3
adrian-php at sixfingeredman dot net
17 years ago
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);
}
?>
up
46
james at roundeights dot com
14 years ago
I was writing unit tests and needed to cause this function to kick out an error and return FALSE in order to test a specific execution path. If anyone else needs to force a failure, the following inputs will work:

<?php
parse_url
("http:///example.com");
parse_url("http://:80");
parse_url("http://user@:80");
?>
up
17
to1ne at hotmail dot com
16 years ago
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...
up
5
pjpawel
2 years ago
Unfortunately parse_url() DO NOT parse correctly urls without scheme or '//'. For example 'www.xyz.com' is consider as path not host:

Code:
<?php
var_dump
(parse_url('www.xyz.com'));
?>
Output:
array(1) {
["path"]=>
string(10) "www.xyz.com"
}

To get better output change url to:
'//www.xyz.com' or 'http://www.xyz.com'