Requests_IRI

IRI parser/serialiser/normaliser.

Defined (1)

The class is defined in the following location(s).

/wp-includes/Requests/IRI.php  
  1. class Requests_IRI { 
  2. /** 
  3. * Scheme 
  4. * @var string 
  5. */ 
  6. protected $scheme = null; 
  7.  
  8. /** 
  9. * User Information 
  10. * @var string 
  11. */ 
  12. protected $iuserinfo = null; 
  13.  
  14. /** 
  15. * ihost 
  16. * @var string 
  17. */ 
  18. protected $ihost = null; 
  19.  
  20. /** 
  21. * Port 
  22. * @var string 
  23. */ 
  24. protected $port = null; 
  25.  
  26. /** 
  27. * ipath 
  28. * @var string 
  29. */ 
  30. protected $ipath = ''; 
  31.  
  32. /** 
  33. * iquery 
  34. * @var string 
  35. */ 
  36. protected $iquery = null; 
  37.  
  38. /** 
  39. * ifragment 
  40. * @var string 
  41. */ 
  42. protected $ifragment = null; 
  43.  
  44. /** 
  45. * Normalization database 
  46. * Each key is the scheme, each value is an array with each key as the IRI 
  47. * part and value as the default value for that part. 
  48. */ 
  49. protected $normalization = array( 
  50. 'acap' => array( 
  51. 'port' => 674 
  52. ),  
  53. 'dict' => array( 
  54. 'port' => 2628 
  55. ),  
  56. 'file' => array( 
  57. 'ihost' => 'localhost' 
  58. ),  
  59. 'http' => array( 
  60. 'port' => 80,  
  61. ),  
  62. 'https' => array( 
  63. 'port' => 443,  
  64. ),  
  65. ); 
  66.  
  67. /** 
  68. * Return the entire IRI when you try and read the object as a string 
  69. * @return string 
  70. */ 
  71. public function __toString() { 
  72. return $this->get_iri(); 
  73.  
  74. /** 
  75. * Overload __set() to provide access via properties 
  76. * @param string $name Property name 
  77. * @param mixed $value Property value 
  78. */ 
  79. public function __set($name, $value) { 
  80. if (method_exists($this, 'set_' . $name)) { 
  81. call_user_func(array($this, 'set_' . $name), $value); 
  82. elseif ( 
  83. $name === 'iauthority' 
  84. || $name === 'iuserinfo' 
  85. || $name === 'ihost' 
  86. || $name === 'ipath' 
  87. || $name === 'iquery' 
  88. || $name === 'ifragment' 
  89. ) { 
  90. call_user_func(array($this, 'set_' . substr($name, 1)), $value); 
  91.  
  92. /** 
  93. * Overload __get() to provide access via properties 
  94. * @param string $name Property name 
  95. * @return mixed 
  96. */ 
  97. public function __get($name) { 
  98. // isset() returns false for null, we don't want to do that 
  99. // Also why we use array_key_exists below instead of isset() 
  100. $props = get_object_vars($this); 
  101.  
  102. if ( 
  103. $name === 'iri' || 
  104. $name === 'uri' || 
  105. $name === 'iauthority' || 
  106. $name === 'authority' 
  107. ) { 
  108. $method = 'get_' . $name; 
  109. $return = $this->$method(); 
  110. elseif (array_key_exists($name, $props)) { 
  111. $return = $this->$name; 
  112. // host -> ihost 
  113. elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) { 
  114. $name = $prop; 
  115. $return = $this->$prop; 
  116. // ischeme -> scheme 
  117. elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) { 
  118. $name = $prop; 
  119. $return = $this->$prop; 
  120. else { 
  121. trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); 
  122. $return = null; 
  123.  
  124. if ($return === null && isset($this->normalization[$this->scheme][$name])) { 
  125. return $this->normalization[$this->scheme][$name]; 
  126. else { 
  127. return $return; 
  128.  
  129. /** 
  130. * Overload __isset() to provide access via properties 
  131. * @param string $name Property name 
  132. * @return bool 
  133. */ 
  134. public function __isset($name) { 
  135. return (method_exists($this, 'get_' . $name) || isset($this->$name)); 
  136.  
  137. /** 
  138. * Overload __unset() to provide access via properties 
  139. * @param string $name Property name 
  140. */ 
  141. public function __unset($name) { 
  142. if (method_exists($this, 'set_' . $name)) { 
  143. call_user_func(array($this, 'set_' . $name), ''); 
  144.  
  145. /** 
  146. * Create a new IRI object, from a specified string 
  147. * @param string|null $iri 
  148. */ 
  149. public function __construct($iri = null) { 
  150. $this->set_iri($iri); 
  151.  
  152. /** 
  153. * Create a new IRI object by resolving a relative IRI 
  154. * Returns false if $base is not absolute, otherwise an IRI. 
  155. * @param IRI|string $base (Absolute) Base IRI 
  156. * @param IRI|string $relative Relative IRI 
  157. * @return IRI|false 
  158. */ 
  159. public static function absolutize($base, $relative) { 
  160. if (!($relative instanceof Requests_IRI)) { 
  161. $relative = new Requests_IRI($relative); 
  162. if (!$relative->is_valid()) { 
  163. return false; 
  164. elseif ($relative->scheme !== null) { 
  165. return clone $relative; 
  166.  
  167. if (!($base instanceof Requests_IRI)) { 
  168. $base = new Requests_IRI($base); 
  169. if ($base->scheme === null || !$base->is_valid()) { 
  170. return false; 
  171.  
  172. if ($relative->get_iri() !== '') { 
  173. if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) { 
  174. $target = clone $relative; 
  175. $target->scheme = $base->scheme; 
  176. else { 
  177. $target = new Requests_IRI; 
  178. $target->scheme = $base->scheme; 
  179. $target->iuserinfo = $base->iuserinfo; 
  180. $target->ihost = $base->ihost; 
  181. $target->port = $base->port; 
  182. if ($relative->ipath !== '') { 
  183. if ($relative->ipath[0] === '/') { 
  184. $target->ipath = $relative->ipath; 
  185. elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') { 
  186. $target->ipath = '/' . $relative->ipath; 
  187. elseif (($last_segment = strrpos($base->ipath, '/')) !== false) { 
  188. $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; 
  189. else { 
  190. $target->ipath = $relative->ipath; 
  191. $target->ipath = $target->remove_dot_segments($target->ipath); 
  192. $target->iquery = $relative->iquery; 
  193. else { 
  194. $target->ipath = $base->ipath; 
  195. if ($relative->iquery !== null) { 
  196. $target->iquery = $relative->iquery; 
  197. elseif ($base->iquery !== null) { 
  198. $target->iquery = $base->iquery; 
  199. $target->ifragment = $relative->ifragment; 
  200. else { 
  201. $target = clone $base; 
  202. $target->ifragment = null; 
  203. $target->scheme_normalization(); 
  204. return $target; 
  205.  
  206. /** 
  207. * Parse an IRI into scheme/authority/path/query/fragment segments 
  208. * @param string $iri 
  209. * @return array 
  210. */ 
  211. protected function parse_iri($iri) { 
  212. $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); 
  213. $has_match = preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match); 
  214. if (!$has_match) { 
  215. throw new Requests_Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri); 
  216.  
  217. if ($match[1] === '') { 
  218. $match['scheme'] = null; 
  219. if (!isset($match[3]) || $match[3] === '') { 
  220. $match['authority'] = null; 
  221. if (!isset($match[5])) { 
  222. $match['path'] = ''; 
  223. if (!isset($match[6]) || $match[6] === '') { 
  224. $match['query'] = null; 
  225. if (!isset($match[8]) || $match[8] === '') { 
  226. $match['fragment'] = null; 
  227. return $match; 
  228.  
  229. /** 
  230. * Remove dot segments from a path 
  231. * @param string $input 
  232. * @return string 
  233. */ 
  234. protected function remove_dot_segments($input) { 
  235. $output = ''; 
  236. while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { 
  237. // A: If the input buffer begins with a prefix of "../" or "./",  
  238. // then remove that prefix from the input buffer; otherwise,  
  239. if (strpos($input, '../') === 0) { 
  240. $input = substr($input, 3); 
  241. elseif (strpos($input, './') === 0) { 
  242. $input = substr($input, 2); 
  243. // B: if the input buffer begins with a prefix of "/./" or "/.",  
  244. // where "." is a complete path segment, then replace that prefix 
  245. // with "/" in the input buffer; otherwise,  
  246. elseif (strpos($input, '/./') === 0) { 
  247. $input = substr($input, 2); 
  248. elseif ($input === '/.') { 
  249. $input = '/'; 
  250. // C: if the input buffer begins with a prefix of "/../" or "/..",  
  251. // where ".." is a complete path segment, then replace that prefix 
  252. // with "/" in the input buffer and remove the last segment and its 
  253. // preceding "/" (if any) from the output buffer; otherwise,  
  254. elseif (strpos($input, '/../') === 0) { 
  255. $input = substr($input, 3); 
  256. $output = substr_replace($output, '', strrpos($output, '/')); 
  257. elseif ($input === '/..') { 
  258. $input = '/'; 
  259. $output = substr_replace($output, '', strrpos($output, '/')); 
  260. // D: if the input buffer consists only of "." or "..", then remove 
  261. // that from the input buffer; otherwise,  
  262. elseif ($input === '.' || $input === '..') { 
  263. $input = ''; 
  264. // E: move the first path segment in the input buffer to the end of 
  265. // the output buffer, including the initial "/" character (if any) 
  266. // and any subsequent characters up to, but not including, the next 
  267. // "/" character or the end of the input buffer 
  268. elseif (($pos = strpos($input, '/', 1)) !== false) { 
  269. $output .= substr($input, 0, $pos); 
  270. $input = substr_replace($input, '', 0, $pos); 
  271. else { 
  272. $output .= $input; 
  273. $input = ''; 
  274. return $output . $input; 
  275.  
  276. /** 
  277. * Replace invalid character with percent encoding 
  278. * @param string $string Input string 
  279. * @param string $extra_chars Valid characters not in iunreserved or 
  280. * iprivate (this is ASCII-only) 
  281. * @param bool $iprivate Allow iprivate 
  282. * @return string 
  283. */ 
  284. protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) { 
  285. // Normalize as many pct-encoded sections as possible 
  286. $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); 
  287.  
  288. // Replace invalid percent characters 
  289. $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); 
  290.  
  291. // Add unreserved and % to $extra_chars (the latter is safe because all 
  292. // pct-encoded sections are now valid). 
  293. $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; 
  294.  
  295. // Now replace any bytes that aren't allowed with their pct-encoded versions 
  296. $position = 0; 
  297. $strlen = strlen($string); 
  298. while (($position += strspn($string, $extra_chars, $position)) < $strlen) { 
  299. $value = ord($string[$position]); 
  300.  
  301. // Start position 
  302. $start = $position; 
  303.  
  304. // By default we are valid 
  305. $valid = true; 
  306.  
  307. // No one byte sequences are valid due to the while. 
  308. // Two byte sequence: 
  309. if (($value & 0xE0) === 0xC0) { 
  310. $character = ($value & 0x1F) << 6; 
  311. $length = 2; 
  312. $remaining = 1; 
  313. // Three byte sequence: 
  314. elseif (($value & 0xF0) === 0xE0) { 
  315. $character = ($value & 0x0F) << 12; 
  316. $length = 3; 
  317. $remaining = 2; 
  318. // Four byte sequence: 
  319. elseif (($value & 0xF8) === 0xF0) { 
  320. $character = ($value & 0x07) << 18; 
  321. $length = 4; 
  322. $remaining = 3; 
  323. // Invalid byte: 
  324. else { 
  325. $valid = false; 
  326. $length = 1; 
  327. $remaining = 0; 
  328.  
  329. if ($remaining) { 
  330. if ($position + $length <= $strlen) { 
  331. for ($position++; $remaining; $position++) { 
  332. $value = ord($string[$position]); 
  333.  
  334. // Check that the byte is valid, then add it to the character: 
  335. if (($value & 0xC0) === 0x80) { 
  336. $character |= ($value & 0x3F) << (--$remaining * 6); 
  337. // If it is invalid, count the sequence as invalid and reprocess the current byte: 
  338. else { 
  339. $valid = false; 
  340. $position--; 
  341. break; 
  342. else { 
  343. $position = $strlen - 1; 
  344. $valid = false; 
  345.  
  346. // Percent encode anything invalid or not in ucschar 
  347. if ( 
  348. // Invalid sequences 
  349. !$valid 
  350. // Non-shortest form sequences are invalid 
  351. || $length > 1 && $character <= 0x7F 
  352. || $length > 2 && $character <= 0x7FF 
  353. || $length > 3 && $character <= 0xFFFF 
  354. // Outside of range of ucschar codepoints 
  355. // Noncharacters 
  356. || ($character & 0xFFFE) === 0xFFFE 
  357. || $character >= 0xFDD0 && $character <= 0xFDEF 
  358. || ( 
  359. // Everything else not in ucschar 
  360. $character > 0xD7FF && $character < 0xF900 
  361. || $character < 0xA0 
  362. || $character > 0xEFFFD 
  363. && ( 
  364. // Everything not in iprivate, if it applies 
  365. !$iprivate 
  366. || $character < 0xE000 
  367. || $character > 0x10FFFD 
  368. ) { 
  369. // If we were a character, pretend we weren't, but rather an error. 
  370. if ($valid) { 
  371. $position--; 
  372.  
  373. for ($j = $start; $j <= $position; $j++) { 
  374. $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); 
  375. $j += 2; 
  376. $position += 2; 
  377. $strlen += 2; 
  378.  
  379. return $string; 
  380.  
  381. /** 
  382. * Callback function for preg_replace_callback. 
  383. * Removes sequences of percent encoded bytes that represent UTF-8 
  384. * encoded characters in iunreserved 
  385. * @param array $match PCRE match 
  386. * @return string Replacement 
  387. */ 
  388. protected function remove_iunreserved_percent_encoded($match) { 
  389. // As we just have valid percent encoded sequences we can just explode 
  390. // and ignore the first member of the returned array (an empty string). 
  391. $bytes = explode('%', $match[0]); 
  392.  
  393. // Initialize the new string (this is what will be returned) and that 
  394. // there are no bytes remaining in the current sequence (unsurprising 
  395. // at the first byte!). 
  396. $string = ''; 
  397. $remaining = 0; 
  398.  
  399. // Loop over each and every byte, and set $value to its value 
  400. for ($i = 1, $len = count($bytes); $i < $len; $i++) { 
  401. $value = hexdec($bytes[$i]); 
  402.  
  403. // If we're the first byte of sequence: 
  404. if (!$remaining) { 
  405. // Start position 
  406. $start = $i; 
  407.  
  408. // By default we are valid 
  409. $valid = true; 
  410.  
  411. // One byte sequence: 
  412. if ($value <= 0x7F) { 
  413. $character = $value; 
  414. $length = 1; 
  415. // Two byte sequence: 
  416. elseif (($value & 0xE0) === 0xC0) { 
  417. $character = ($value & 0x1F) << 6; 
  418. $length = 2; 
  419. $remaining = 1; 
  420. // Three byte sequence: 
  421. elseif (($value & 0xF0) === 0xE0) { 
  422. $character = ($value & 0x0F) << 12; 
  423. $length = 3; 
  424. $remaining = 2; 
  425. // Four byte sequence: 
  426. elseif (($value & 0xF8) === 0xF0) { 
  427. $character = ($value & 0x07) << 18; 
  428. $length = 4; 
  429. $remaining = 3; 
  430. // Invalid byte: 
  431. else { 
  432. $valid = false; 
  433. $remaining = 0; 
  434. // Continuation byte: 
  435. else { 
  436. // Check that the byte is valid, then add it to the character: 
  437. if (($value & 0xC0) === 0x80) { 
  438. $remaining--; 
  439. $character |= ($value & 0x3F) << ($remaining * 6); 
  440. // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: 
  441. else { 
  442. $valid = false; 
  443. $remaining = 0; 
  444. $i--; 
  445.  
  446. // If we've reached the end of the current byte sequence, append it to Unicode::$data 
  447. if (!$remaining) { 
  448. // Percent encode anything invalid or not in iunreserved 
  449. if ( 
  450. // Invalid sequences 
  451. !$valid 
  452. // Non-shortest form sequences are invalid 
  453. || $length > 1 && $character <= 0x7F 
  454. || $length > 2 && $character <= 0x7FF 
  455. || $length > 3 && $character <= 0xFFFF 
  456. // Outside of range of iunreserved codepoints 
  457. || $character < 0x2D 
  458. || $character > 0xEFFFD 
  459. // Noncharacters 
  460. || ($character & 0xFFFE) === 0xFFFE 
  461. || $character >= 0xFDD0 && $character <= 0xFDEF 
  462. // Everything else not in iunreserved (this is all BMP) 
  463. || $character === 0x2F 
  464. || $character > 0x39 && $character < 0x41 
  465. || $character > 0x5A && $character < 0x61 
  466. || $character > 0x7A && $character < 0x7E 
  467. || $character > 0x7E && $character < 0xA0 
  468. || $character > 0xD7FF && $character < 0xF900 
  469. ) { 
  470. for ($j = $start; $j <= $i; $j++) { 
  471. $string .= '%' . strtoupper($bytes[$j]); 
  472. else { 
  473. for ($j = $start; $j <= $i; $j++) { 
  474. $string .= chr(hexdec($bytes[$j])); 
  475.  
  476. // If we have any bytes left over they are invalid (i.e., we are 
  477. // mid-way through a multi-byte sequence) 
  478. if ($remaining) { 
  479. for ($j = $start; $j < $len; $j++) { 
  480. $string .= '%' . strtoupper($bytes[$j]); 
  481.  
  482. return $string; 
  483.  
  484. protected function scheme_normalization() { 
  485. if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) { 
  486. $this->iuserinfo = null; 
  487. if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) { 
  488. $this->ihost = null; 
  489. if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) { 
  490. $this->port = null; 
  491. if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) { 
  492. $this->ipath = ''; 
  493. if (isset($this->ihost) && empty($this->ipath)) { 
  494. $this->ipath = '/'; 
  495. if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) { 
  496. $this->iquery = null; 
  497. if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) { 
  498. $this->ifragment = null; 
  499.  
  500. /** 
  501. * Check if the object represents a valid IRI. This needs to be done on each 
  502. * call as some things change depending on another part of the IRI. 
  503. * @return bool 
  504. */ 
  505. public function is_valid() { 
  506. $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; 
  507. if ($this->ipath !== '' && 
  508. $isauthority && $this->ipath[0] !== '/' || 
  509. $this->scheme === null && 
  510. !$isauthority && 
  511. strpos($this->ipath, ':') !== false && 
  512. (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) 
  513. ) { 
  514. return false; 
  515.  
  516. return true; 
  517.  
  518. /** 
  519. * Set the entire IRI. Returns true on success, false on failure (if there 
  520. * are any invalid characters). 
  521. * @param string $iri 
  522. * @return bool 
  523. */ 
  524. protected function set_iri($iri) { 
  525. static $cache; 
  526. if (!$cache) { 
  527. $cache = array(); 
  528.  
  529. if ($iri === null) { 
  530. return true; 
  531. if (isset($cache[$iri])) { 
  532. list($this->scheme,  
  533. $this->iuserinfo,  
  534. $this->ihost,  
  535. $this->port,  
  536. $this->ipath,  
  537. $this->iquery,  
  538. $this->ifragment,  
  539. $return) = $cache[$iri]; 
  540. return $return; 
  541.  
  542. $parsed = $this->parse_iri((string) $iri); 
  543.  
  544. $return = $this->set_scheme($parsed['scheme']) 
  545. && $this->set_authority($parsed['authority']) 
  546. && $this->set_path($parsed['path']) 
  547. && $this->set_query($parsed['query']) 
  548. && $this->set_fragment($parsed['fragment']); 
  549.  
  550. $cache[$iri] = array($this->scheme,  
  551. $this->iuserinfo,  
  552. $this->ihost,  
  553. $this->port,  
  554. $this->ipath,  
  555. $this->iquery,  
  556. $this->ifragment,  
  557. $return); 
  558. return $return; 
  559.  
  560. /** 
  561. * Set the scheme. Returns true on success, false on failure (if there are 
  562. * any invalid characters). 
  563. * @param string $scheme 
  564. * @return bool 
  565. */ 
  566. protected function set_scheme($scheme) { 
  567. if ($scheme === null) { 
  568. $this->scheme = null; 
  569. elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) { 
  570. $this->scheme = null; 
  571. return false; 
  572. else { 
  573. $this->scheme = strtolower($scheme); 
  574. return true; 
  575.  
  576. /** 
  577. * Set the authority. Returns true on success, false on failure (if there are 
  578. * any invalid characters). 
  579. * @param string $authority 
  580. * @return bool 
  581. */ 
  582. protected function set_authority($authority) { 
  583. static $cache; 
  584. if (!$cache) { 
  585. $cache = array(); 
  586.  
  587. if ($authority === null) { 
  588. $this->iuserinfo = null; 
  589. $this->ihost = null; 
  590. $this->port = null; 
  591. return true; 
  592. if (isset($cache[$authority])) { 
  593. list($this->iuserinfo,  
  594. $this->ihost,  
  595. $this->port,  
  596. $return) = $cache[$authority]; 
  597.  
  598. return $return; 
  599.  
  600. $remaining = $authority; 
  601. if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { 
  602. $iuserinfo = substr($remaining, 0, $iuserinfo_end); 
  603. $remaining = substr($remaining, $iuserinfo_end + 1); 
  604. else { 
  605. $iuserinfo = null; 
  606. if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { 
  607. $port = substr($remaining, $port_start + 1); 
  608. if ($port === false || $port === '') { 
  609. $port = null; 
  610. $remaining = substr($remaining, 0, $port_start); 
  611. else { 
  612. $port = null; 
  613.  
  614. $return = $this->set_userinfo($iuserinfo) && 
  615. $this->set_host($remaining) && 
  616. $this->set_port($port); 
  617.  
  618. $cache[$authority] = array($this->iuserinfo,  
  619. $this->ihost,  
  620. $this->port,  
  621. $return); 
  622.  
  623. return $return; 
  624.  
  625. /** 
  626. * Set the iuserinfo. 
  627. * @param string $iuserinfo 
  628. * @return bool 
  629. */ 
  630. protected function set_userinfo($iuserinfo) { 
  631. if ($iuserinfo === null) { 
  632. $this->iuserinfo = null; 
  633. else { 
  634. $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+, ;=:'); 
  635. $this->scheme_normalization(); 
  636.  
  637. return true; 
  638.  
  639. /** 
  640. * Set the ihost. Returns true on success, false on failure (if there are 
  641. * any invalid characters). 
  642. * @param string $ihost 
  643. * @return bool 
  644. */ 
  645. protected function set_host($ihost) { 
  646. if ($ihost === null) { 
  647. $this->ihost = null; 
  648. return true; 
  649. if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') { 
  650. if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) { 
  651. $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']'; 
  652. else { 
  653. $this->ihost = null; 
  654. return false; 
  655. else { 
  656. $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+, ;='); 
  657.  
  658. // Lowercase, but ignore pct-encoded sections (as they should 
  659. // remain uppercase). This must be done after the previous step 
  660. // as that can add unescaped characters. 
  661. $position = 0; 
  662. $strlen = strlen($ihost); 
  663. while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) { 
  664. if ($ihost[$position] === '%') { 
  665. $position += 3; 
  666. else { 
  667. $ihost[$position] = strtolower($ihost[$position]); 
  668. $position++; 
  669.  
  670. $this->ihost = $ihost; 
  671.  
  672. $this->scheme_normalization(); 
  673.  
  674. return true; 
  675.  
  676. /** 
  677. * Set the port. Returns true on success, false on failure (if there are 
  678. * any invalid characters). 
  679. * @param string $port 
  680. * @return bool 
  681. */ 
  682. protected function set_port($port) { 
  683. if ($port === null) { 
  684. $this->port = null; 
  685. return true; 
  686.  
  687. if (strspn($port, '0123456789') === strlen($port)) { 
  688. $this->port = (int) $port; 
  689. $this->scheme_normalization(); 
  690. return true; 
  691.  
  692. $this->port = null; 
  693. return false; 
  694.  
  695. /** 
  696. * Set the ipath. 
  697. * @param string $ipath 
  698. * @return bool 
  699. */ 
  700. protected function set_path($ipath) { 
  701. static $cache; 
  702. if (!$cache) { 
  703. $cache = array(); 
  704.  
  705. $ipath = (string) $ipath; 
  706.  
  707. if (isset($cache[$ipath])) { 
  708. $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; 
  709. else { 
  710. $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+, ;=@:/'); 
  711. $removed = $this->remove_dot_segments($valid); 
  712.  
  713. $cache[$ipath] = array($valid, $removed); 
  714. $this->ipath = ($this->scheme !== null) ? $removed : $valid; 
  715. $this->scheme_normalization(); 
  716. return true; 
  717.  
  718. /** 
  719. * Set the iquery. 
  720. * @param string $iquery 
  721. * @return bool 
  722. */ 
  723. protected function set_query($iquery) { 
  724. if ($iquery === null) { 
  725. $this->iquery = null; 
  726. else { 
  727. $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+, ;=:@/?', true); 
  728. $this->scheme_normalization(); 
  729. return true; 
  730.  
  731. /** 
  732. * Set the ifragment. 
  733. * @param string $ifragment 
  734. * @return bool 
  735. */ 
  736. protected function set_fragment($ifragment) { 
  737. if ($ifragment === null) { 
  738. $this->ifragment = null; 
  739. else { 
  740. $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+, ;=:@/?'); 
  741. $this->scheme_normalization(); 
  742. return true; 
  743.  
  744. /** 
  745. * Convert an IRI to a URI (or parts thereof) 
  746. * @param string|bool IRI to convert (or false from {@see get_iri}) 
  747. * @return string|false URI if IRI is valid, false otherwise. 
  748. */ 
  749. protected function to_uri($string) { 
  750. if (!is_string($string)) { 
  751. return false; 
  752.  
  753. static $non_ascii; 
  754. if (!$non_ascii) { 
  755. $non_ascii = implode('', range("\x80", "\xFF")); 
  756.  
  757. $position = 0; 
  758. $strlen = strlen($string); 
  759. while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { 
  760. $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); 
  761. $position += 3; 
  762. $strlen += 2; 
  763.  
  764. return $string; 
  765.  
  766. /** 
  767. * Get the complete IRI 
  768. * @return string 
  769. */ 
  770. protected function get_iri() { 
  771. if (!$this->is_valid()) { 
  772. return false; 
  773.  
  774. $iri = ''; 
  775. if ($this->scheme !== null) { 
  776. $iri .= $this->scheme . ':'; 
  777. if (($iauthority = $this->get_iauthority()) !== null) { 
  778. $iri .= '//' . $iauthority; 
  779. $iri .= $this->ipath; 
  780. if ($this->iquery !== null) { 
  781. $iri .= '?' . $this->iquery; 
  782. if ($this->ifragment !== null) { 
  783. $iri .= '#' . $this->ifragment; 
  784.  
  785. return $iri; 
  786.  
  787. /** 
  788. * Get the complete URI 
  789. * @return string 
  790. */ 
  791. protected function get_uri() { 
  792. return $this->to_uri($this->get_iri()); 
  793.  
  794. /** 
  795. * Get the complete iauthority 
  796. * @return string 
  797. */ 
  798. protected function get_iauthority() { 
  799. if ($this->iuserinfo === null && $this->ihost === null && $this->port === null) { 
  800. return null; 
  801.  
  802. $iauthority = ''; 
  803. if ($this->iuserinfo !== null) { 
  804. $iauthority .= $this->iuserinfo . '@'; 
  805. if ($this->ihost !== null) { 
  806. $iauthority .= $this->ihost; 
  807. if ($this->port !== null) { 
  808. $iauthority .= ':' . $this->port; 
  809. return $iauthority; 
  810.  
  811. /** 
  812. * Get the complete authority 
  813. * @return string 
  814. */ 
  815. protected function get_authority() { 
  816. $iauthority = $this->get_iauthority(); 
  817. if (is_string($iauthority)) { 
  818. return $this->to_uri($iauthority); 
  819. else { 
  820. return $iauthority;