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