Changeset View
Changeset View
Standalone View
Standalone View
src/parser/PhutilURI.php
Show All 21 Lines | final class PhutilURI extends Phobject { | ||||
private $path; | private $path; | ||||
private $query = array(); | private $query = array(); | ||||
private $fragment; | private $fragment; | ||||
private $type; | private $type; | ||||
const TYPE_URI = 'uri'; | const TYPE_URI = 'uri'; | ||||
const TYPE_GIT = 'git'; | const TYPE_GIT = 'git'; | ||||
public function __construct($uri) { | public function __construct($uri, $params = array()) { | ||||
if ($uri instanceof PhutilURI) { | if ($uri instanceof PhutilURI) { | ||||
$this->protocol = $uri->protocol; | $this->protocol = $uri->protocol; | ||||
$this->user = $uri->user; | $this->user = $uri->user; | ||||
$this->pass = $uri->pass; | $this->pass = $uri->pass; | ||||
$this->domain = $uri->domain; | $this->domain = $uri->domain; | ||||
$this->port = $uri->port; | $this->port = $uri->port; | ||||
$this->path = $uri->path; | $this->path = $uri->path; | ||||
$this->query = $uri->query; | $this->query = $uri->query; | ||||
$this->fragment = $uri->fragment; | $this->fragment = $uri->fragment; | ||||
$this->type = $uri->type; | $this->type = $uri->type; | ||||
$this->initializeQueryParams(phutil_string_cast($uri), $params); | |||||
return; | return; | ||||
} | } | ||||
$uri = (string)$uri; | $uri = phutil_string_cast($uri); | ||||
$type = self::TYPE_URI; | $type = self::TYPE_URI; | ||||
// Reject ambiguous URIs outright. Different versions of different clients | // Reject ambiguous URIs outright. Different versions of different clients | ||||
// parse these in different ways. See T12526 for discussion. | // parse these in different ways. See T12526 for discussion. | ||||
if (preg_match('(^[^/:]*://[^/]*[#?].*:)', $uri)) { | if (preg_match('(^[^/:]*://[^/]*[#?].*:)', $uri)) { | ||||
throw new Exception( | throw new Exception( | ||||
pht( | pht( | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | if ($query) { | ||||
foreach ($pairs as $pair) { | foreach ($pairs as $pair) { | ||||
list($key, $value) = $pair; | list($key, $value) = $pair; | ||||
$this->appendQueryParam($key, $value); | $this->appendQueryParam($key, $value); | ||||
} | } | ||||
} | } | ||||
$this->fragment = idx($parts, 'fragment', ''); | $this->fragment = idx($parts, 'fragment', ''); | ||||
$this->type = $type; | $this->type = $type; | ||||
$this->initializeQueryParams($uri, $params); | |||||
} | } | ||||
public function __toString() { | public function __toString() { | ||||
$prefix = null; | $prefix = null; | ||||
if ($this->isGitURI()) { | if ($this->isGitURI()) { | ||||
$port = null; | $port = null; | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | if ($this->isGitURI()) { | ||||
if (strlen($path)) { | if (strlen($path)) { | ||||
$path = ':'.$path; | $path = ':'.$path; | ||||
} | } | ||||
} | } | ||||
return $prefix.$path.$query.$fragment; | return $prefix.$path.$query.$fragment; | ||||
} | } | ||||
/** | |||||
* @deprecated | |||||
*/ | |||||
public function setQueryParam($key, $value) { | public function setQueryParam($key, $value) { | ||||
// To set, we replace the first matching key with the new value, then | // To set, we replace the first matching key with the new value, then | ||||
// remove all other matching keys. This replaces the old value and retains | // remove all other matching keys. This replaces the old value and retains | ||||
// the parameter order. | // the parameter order. | ||||
$is_null = ($value === null); | $is_null = ($value === null); | ||||
// Typecheck and cast the key before we compare it to existing keys. This | // Typecheck and cast the key before we compare it to existing keys. This | ||||
Show All 29 Lines | if (!$found) { | ||||
if (!$is_null) { | if (!$is_null) { | ||||
$this->appendQueryParam($key, $value); | $this->appendQueryParam($key, $value); | ||||
} | } | ||||
} | } | ||||
return $this; | return $this; | ||||
} | } | ||||
/** | |||||
* @deprecated | |||||
*/ | |||||
public function setQueryParams(array $params) { | public function setQueryParams(array $params) { | ||||
$this->query = array(); | $this->query = array(); | ||||
foreach ($params as $k => $v) { | foreach ($params as $k => $v) { | ||||
$this->appendQueryParam($k, $v); | $this->appendQueryParam($k, $v); | ||||
} | } | ||||
return $this; | return $this; | ||||
Show All 36 Lines | final class PhutilURI extends Phobject { | ||||
public function getQueryParamsAsPairList() { | public function getQueryParamsAsPairList() { | ||||
return $this->query; | return $this->query; | ||||
} | } | ||||
public function appendQueryParam($key, $value) { | public function appendQueryParam($key, $value) { | ||||
return $this->insertQueryParam($key, $value); | return $this->insertQueryParam($key, $value); | ||||
} | } | ||||
public function removeAllQueryParams() { | |||||
$this->query = array(); | |||||
return $this; | |||||
} | |||||
public function removeQueryParam($remove_key) { | |||||
list($remove_key) = phutil_http_parameter_pair($remove_key, ''); | |||||
foreach ($this->query as $idx => $pair) { | |||||
list($key, $value) = $pair; | |||||
if ($key !== $remove_key) { | |||||
continue; | |||||
} | |||||
unset($this->query[$idx]); | |||||
} | |||||
$this->query = array_values($this->query); | |||||
return $this; | |||||
} | |||||
public function replaceQueryParam($replace_key, $replace_value) { | |||||
if ($replace_value === null) { | |||||
throw new InvalidArgumentException( | |||||
pht( | |||||
'Value provided to "replaceQueryParam()" for key "%s" is NULL. '. | |||||
'Use "removeQueryParam()" to remove a query parameter.', | |||||
$replace_key)); | |||||
} | |||||
$this->removeQueryParam($replace_key); | |||||
$this->appendQueryParam($replace_key, $replace_value); | |||||
return $this; | |||||
} | |||||
private function insertQueryParam($key, $value, $idx = null) { | private function insertQueryParam($key, $value, $idx = null) { | ||||
list($key, $value) = phutil_http_parameter_pair($key, $value); | list($key, $value) = phutil_http_parameter_pair($key, $value); | ||||
if ($idx === null) { | if ($idx === null) { | ||||
$this->query[] = array($key, $value); | $this->query[] = array($key, $value); | ||||
} else { | } else { | ||||
$this->query[$idx] = array($key, $value); | $this->query[$idx] = array($key, $value); | ||||
} | } | ||||
return $this; | return $this; | ||||
} | } | ||||
private function initializeQueryParams($uri, array $params) { | |||||
$have_params = array(); | |||||
foreach ($this->query as $pair) { | |||||
list($key) = $pair; | |||||
$have_params[$key] = true; | |||||
} | |||||
foreach ($params as $key => $value) { | |||||
if (isset($have_params[$key])) { | |||||
throw new InvalidArgumentException( | |||||
pht( | |||||
'You are trying to construct an ambiguous URI: query parameter '. | |||||
'"%s" is present in both the string argument ("%s") and the map '. | |||||
'argument.', | |||||
$key, | |||||
$uri)); | |||||
} | |||||
if ($value === null) { | |||||
continue; | |||||
amckinley: Shouldn't we throw here instead of just swallowing a null value as though we did something with… | |||||
Done Inline ActionsI'm intentionally allowing null to mean "no value" in the constructor, since there were a number of callsites like: $params = array( 'class' => $symbol->getClass(), 'line' => $symbol->getLine(), 'context' => $symbol->getContext(), 'language' => $symbol->getLanguage(), ); ...where several parameters may be null. If we can't pass null, this turns into 20 lines of null tests, or we just end up wrapping it in some filter_nulls_out() method which we don't currently have (array_filter() filters all "falsey" values). I think mistakes are unlikely here / the intent is fairly clear / the behavior is significantly common and useful. epriestley: I'm intentionally allowing `null` to mean "no value" in the constructor, since there were a… | |||||
} | |||||
$this->appendQueryParam($key, $value); | |||||
} | |||||
return $this; | |||||
} | |||||
public function setProtocol($protocol) { | public function setProtocol($protocol) { | ||||
$this->protocol = $protocol; | $this->protocol = $protocol; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getProtocol() { | public function getProtocol() { | ||||
return $this->protocol; | return $this->protocol; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | final class PhutilURI extends Phobject { | ||||
} | } | ||||
public function getPass() { | public function getPass() { | ||||
return $this->pass; | return $this->pass; | ||||
} | } | ||||
public function alter($key, $value) { | public function alter($key, $value) { | ||||
$altered = clone $this; | $altered = clone $this; | ||||
$altered->setQueryParam($key, $value); | $altered->replaceQueryParam($key, $value); | ||||
return $altered; | return $altered; | ||||
} | } | ||||
public function isGitURI() { | public function isGitURI() { | ||||
return ($this->type == self::TYPE_GIT); | return ($this->type == self::TYPE_GIT); | ||||
} | } | ||||
public function setType($type) { | public function setType($type) { | ||||
▲ Show 20 Lines • Show All 62 Lines • Show Last 20 Lines |
Shouldn't we throw here instead of just swallowing a null value as though we did something with it?