diff --git a/src/Guzzle/Http/Url.php b/src/Guzzle/Http/Url.php index c5b701b3..756585bd 100644 --- a/src/Guzzle/Http/Url.php +++ b/src/Guzzle/Http/Url.php @@ -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; } } diff --git a/tests/Guzzle/Tests/Http/ClientTest.php b/tests/Guzzle/Tests/Http/ClientTest.php index 9972f6a8..8b1186e6 100644 --- a/tests/Guzzle/Tests/Http/ClientTest.php +++ b/tests/Guzzle/Tests/Http/ClientTest.php @@ -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') ); } diff --git a/tests/Guzzle/Tests/Http/UrlTest.php b/tests/Guzzle/Tests/Http/UrlTest.php index be400739..c0f7c783 100644 --- a/tests/Guzzle/Tests/Http/UrlTest.php +++ b/tests/Guzzle/Tests/Http/UrlTest.php @@ -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') ); }