4
* Basic URI parser object.
6
final class PhutilURI {
14
private $query = array();
17
public function __construct($uri) {
21
if (preg_match('(^([^/:]*://[^/]*)(\\?.*)\z)', $uri, $matches)) {
22
// If the URI is something like `idea://open?file=/path/to/file`, the
23
// `parse_url()` function will parse `open?file=` as the host. This is
24
// not the expected result. Break the URI into two pieces, stick a slash
25
// in between them, parse that, then remove the path. See T6106.
27
$parts = parse_url($matches[1].'/'.$matches[2]);
28
unset($parts['path']);
30
$parts = parse_url($uri);
33
// The parse_url() call will accept URIs with leading whitespace, but many
34
// other tools (like git) will not. See T4913 for a specific example. If
35
// the input string has leading whitespace, fail the parse.
37
if (ltrim($uri) != $uri) {
43
// NOTE: `parse_url()` is very liberal about host names; fail the parse if
44
// the host looks like garbage.
46
$host = idx($parts, 'host', '');
47
if (!preg_match('/^([a-zA-Z0-9\\.\\-]*)$/', $host)) {
56
// stringyness is to preserve API compatibility and
57
// allow the tests to continue passing
58
$this->protocol = idx($parts, 'scheme', '');
59
$this->user = rawurldecode(idx($parts, 'user', ''));
60
$this->pass = rawurldecode(idx($parts, 'pass', ''));
61
$this->domain = idx($parts, 'host', '');
62
$this->port = (string)idx($parts, 'port', '');
63
$this->path = idx($parts, 'path', '');
64
$query = idx($parts, 'query');
66
$this->query = id(new PhutilQueryStringParser())->parseQueryString(
69
$this->fragment = idx($parts, 'fragment', '');
72
public function __toString() {
74
if ($this->protocol || $this->domain || $this->port) {
75
$protocol = nonempty($this->protocol, 'http');
78
if (strlen($this->user) && strlen($this->pass)) {
79
$auth = phutil_escape_uri($this->user).':'.
80
phutil_escape_uri($this->pass).'@';
81
} else if (strlen($this->user)) {
82
$auth = phutil_escape_uri($this->user).'@';
85
$prefix = $protocol.'://'.$auth.$this->domain;
87
$prefix .= ':'.$this->port;
92
$query = '?'.http_build_query($this->query);
97
if (strlen($this->getFragment())) {
98
$fragment = '#'.$this->getFragment();
104
return $prefix.$this->getPath().$query.$fragment;
107
public function setQueryParam($key, $value) {
108
if ($value === null) {
109
unset($this->query[$key]);
111
$this->query[$key] = $value;
116
public function setQueryParams(array $params) {
117
$this->query = $params;
121
public function getQueryParams() {
125
public function setProtocol($protocol) {
126
$this->protocol = $protocol;
129
public function getProtocol() {
130
return $this->protocol;
133
public function setDomain($domain) {
134
$this->domain = $domain;
138
public function getDomain() {
139
return $this->domain;
142
public function setPort($port) {
146
public function getPort() {
150
public function setPath($path) {
151
if ($this->domain && strlen($path) && $path[0] !== '/') {
158
public function appendPath($path) {
159
$first = strlen($path) ? $path[0] : null;
160
$last = strlen($this->path) ? $this->path[strlen($this->path) - 1] : null;
163
return $this->setPath($path);
164
} else if ($first === '/' && $last === '/') {
165
$path = substr($path, 1);
166
} else if ($first !== '/' && $last !== '/') {
170
$this->path .= $path;
174
public function getPath() {
178
public function setFragment($fragment) {
179
$this->fragment = $fragment;
183
public function getFragment() {
184
return $this->fragment;
187
public function setUser($user) {
192
public function getUser() {
196
public function setPass($pass) {
201
public function getPass() {
205
public function alter($key, $value) {
206
$altered = clone $this;
207
$altered->setQueryParam($key, $value);