[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * IRI parser/serialiser/normaliser 4 * 5 * @package Requests 6 * @subpackage Utilities 7 */ 8 9 /** 10 * IRI parser/serialiser/normaliser 11 * 12 * Copyright (c) 2007-2010, Geoffrey Sneddon and Steve Minutillo. 13 * All rights reserved. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions are met: 17 * 18 * * Redistributions of source code must retain the above copyright notice, 19 * this list of conditions and the following disclaimer. 20 * 21 * * Redistributions in binary form must reproduce the above copyright notice, 22 * this list of conditions and the following disclaimer in the documentation 23 * and/or other materials provided with the distribution. 24 * 25 * * Neither the name of the SimplePie Team nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 33 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 39 * POSSIBILITY OF SUCH DAMAGE. 40 * 41 * @package Requests 42 * @subpackage Utilities 43 * @author Geoffrey Sneddon 44 * @author Steve Minutillo 45 * @copyright 2007-2009 Geoffrey Sneddon and Steve Minutillo 46 * @license http://www.opensource.org/licenses/bsd-license.php 47 * @link http://hg.gsnedders.com/iri/ 48 * 49 * @property string $iri IRI we're working with 50 * @property-read string $uri IRI in URI form, {@see to_uri} 51 * @property string $scheme Scheme part of the IRI 52 * @property string $authority Authority part, formatted for a URI (userinfo + host + port) 53 * @property string $iauthority Authority part of the IRI (userinfo + host + port) 54 * @property string $userinfo Userinfo part, formatted for a URI (after '://' and before '@') 55 * @property string $iuserinfo Userinfo part of the IRI (after '://' and before '@') 56 * @property string $host Host part, formatted for a URI 57 * @property string $ihost Host part of the IRI 58 * @property string $port Port part of the IRI (after ':') 59 * @property string $path Path part, formatted for a URI (after first '/') 60 * @property string $ipath Path part of the IRI (after first '/') 61 * @property string $query Query part, formatted for a URI (after '?') 62 * @property string $iquery Query part of the IRI (after '?') 63 * @property string $fragment Fragment, formatted for a URI (after '#') 64 * @property string $ifragment Fragment part of the IRI (after '#') 65 */ 66 class Requests_IRI { 67 /** 68 * Scheme 69 * 70 * @var string 71 */ 72 protected $scheme = null; 73 74 /** 75 * User Information 76 * 77 * @var string 78 */ 79 protected $iuserinfo = null; 80 81 /** 82 * ihost 83 * 84 * @var string 85 */ 86 protected $ihost = null; 87 88 /** 89 * Port 90 * 91 * @var string 92 */ 93 protected $port = null; 94 95 /** 96 * ipath 97 * 98 * @var string 99 */ 100 protected $ipath = ''; 101 102 /** 103 * iquery 104 * 105 * @var string 106 */ 107 protected $iquery = null; 108 109 /** 110 * ifragment 111 * 112 * @var string 113 */ 114 protected $ifragment = null; 115 116 /** 117 * Normalization database 118 * 119 * Each key is the scheme, each value is an array with each key as the IRI 120 * part and value as the default value for that part. 121 */ 122 protected $normalization = array( 123 'acap' => array( 124 'port' => 674 125 ), 126 'dict' => array( 127 'port' => 2628 128 ), 129 'file' => array( 130 'ihost' => 'localhost' 131 ), 132 'http' => array( 133 'port' => 80, 134 ), 135 'https' => array( 136 'port' => 443, 137 ), 138 ); 139 140 /** 141 * Return the entire IRI when you try and read the object as a string 142 * 143 * @return string 144 */ 145 public function __toString() { 146 return $this->get_iri(); 147 } 148 149 /** 150 * Overload __set() to provide access via properties 151 * 152 * @param string $name Property name 153 * @param mixed $value Property value 154 */ 155 public function __set($name, $value) { 156 if (method_exists($this, 'set_' . $name)) { 157 call_user_func(array($this, 'set_' . $name), $value); 158 } 159 elseif ( 160 $name === 'iauthority' 161 || $name === 'iuserinfo' 162 || $name === 'ihost' 163 || $name === 'ipath' 164 || $name === 'iquery' 165 || $name === 'ifragment' 166 ) { 167 call_user_func(array($this, 'set_' . substr($name, 1)), $value); 168 } 169 } 170 171 /** 172 * Overload __get() to provide access via properties 173 * 174 * @param string $name Property name 175 * @return mixed 176 */ 177 public function __get($name) { 178 // isset() returns false for null, we don't want to do that 179 // Also why we use array_key_exists below instead of isset() 180 $props = get_object_vars($this); 181 182 if ( 183 $name === 'iri' || 184 $name === 'uri' || 185 $name === 'iauthority' || 186 $name === 'authority' 187 ) { 188 $method = 'get_' . $name; 189 $return = $this->$method(); 190 } 191 elseif (array_key_exists($name, $props)) { 192 $return = $this->$name; 193 } 194 // host -> ihost 195 elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) { 196 $name = $prop; 197 $return = $this->$prop; 198 } 199 // ischeme -> scheme 200 elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) { 201 $name = $prop; 202 $return = $this->$prop; 203 } 204 else { 205 trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); 206 $return = null; 207 } 208 209 if ($return === null && isset($this->normalization[$this->scheme][$name])) { 210 return $this->normalization[$this->scheme][$name]; 211 } 212 else { 213 return $return; 214 } 215 } 216 217 /** 218 * Overload __isset() to provide access via properties 219 * 220 * @param string $name Property name 221 * @return bool 222 */ 223 public function __isset($name) { 224 return (method_exists($this, 'get_' . $name) || isset($this->$name)); 225 } 226 227 /** 228 * Overload __unset() to provide access via properties 229 * 230 * @param string $name Property name 231 */ 232 public function __unset($name) { 233 if (method_exists($this, 'set_' . $name)) { 234 call_user_func(array($this, 'set_' . $name), ''); 235 } 236 } 237 238 /** 239 * Create a new IRI object, from a specified string 240 * 241 * @param string|null $iri 242 */ 243 public function __construct($iri = null) { 244 $this->set_iri($iri); 245 } 246 247 /** 248 * Create a new IRI object by resolving a relative IRI 249 * 250 * Returns false if $base is not absolute, otherwise an IRI. 251 * 252 * @param IRI|string $base (Absolute) Base IRI 253 * @param IRI|string $relative Relative IRI 254 * @return IRI|false 255 */ 256 public static function absolutize($base, $relative) { 257 if (!($relative instanceof Requests_IRI)) { 258 $relative = new Requests_IRI($relative); 259 } 260 if (!$relative->is_valid()) { 261 return false; 262 } 263 elseif ($relative->scheme !== null) { 264 return clone $relative; 265 } 266 267 if (!($base instanceof Requests_IRI)) { 268 $base = new Requests_IRI($base); 269 } 270 if ($base->scheme === null || !$base->is_valid()) { 271 return false; 272 } 273 274 if ($relative->get_iri() !== '') { 275 if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) { 276 $target = clone $relative; 277 $target->scheme = $base->scheme; 278 } 279 else { 280 $target = new Requests_IRI; 281 $target->scheme = $base->scheme; 282 $target->iuserinfo = $base->iuserinfo; 283 $target->ihost = $base->ihost; 284 $target->port = $base->port; 285 if ($relative->ipath !== '') { 286 if ($relative->ipath[0] === '/') { 287 $target->ipath = $relative->ipath; 288 } 289 elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') { 290 $target->ipath = '/' . $relative->ipath; 291 } 292 elseif (($last_segment = strrpos($base->ipath, '/')) !== false) { 293 $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; 294 } 295 else { 296 $target->ipath = $relative->ipath; 297 } 298 $target->ipath = $target->remove_dot_segments($target->ipath); 299 $target->iquery = $relative->iquery; 300 } 301 else { 302 $target->ipath = $base->ipath; 303 if ($relative->iquery !== null) { 304 $target->iquery = $relative->iquery; 305 } 306 elseif ($base->iquery !== null) { 307 $target->iquery = $base->iquery; 308 } 309 } 310 $target->ifragment = $relative->ifragment; 311 } 312 } 313 else { 314 $target = clone $base; 315 $target->ifragment = null; 316 } 317 $target->scheme_normalization(); 318 return $target; 319 } 320 321 /** 322 * Parse an IRI into scheme/authority/path/query/fragment segments 323 * 324 * @param string $iri 325 * @return array 326 */ 327 protected function parse_iri($iri) { 328 $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); 329 $has_match = preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match); 330 if (!$has_match) { 331 throw new Requests_Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri); 332 } 333 334 if ($match[1] === '') { 335 $match['scheme'] = null; 336 } 337 if (!isset($match[3]) || $match[3] === '') { 338 $match['authority'] = null; 339 } 340 if (!isset($match[5])) { 341 $match['path'] = ''; 342 } 343 if (!isset($match[6]) || $match[6] === '') { 344 $match['query'] = null; 345 } 346 if (!isset($match[8]) || $match[8] === '') { 347 $match['fragment'] = null; 348 } 349 return $match; 350 } 351 352 /** 353 * Remove dot segments from a path 354 * 355 * @param string $input 356 * @return string 357 */ 358 protected function remove_dot_segments($input) { 359 $output = ''; 360 while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { 361 // A: If the input buffer begins with a prefix of "../" or "./", 362 // then remove that prefix from the input buffer; otherwise, 363 if (strpos($input, '../') === 0) { 364 $input = substr($input, 3); 365 } 366 elseif (strpos($input, './') === 0) { 367 $input = substr($input, 2); 368 } 369 // B: if the input buffer begins with a prefix of "/./" or "/.", 370 // where "." is a complete path segment, then replace that prefix 371 // with "/" in the input buffer; otherwise, 372 elseif (strpos($input, '/./') === 0) { 373 $input = substr($input, 2); 374 } 375 elseif ($input === '/.') { 376 $input = '/'; 377 } 378 // C: if the input buffer begins with a prefix of "/../" or "/..", 379 // where ".." is a complete path segment, then replace that prefix 380 // with "/" in the input buffer and remove the last segment and its 381 // preceding "/" (if any) from the output buffer; otherwise, 382 elseif (strpos($input, '/../') === 0) { 383 $input = substr($input, 3); 384 $output = substr_replace($output, '', strrpos($output, '/')); 385 } 386 elseif ($input === '/..') { 387 $input = '/'; 388 $output = substr_replace($output, '', strrpos($output, '/')); 389 } 390 // D: if the input buffer consists only of "." or "..", then remove 391 // that from the input buffer; otherwise, 392 elseif ($input === '.' || $input === '..') { 393 $input = ''; 394 } 395 // E: move the first path segment in the input buffer to the end of 396 // the output buffer, including the initial "/" character (if any) 397 // and any subsequent characters up to, but not including, the next 398 // "/" character or the end of the input buffer 399 elseif (($pos = strpos($input, '/', 1)) !== false) { 400 $output .= substr($input, 0, $pos); 401 $input = substr_replace($input, '', 0, $pos); 402 } 403 else { 404 $output .= $input; 405 $input = ''; 406 } 407 } 408 return $output . $input; 409 } 410 411 /** 412 * Replace invalid character with percent encoding 413 * 414 * @param string $string Input string 415 * @param string $extra_chars Valid characters not in iunreserved or 416 * iprivate (this is ASCII-only) 417 * @param bool $iprivate Allow iprivate 418 * @return string 419 */ 420 protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) { 421 // Normalize as many pct-encoded sections as possible 422 $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); 423 424 // Replace invalid percent characters 425 $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); 426 427 // Add unreserved and % to $extra_chars (the latter is safe because all 428 // pct-encoded sections are now valid). 429 $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; 430 431 // Now replace any bytes that aren't allowed with their pct-encoded versions 432 $position = 0; 433 $strlen = strlen($string); 434 while (($position += strspn($string, $extra_chars, $position)) < $strlen) { 435 $value = ord($string[$position]); 436 437 // Start position 438 $start = $position; 439 440 // By default we are valid 441 $valid = true; 442 443 // No one byte sequences are valid due to the while. 444 // Two byte sequence: 445 if (($value & 0xE0) === 0xC0) { 446 $character = ($value & 0x1F) << 6; 447 $length = 2; 448 $remaining = 1; 449 } 450 // Three byte sequence: 451 elseif (($value & 0xF0) === 0xE0) { 452 $character = ($value & 0x0F) << 12; 453 $length = 3; 454 $remaining = 2; 455 } 456 // Four byte sequence: 457 elseif (($value & 0xF8) === 0xF0) { 458 $character = ($value & 0x07) << 18; 459 $length = 4; 460 $remaining = 3; 461 } 462 // Invalid byte: 463 else { 464 $valid = false; 465 $length = 1; 466 $remaining = 0; 467 } 468 469 if ($remaining) { 470 if ($position + $length <= $strlen) { 471 for ($position++; $remaining; $position++) { 472 $value = ord($string[$position]); 473 474 // Check that the byte is valid, then add it to the character: 475 if (($value & 0xC0) === 0x80) { 476 $character |= ($value & 0x3F) << (--$remaining * 6); 477 } 478 // If it is invalid, count the sequence as invalid and reprocess the current byte: 479 else { 480 $valid = false; 481 $position--; 482 break; 483 } 484 } 485 } 486 else { 487 $position = $strlen - 1; 488 $valid = false; 489 } 490 } 491 492 // Percent encode anything invalid or not in ucschar 493 if ( 494 // Invalid sequences 495 !$valid 496 // Non-shortest form sequences are invalid 497 || $length > 1 && $character <= 0x7F 498 || $length > 2 && $character <= 0x7FF 499 || $length > 3 && $character <= 0xFFFF 500 // Outside of range of ucschar codepoints 501 // Noncharacters 502 || ($character & 0xFFFE) === 0xFFFE 503 || $character >= 0xFDD0 && $character <= 0xFDEF 504 || ( 505 // Everything else not in ucschar 506 $character > 0xD7FF && $character < 0xF900 507 || $character < 0xA0 508 || $character > 0xEFFFD 509 ) 510 && ( 511 // Everything not in iprivate, if it applies 512 !$iprivate 513 || $character < 0xE000 514 || $character > 0x10FFFD 515 ) 516 ) { 517 // If we were a character, pretend we weren't, but rather an error. 518 if ($valid) { 519 $position--; 520 } 521 522 for ($j = $start; $j <= $position; $j++) { 523 $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); 524 $j += 2; 525 $position += 2; 526 $strlen += 2; 527 } 528 } 529 } 530 531 return $string; 532 } 533 534 /** 535 * Callback function for preg_replace_callback. 536 * 537 * Removes sequences of percent encoded bytes that represent UTF-8 538 * encoded characters in iunreserved 539 * 540 * @param array $match PCRE match 541 * @return string Replacement 542 */ 543 protected function remove_iunreserved_percent_encoded($match) { 544 // As we just have valid percent encoded sequences we can just explode 545 // and ignore the first member of the returned array (an empty string). 546 $bytes = explode('%', $match[0]); 547 548 // Initialize the new string (this is what will be returned) and that 549 // there are no bytes remaining in the current sequence (unsurprising 550 // at the first byte!). 551 $string = ''; 552 $remaining = 0; 553 554 // Loop over each and every byte, and set $value to its value 555 for ($i = 1, $len = count($bytes); $i < $len; $i++) { 556 $value = hexdec($bytes[$i]); 557 558 // If we're the first byte of sequence: 559 if (!$remaining) { 560 // Start position 561 $start = $i; 562 563 // By default we are valid 564 $valid = true; 565 566 // One byte sequence: 567 if ($value <= 0x7F) { 568 $character = $value; 569 $length = 1; 570 } 571 // Two byte sequence: 572 elseif (($value & 0xE0) === 0xC0) { 573 $character = ($value & 0x1F) << 6; 574 $length = 2; 575 $remaining = 1; 576 } 577 // Three byte sequence: 578 elseif (($value & 0xF0) === 0xE0) { 579 $character = ($value & 0x0F) << 12; 580 $length = 3; 581 $remaining = 2; 582 } 583 // Four byte sequence: 584 elseif (($value & 0xF8) === 0xF0) { 585 $character = ($value & 0x07) << 18; 586 $length = 4; 587 $remaining = 3; 588 } 589 // Invalid byte: 590 else { 591 $valid = false; 592 $remaining = 0; 593 } 594 } 595 // Continuation byte: 596 else { 597 // Check that the byte is valid, then add it to the character: 598 if (($value & 0xC0) === 0x80) { 599 $remaining--; 600 $character |= ($value & 0x3F) << ($remaining * 6); 601 } 602 // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: 603 else { 604 $valid = false; 605 $remaining = 0; 606 $i--; 607 } 608 } 609 610 // If we've reached the end of the current byte sequence, append it to Unicode::$data 611 if (!$remaining) { 612 // Percent encode anything invalid or not in iunreserved 613 if ( 614 // Invalid sequences 615 !$valid 616 // Non-shortest form sequences are invalid 617 || $length > 1 && $character <= 0x7F 618 || $length > 2 && $character <= 0x7FF 619 || $length > 3 && $character <= 0xFFFF 620 // Outside of range of iunreserved codepoints 621 || $character < 0x2D 622 || $character > 0xEFFFD 623 // Noncharacters 624 || ($character & 0xFFFE) === 0xFFFE 625 || $character >= 0xFDD0 && $character <= 0xFDEF 626 // Everything else not in iunreserved (this is all BMP) 627 || $character === 0x2F 628 || $character > 0x39 && $character < 0x41 629 || $character > 0x5A && $character < 0x61 630 || $character > 0x7A && $character < 0x7E 631 || $character > 0x7E && $character < 0xA0 632 || $character > 0xD7FF && $character < 0xF900 633 ) { 634 for ($j = $start; $j <= $i; $j++) { 635 $string .= '%' . strtoupper($bytes[$j]); 636 } 637 } 638 else { 639 for ($j = $start; $j <= $i; $j++) { 640 $string .= chr(hexdec($bytes[$j])); 641 } 642 } 643 } 644 } 645 646 // If we have any bytes left over they are invalid (i.e., we are 647 // mid-way through a multi-byte sequence) 648 if ($remaining) { 649 for ($j = $start; $j < $len; $j++) { 650 $string .= '%' . strtoupper($bytes[$j]); 651 } 652 } 653 654 return $string; 655 } 656 657 protected function scheme_normalization() { 658 if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) { 659 $this->iuserinfo = null; 660 } 661 if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) { 662 $this->ihost = null; 663 } 664 if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) { 665 $this->port = null; 666 } 667 if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) { 668 $this->ipath = ''; 669 } 670 if (isset($this->ihost) && empty($this->ipath)) { 671 $this->ipath = '/'; 672 } 673 if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) { 674 $this->iquery = null; 675 } 676 if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) { 677 $this->ifragment = null; 678 } 679 } 680 681 /** 682 * Check if the object represents a valid IRI. This needs to be done on each 683 * call as some things change depending on another part of the IRI. 684 * 685 * @return bool 686 */ 687 public function is_valid() { 688 $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; 689 if ($this->ipath !== '' && 690 ( 691 $isauthority && $this->ipath[0] !== '/' || 692 ( 693 $this->scheme === null && 694 !$isauthority && 695 strpos($this->ipath, ':') !== false && 696 (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) 697 ) 698 ) 699 ) { 700 return false; 701 } 702 703 return true; 704 } 705 706 /** 707 * Set the entire IRI. Returns true on success, false on failure (if there 708 * are any invalid characters). 709 * 710 * @param string $iri 711 * @return bool 712 */ 713 protected function set_iri($iri) { 714 static $cache; 715 if (!$cache) { 716 $cache = array(); 717 } 718 719 if ($iri === null) { 720 return true; 721 } 722 if (isset($cache[$iri])) { 723 list($this->scheme, 724 $this->iuserinfo, 725 $this->ihost, 726 $this->port, 727 $this->ipath, 728 $this->iquery, 729 $this->ifragment, 730 $return) = $cache[$iri]; 731 return $return; 732 } 733 734 $parsed = $this->parse_iri((string) $iri); 735 736 $return = $this->set_scheme($parsed['scheme']) 737 && $this->set_authority($parsed['authority']) 738 && $this->set_path($parsed['path']) 739 && $this->set_query($parsed['query']) 740 && $this->set_fragment($parsed['fragment']); 741 742 $cache[$iri] = array($this->scheme, 743 $this->iuserinfo, 744 $this->ihost, 745 $this->port, 746 $this->ipath, 747 $this->iquery, 748 $this->ifragment, 749 $return); 750 return $return; 751 } 752 753 /** 754 * Set the scheme. Returns true on success, false on failure (if there are 755 * any invalid characters). 756 * 757 * @param string $scheme 758 * @return bool 759 */ 760 protected function set_scheme($scheme) { 761 if ($scheme === null) { 762 $this->scheme = null; 763 } 764 elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) { 765 $this->scheme = null; 766 return false; 767 } 768 else { 769 $this->scheme = strtolower($scheme); 770 } 771 return true; 772 } 773 774 /** 775 * Set the authority. Returns true on success, false on failure (if there are 776 * any invalid characters). 777 * 778 * @param string $authority 779 * @return bool 780 */ 781 protected function set_authority($authority) { 782 static $cache; 783 if (!$cache) { 784 $cache = array(); 785 } 786 787 if ($authority === null) { 788 $this->iuserinfo = null; 789 $this->ihost = null; 790 $this->port = null; 791 return true; 792 } 793 if (isset($cache[$authority])) { 794 list($this->iuserinfo, 795 $this->ihost, 796 $this->port, 797 $return) = $cache[$authority]; 798 799 return $return; 800 } 801 802 $remaining = $authority; 803 if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { 804 $iuserinfo = substr($remaining, 0, $iuserinfo_end); 805 $remaining = substr($remaining, $iuserinfo_end + 1); 806 } 807 else { 808 $iuserinfo = null; 809 } 810 if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { 811 $port = substr($remaining, $port_start + 1); 812 if ($port === false || $port === '') { 813 $port = null; 814 } 815 $remaining = substr($remaining, 0, $port_start); 816 } 817 else { 818 $port = null; 819 } 820 821 $return = $this->set_userinfo($iuserinfo) && 822 $this->set_host($remaining) && 823 $this->set_port($port); 824 825 $cache[$authority] = array($this->iuserinfo, 826 $this->ihost, 827 $this->port, 828 $return); 829 830 return $return; 831 } 832 833 /** 834 * Set the iuserinfo. 835 * 836 * @param string $iuserinfo 837 * @return bool 838 */ 839 protected function set_userinfo($iuserinfo) { 840 if ($iuserinfo === null) { 841 $this->iuserinfo = null; 842 } 843 else { 844 $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); 845 $this->scheme_normalization(); 846 } 847 848 return true; 849 } 850 851 /** 852 * Set the ihost. Returns true on success, false on failure (if there are 853 * any invalid characters). 854 * 855 * @param string $ihost 856 * @return bool 857 */ 858 protected function set_host($ihost) { 859 if ($ihost === null) { 860 $this->ihost = null; 861 return true; 862 } 863 if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') { 864 if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) { 865 $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']'; 866 } 867 else { 868 $this->ihost = null; 869 return false; 870 } 871 } 872 else { 873 $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); 874 875 // Lowercase, but ignore pct-encoded sections (as they should 876 // remain uppercase). This must be done after the previous step 877 // as that can add unescaped characters. 878 $position = 0; 879 $strlen = strlen($ihost); 880 while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) { 881 if ($ihost[$position] === '%') { 882 $position += 3; 883 } 884 else { 885 $ihost[$position] = strtolower($ihost[$position]); 886 $position++; 887 } 888 } 889 890 $this->ihost = $ihost; 891 } 892 893 $this->scheme_normalization(); 894 895 return true; 896 } 897 898 /** 899 * Set the port. Returns true on success, false on failure (if there are 900 * any invalid characters). 901 * 902 * @param string $port 903 * @return bool 904 */ 905 protected function set_port($port) { 906 if ($port === null) { 907 $this->port = null; 908 return true; 909 } 910 911 if (strspn($port, '0123456789') === strlen($port)) { 912 $this->port = (int) $port; 913 $this->scheme_normalization(); 914 return true; 915 } 916 917 $this->port = null; 918 return false; 919 } 920 921 /** 922 * Set the ipath. 923 * 924 * @param string $ipath 925 * @return bool 926 */ 927 protected function set_path($ipath) { 928 static $cache; 929 if (!$cache) { 930 $cache = array(); 931 } 932 933 $ipath = (string) $ipath; 934 935 if (isset($cache[$ipath])) { 936 $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; 937 } 938 else { 939 $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); 940 $removed = $this->remove_dot_segments($valid); 941 942 $cache[$ipath] = array($valid, $removed); 943 $this->ipath = ($this->scheme !== null) ? $removed : $valid; 944 } 945 $this->scheme_normalization(); 946 return true; 947 } 948 949 /** 950 * Set the iquery. 951 * 952 * @param string $iquery 953 * @return bool 954 */ 955 protected function set_query($iquery) { 956 if ($iquery === null) { 957 $this->iquery = null; 958 } 959 else { 960 $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); 961 $this->scheme_normalization(); 962 } 963 return true; 964 } 965 966 /** 967 * Set the ifragment. 968 * 969 * @param string $ifragment 970 * @return bool 971 */ 972 protected function set_fragment($ifragment) { 973 if ($ifragment === null) { 974 $this->ifragment = null; 975 } 976 else { 977 $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); 978 $this->scheme_normalization(); 979 } 980 return true; 981 } 982 983 /** 984 * Convert an IRI to a URI (or parts thereof) 985 * 986 * @param string|bool IRI to convert (or false from {@see get_iri}) 987 * @return string|false URI if IRI is valid, false otherwise. 988 */ 989 protected function to_uri($string) { 990 if (!is_string($string)) { 991 return false; 992 } 993 994 static $non_ascii; 995 if (!$non_ascii) { 996 $non_ascii = implode('', range("\x80", "\xFF")); 997 } 998 999 $position = 0; 1000 $strlen = strlen($string); 1001 while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { 1002 $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); 1003 $position += 3; 1004 $strlen += 2; 1005 } 1006 1007 return $string; 1008 } 1009 1010 /** 1011 * Get the complete IRI 1012 * 1013 * @return string 1014 */ 1015 protected function get_iri() { 1016 if (!$this->is_valid()) { 1017 return false; 1018 } 1019 1020 $iri = ''; 1021 if ($this->scheme !== null) { 1022 $iri .= $this->scheme . ':'; 1023 } 1024 if (($iauthority = $this->get_iauthority()) !== null) { 1025 $iri .= '//' . $iauthority; 1026 } 1027 $iri .= $this->ipath; 1028 if ($this->iquery !== null) { 1029 $iri .= '?' . $this->iquery; 1030 } 1031 if ($this->ifragment !== null) { 1032 $iri .= '#' . $this->ifragment; 1033 } 1034 1035 return $iri; 1036 } 1037 1038 /** 1039 * Get the complete URI 1040 * 1041 * @return string 1042 */ 1043 protected function get_uri() { 1044 return $this->to_uri($this->get_iri()); 1045 } 1046 1047 /** 1048 * Get the complete iauthority 1049 * 1050 * @return string 1051 */ 1052 protected function get_iauthority() { 1053 if ($this->iuserinfo === null && $this->ihost === null && $this->port === null) { 1054 return null; 1055 } 1056 1057 $iauthority = ''; 1058 if ($this->iuserinfo !== null) { 1059 $iauthority .= $this->iuserinfo . '@'; 1060 } 1061 if ($this->ihost !== null) { 1062 $iauthority .= $this->ihost; 1063 } 1064 if ($this->port !== null) { 1065 $iauthority .= ':' . $this->port; 1066 } 1067 return $iauthority; 1068 } 1069 1070 /** 1071 * Get the complete authority 1072 * 1073 * @return string 1074 */ 1075 protected function get_authority() { 1076 $iauthority = $this->get_iauthority(); 1077 if (is_string($iauthority)) { 1078 return $this->to_uri($iauthority); 1079 } 1080 else { 1081 return $iauthority; 1082 } 1083 } 1084 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sat Jan 23 01:00:05 2021 | Cross-referenced by PHPXref 0.7.1 |