SimplePie_IRI

IRI parser/serialiser/normaliser.

Defined (1)

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

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