Uri.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <?php
  2. namespace OAuth\Common\Http\Uri;
  3. use InvalidArgumentException;
  4. /**
  5. * Standards-compliant URI class.
  6. */
  7. class Uri implements UriInterface
  8. {
  9. /**
  10. * @var string
  11. */
  12. private $scheme = 'http';
  13. /**
  14. * @var string
  15. */
  16. private $userInfo = '';
  17. /**
  18. * @var string
  19. */
  20. private $rawUserInfo = '';
  21. /**
  22. * @var string
  23. */
  24. private $host;
  25. /**
  26. * @var int
  27. */
  28. private $port = 80;
  29. /**
  30. * @var string
  31. */
  32. private $path = '/';
  33. /**
  34. * @var string
  35. */
  36. private $query = '';
  37. /**
  38. * @var string
  39. */
  40. private $fragment = '';
  41. /**
  42. * @var bool
  43. */
  44. private $explicitPortSpecified = false;
  45. /**
  46. * @var bool
  47. */
  48. private $explicitTrailingHostSlash = false;
  49. /**
  50. * @param string $uri
  51. */
  52. public function __construct($uri = null)
  53. {
  54. if (null !== $uri) {
  55. $this->parseUri($uri);
  56. }
  57. }
  58. /**
  59. * @param string $uri
  60. *
  61. * @throws \InvalidArgumentException
  62. */
  63. protected function parseUri($uri)
  64. {
  65. if (false === ($uriParts = parse_url($uri))) {
  66. // congratulations if you've managed to get parse_url to fail,
  67. // it seems to always return some semblance of a parsed url no matter what
  68. throw new InvalidArgumentException("Invalid URI: $uri");
  69. }
  70. if (!isset($uriParts['scheme'])) {
  71. throw new InvalidArgumentException('Invalid URI: http|https scheme required');
  72. }
  73. $this->scheme = $uriParts['scheme'];
  74. $this->host = $uriParts['host'];
  75. if (isset($uriParts['port'])) {
  76. $this->port = $uriParts['port'];
  77. $this->explicitPortSpecified = true;
  78. } else {
  79. $this->port = strcmp('https', $uriParts['scheme']) ? 80 : 443;
  80. $this->explicitPortSpecified = false;
  81. }
  82. if (isset($uriParts['path'])) {
  83. $this->path = $uriParts['path'];
  84. if ('/' === $uriParts['path']) {
  85. $this->explicitTrailingHostSlash = true;
  86. }
  87. } else {
  88. $this->path = '/';
  89. }
  90. $this->query = isset($uriParts['query']) ? $uriParts['query'] : '';
  91. $this->fragment = isset($uriParts['fragment']) ? $uriParts['fragment'] : '';
  92. $userInfo = '';
  93. if (!empty($uriParts['user'])) {
  94. $userInfo .= $uriParts['user'];
  95. }
  96. if ($userInfo && !empty($uriParts['pass'])) {
  97. $userInfo .= ':' . $uriParts['pass'];
  98. }
  99. $this->setUserInfo($userInfo);
  100. }
  101. /**
  102. * @param string $rawUserInfo
  103. *
  104. * @return string
  105. */
  106. protected function protectUserInfo($rawUserInfo)
  107. {
  108. $colonPos = strpos($rawUserInfo, ':');
  109. // rfc3986-3.2.1 | http://tools.ietf.org/html/rfc3986#section-3.2
  110. // "Applications should not render as clear text any data
  111. // after the first colon (":") character found within a userinfo
  112. // subcomponent unless the data after the colon is the empty string
  113. // (indicating no password)"
  114. if ($colonPos !== false && strlen($rawUserInfo)-1 > $colonPos) {
  115. return substr($rawUserInfo, 0, $colonPos) . ':********';
  116. } else {
  117. return $rawUserInfo;
  118. }
  119. }
  120. /**
  121. * @return string
  122. */
  123. public function getScheme()
  124. {
  125. return $this->scheme;
  126. }
  127. /**
  128. * @return string
  129. */
  130. public function getUserInfo()
  131. {
  132. return $this->userInfo;
  133. }
  134. /**
  135. * @return string
  136. */
  137. public function getRawUserInfo()
  138. {
  139. return $this->rawUserInfo;
  140. }
  141. /**
  142. * @return string
  143. */
  144. public function getHost()
  145. {
  146. return $this->host;
  147. }
  148. /**
  149. * @return int
  150. */
  151. public function getPort()
  152. {
  153. return $this->port;
  154. }
  155. /**
  156. * @return string
  157. */
  158. public function getPath()
  159. {
  160. return $this->path;
  161. }
  162. /**
  163. * @return string
  164. */
  165. public function getQuery()
  166. {
  167. return $this->query;
  168. }
  169. /**
  170. * @return string
  171. */
  172. public function getFragment()
  173. {
  174. return $this->fragment;
  175. }
  176. /**
  177. * Uses protected user info by default as per rfc3986-3.2.1
  178. * Uri::getRawAuthority() is available if plain-text password information is desirable.
  179. *
  180. * @return string
  181. */
  182. public function getAuthority()
  183. {
  184. $authority = $this->userInfo ? $this->userInfo.'@' : '';
  185. $authority .= $this->host;
  186. if ($this->explicitPortSpecified) {
  187. $authority .= ":{$this->port}";
  188. }
  189. return $authority;
  190. }
  191. /**
  192. * @return string
  193. */
  194. public function getRawAuthority()
  195. {
  196. $authority = $this->rawUserInfo ? $this->rawUserInfo.'@' : '';
  197. $authority .= $this->host;
  198. if ($this->explicitPortSpecified) {
  199. $authority .= ":{$this->port}";
  200. }
  201. return $authority;
  202. }
  203. /**
  204. * @return string
  205. */
  206. public function getAbsoluteUri()
  207. {
  208. $uri = $this->scheme . '://' . $this->getRawAuthority();
  209. if ('/' === $this->path) {
  210. $uri .= $this->explicitTrailingHostSlash ? '/' : '';
  211. } else {
  212. $uri .= $this->path;
  213. }
  214. if (!empty($this->query)) {
  215. $uri .= "?{$this->query}";
  216. }
  217. if (!empty($this->fragment)) {
  218. $uri .= "#{$this->fragment}";
  219. }
  220. return $uri;
  221. }
  222. /**
  223. * @return string
  224. */
  225. public function getRelativeUri()
  226. {
  227. $uri = '';
  228. if ('/' === $this->path) {
  229. $uri .= $this->explicitTrailingHostSlash ? '/' : '';
  230. } else {
  231. $uri .= $this->path;
  232. }
  233. return $uri;
  234. }
  235. /**
  236. * Uses protected user info by default as per rfc3986-3.2.1
  237. * Uri::getAbsoluteUri() is available if plain-text password information is desirable.
  238. *
  239. * @return string
  240. */
  241. public function __toString()
  242. {
  243. $uri = $this->scheme . '://' . $this->getAuthority();
  244. if ('/' === $this->path) {
  245. $uri .= $this->explicitTrailingHostSlash ? '/' : '';
  246. } else {
  247. $uri .= $this->path;
  248. }
  249. if (!empty($this->query)) {
  250. $uri .= "?{$this->query}";
  251. }
  252. if (!empty($this->fragment)) {
  253. $uri .= "#{$this->fragment}";
  254. }
  255. return $uri;
  256. }
  257. /**
  258. * @param $path
  259. */
  260. public function setPath($path)
  261. {
  262. if (empty($path)) {
  263. $this->path = '/';
  264. $this->explicitTrailingHostSlash = false;
  265. } else {
  266. $this->path = $path;
  267. if ('/' === $this->path) {
  268. $this->explicitTrailingHostSlash = true;
  269. }
  270. }
  271. }
  272. /**
  273. * @param string $query
  274. */
  275. public function setQuery($query)
  276. {
  277. $this->query = $query;
  278. }
  279. /**
  280. * @param string $var
  281. * @param string $val
  282. */
  283. public function addToQuery($var, $val)
  284. {
  285. if (strlen($this->query) > 0) {
  286. $this->query .= '&';
  287. }
  288. $this->query .= http_build_query(array($var => $val), '', '&');
  289. }
  290. /**
  291. * @param string $fragment
  292. */
  293. public function setFragment($fragment)
  294. {
  295. $this->fragment = $fragment;
  296. }
  297. /**
  298. * @param string $scheme
  299. */
  300. public function setScheme($scheme)
  301. {
  302. $this->scheme = $scheme;
  303. }
  304. /**
  305. * @param string $userInfo
  306. */
  307. public function setUserInfo($userInfo)
  308. {
  309. $this->userInfo = $userInfo ? $this->protectUserInfo($userInfo) : '';
  310. $this->rawUserInfo = $userInfo;
  311. }
  312. /**
  313. * @param int $port
  314. */
  315. public function setPort($port)
  316. {
  317. $this->port = intval($port);
  318. if (('https' === $this->scheme && $this->port === 443) || ('http' === $this->scheme && $this->port === 80)) {
  319. $this->explicitPortSpecified = false;
  320. } else {
  321. $this->explicitPortSpecified = true;
  322. }
  323. }
  324. /**
  325. * @param string $host
  326. */
  327. public function setHost($host)
  328. {
  329. $this->host = $host;
  330. }
  331. /**
  332. * @return bool
  333. */
  334. public function hasExplicitTrailingHostSlash()
  335. {
  336. return $this->explicitTrailingHostSlash;
  337. }
  338. /**
  339. * @return bool
  340. */
  341. public function hasExplicitPortSpecified()
  342. {
  343. return $this->explicitPortSpecified;
  344. }
  345. }