<?php
/**
- * Zend Framework (http://framework.zend.com/)
- *
- * @see http://github.com/zendframework/zend-diactoros for the canonical source repository
- * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
+ * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
+ * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/
class Uri implements UriInterface
{
/**
- * Sub-delimiters used in query strings and fragments.
+ * Sub-delimiters used in user info, query strings and fragments.
*
* @const string
*/
const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
/**
- * Unreserved characters used in paths, query strings, and fragments.
+ * Unreserved characters used in user info, paths, query strings, and fragments.
*
* @const string
*/
}
/**
+ * Retrieve the user-info part of the URI.
+ *
+ * This value is percent-encoded, per RFC 3986 Section 3.2.1.
+ *
* {@inheritdoc}
*/
public function getUserInfo()
if ($scheme === $this->scheme) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
$new = clone $this;
}
/**
+ * Create and return a new instance containing the provided user credentials.
+ *
+ * The value will be percent-encoded in the new instance, but with measures
+ * taken to prevent double-encoding.
+ *
* {@inheritdoc}
*/
public function withUserInfo($user, $password = null)
));
}
- $info = $user;
+ $info = $this->filterUserInfoPart($user);
if ($password) {
- $info .= ':' . $password;
+ $info .= ':' . $this->filterUserInfoPart($password);
}
if ($info === $this->userInfo) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
$new = clone $this;
if ($host === $this->host) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
$new = clone $this;
- $new->host = $host;
+ $new->host = strtolower($host);
return $new;
}
if ($port === $this->port) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
- if ($port !== null && $port < 1 || $port > 65535) {
+ if ($port !== null && ($port < 1 || $port > 65535)) {
throw new InvalidArgumentException(sprintf(
'Invalid port "%d" specified; must be a valid TCP/UDP port',
$port
if ($path === $this->path) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
$new = clone $this;
if ($query === $this->query) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
$new = clone $this;
if ($fragment === $this->fragment) {
// Do nothing if no change was made.
- return clone $this;
+ return $this;
}
$new = clone $this;
}
$this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : '';
- $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
- $this->host = isset($parts['host']) ? $parts['host'] : '';
+ $this->userInfo = isset($parts['user']) ? $this->filterUserInfoPart($parts['user']) : '';
+ $this->host = isset($parts['host']) ? strtolower($parts['host']) : '';
$this->port = isset($parts['port']) ? $parts['port'] : null;
$this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : '';
$this->query = isset($parts['query']) ? $this->filterQuery($parts['query']) : '';
return $scheme;
}
+ /**
+ * Filters a part of user info in a URI to ensure it is properly encoded.
+ *
+ * @param string $part
+ * @return string
+ */
+ private function filterUserInfoPart($part)
+ {
+ // Note the addition of `%` to initial charset; this allows `|` portion
+ // to match and thus prevent double-encoding.
+ return preg_replace_callback(
+ '/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/u',
+ [$this, 'urlEncodeChar'],
+ $part
+ );
+ }
+
/**
* Filters the path of a URI to ensure it is properly encoded.
*
private function filterPath($path)
{
$path = preg_replace_callback(
- '/(?:[^' . self::CHAR_UNRESERVED . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u',
+ '/(?:[^' . self::CHAR_UNRESERVED . ')(:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u',
[$this, 'urlEncodeChar'],
$path
);
/**
* Filter a fragment value to ensure it is properly encoded.
*
- * @param null|string $fragment
+ * @param string $fragment
* @return string
*/
private function filterFragment($fragment)