1
0
mirror of https://github.com/guzzle/guzzle.git synced 2025-02-24 18:13:00 +01:00

Properly merging URLs and removing dot segments based on RFC 3986 section 5.4. Closes #288 and #289.

This commit is contained in:
Michael Dowling 2013-04-08 13:05:09 -07:00
parent 6290b06491
commit cdb1da7d1a
3 changed files with 68 additions and 56 deletions

View File

@ -285,28 +285,32 @@ class Url
// Replace // and /./ with /
$this->path = str_replace(array('/./', '//'), '/', $this->path);
// Remove trailing relative paths if possible
$segments = $this->getPathSegments();
$last = end($segments);
$trailingSlash = false;
if ($last === '') {
array_pop($segments);
$trailingSlash = true;
}
// Remove dot segments
if (strpos($this->path, '..') !== false) {
while ($last == '..' || $last == '.') {
if ($last == '..') {
// Remove trailing relative paths if possible
$segments = $this->getPathSegments();
$last = end($segments);
$trailingSlash = false;
if ($last === '') {
array_pop($segments);
$last = array_pop($segments);
$trailingSlash = true;
}
if ($last == '.' || $last == '') {
$last = array_pop($segments);
}
}
$this->path = implode('/', $segments);
if ($trailingSlash) {
$this->path .= '/';
while ($last == '..' || $last == '.') {
if ($last == '..') {
array_pop($segments);
$last = array_pop($segments);
}
if ($last == '.' || $last == '') {
$last = array_pop($segments);
}
}
$this->path = implode('/', $segments);
if ($trailingSlash) {
$this->path .= '/';
}
}
return $this;
@ -468,60 +472,66 @@ class Url
}
/**
* Combine the URL with another URL. Parts specified in the passed URL will supersede parts in the current URL.
* Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4.
*
* @param string $url Relative URL to combine with
*
* @return Url
* @throws InvalidArgumentException
* @link http://tools.ietf.org/html/rfc3986#section-5.4
*/
public function combine($url)
{
$absolutePath = $url[0] == '/';
$url = self::factory($url);
// Use the more absolute URL as the base URL
if (!$this->isAbsolute() && $url->isAbsolute()) {
$url = $url->combine($this);
}
// Passing a URL with a scheme overrides everything
if ($buffer = $url->getScheme()) {
$this->scheme = $buffer;
$this->host = $url->getHost();
$this->port = $url->getPort();
$this->username = $url->getUsername();
$this->password = $url->getPassword();
$this->path = $url->getPath();
$this->query = $url->getQuery();
$this->fragment = $url->getFragment();
return $this;
}
// Setting a host overrides the entire rest of the URL
if ($buffer = $url->getHost()) {
$this->host = $buffer;
$this->port = $url->getPort();
$this->username = $url->getUsername();
$this->password = $url->getPassword();
$this->path = $url->getPath();
$this->fragment = $url->getFragment();
return $this;
}
if ($buffer = $url->getPort()) {
$this->port = $buffer;
}
$path = $url->getPath();
$query = $url->getQuery();
if ($buffer = $url->getUsername()) {
$this->username = $buffer;
}
if ($buffer = $url->getPassword()) {
$this->password = $buffer;
}
if ($buffer = $url->getFragment()) {
$this->fragment = $buffer;
}
if ($absolutePath) {
// Replace the current URL and query if set
if ($buffer = $url->getPath()) {
$this->path = $buffer;
}
if (count($url->getQuery())) {
$this->query = $url->getQuery();
if (!$path) {
if (count($query)) {
$this->query = $query;
}
} else {
// Append to the current path and query string
if ($buffer = $url->getPath()) {
$this->addPath($buffer);
}
if ($buffer = $url->getQuery()) {
$this->query->merge($buffer);
if ($path[0] == '/') {
$this->path = $path;
} else {
$this->path .= '/' . $path;
}
$this->normalizePath();
$this->query = $query;
}
$this->fragment = $url->getFragment();
return $this;
}
}

View File

@ -304,8 +304,8 @@ class ClientTest extends \Guzzle\Tests\GuzzleTestCase
array($u, '/absolute/path/to/resource', $this->getServer()->getUrl() . 'absolute/path/to/resource'),
array($u, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'),
array($u2, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'),
array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1'),
array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1&another=query')
array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource'),
array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?another=query')
);
}

View File

@ -148,12 +148,13 @@ class UrlTest extends \Guzzle\Tests\GuzzleTestCase
array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'),
array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'),
array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'),
array('http://www.example.com/path', 'http://test.com', 'http://test.com/path'),
array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com/path'),
array('http://www.example.com/path', 'http://test.com', 'http://test.com'),
array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com'),
array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'),
array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'),
array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/path'),
array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'),
array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'),
array('http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token')
);
}
@ -198,10 +199,11 @@ class UrlTest extends \Guzzle\Tests\GuzzleTestCase
array('/foo/../..', ''),
array('/foo/../.', ''),
array('/./foo/..', ''),
array('/./foo', 'foo'),
array('/./foo/', 'foo/'),
array('/./foo', '/foo'),
array('/./foo/', '/foo/'),
array('/./foo/bar/baz/pho/../..', 'foo/bar'),
array('*', '*')
array('*', '*'),
array('/foo', '/foo')
);
}