[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/Requests/ -> IRI.php (source)

   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  }


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7