[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/SimplePie/src/Net/ -> IPv6.php (source)

   1  <?php
   2  
   3  // SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
   4  // SPDX-License-Identifier: BSD-3-Clause
   5  
   6  declare(strict_types=1);
   7  
   8  namespace SimplePie\Net;
   9  
  10  /**
  11   * Class to validate and to work with IPv6 addresses.
  12   *
  13   * @copyright 2003-2005 The PHP Group
  14   * @license http://www.opensource.org/licenses/bsd-license.php
  15   * @link http://pear.php.net/package/Net_IPv6
  16   * @author Alexander Merz <alexander.merz@web.de>
  17   * @author elfrink at introweb dot nl
  18   * @author Josh Peck <jmp at joshpeck dot org>
  19   * @author Sam Sneddon <geoffers@gmail.com>
  20   */
  21  class IPv6
  22  {
  23      /**
  24       * Uncompresses an IPv6 address
  25       *
  26       * RFC 4291 allows you to compress consecutive zero pieces in an address to
  27       * '::'. This method expects a valid IPv6 address and expands the '::' to
  28       * the required number of zero pieces.
  29       *
  30       * Example:  FF01::101   ->  FF01:0:0:0:0:0:0:101
  31       *           ::1         ->  0:0:0:0:0:0:0:1
  32       *
  33       * @author Alexander Merz <alexander.merz@web.de>
  34       * @author elfrink at introweb dot nl
  35       * @author Josh Peck <jmp at joshpeck dot org>
  36       * @copyright 2003-2005 The PHP Group
  37       * @license http://www.opensource.org/licenses/bsd-license.php
  38       * @param string $ip An IPv6 address
  39       * @return string The uncompressed IPv6 address
  40       */
  41      public static function uncompress(string $ip)
  42      {
  43          $c1 = -1;
  44          $c2 = -1;
  45          if (substr_count($ip, '::') === 1) {
  46              [$ip1, $ip2] = explode('::', $ip);
  47              if ($ip1 === '') {
  48                  $c1 = -1;
  49              } else {
  50                  $c1 = substr_count($ip1, ':');
  51              }
  52              if ($ip2 === '') {
  53                  $c2 = -1;
  54              } else {
  55                  $c2 = substr_count($ip2, ':');
  56              }
  57              if (strpos($ip2, '.') !== false) {
  58                  $c2++;
  59              }
  60              // ::
  61              if ($c1 === -1 && $c2 === -1) {
  62                  $ip = '0:0:0:0:0:0:0:0';
  63              }
  64              // ::xxx
  65              elseif ($c1 === -1) {
  66                  $fill = str_repeat('0:', 7 - $c2);
  67                  $ip = str_replace('::', $fill, $ip);
  68              }
  69              // xxx::
  70              elseif ($c2 === -1) {
  71                  $fill = str_repeat(':0', 7 - $c1);
  72                  $ip = str_replace('::', $fill, $ip);
  73              }
  74              // xxx::xxx
  75              else {
  76                  $fill = ':' . str_repeat('0:', 6 - $c2 - $c1);
  77                  $ip = str_replace('::', $fill, $ip);
  78              }
  79          }
  80          return $ip;
  81      }
  82  
  83      /**
  84       * Compresses an IPv6 address
  85       *
  86       * RFC 4291 allows you to compress consecutive zero pieces in an address to
  87       * '::'. This method expects a valid IPv6 address and compresses consecutive
  88       * zero pieces to '::'.
  89       *
  90       * Example:  FF01:0:0:0:0:0:0:101   ->  FF01::101
  91       *           0:0:0:0:0:0:0:1        ->  ::1
  92       *
  93       * @see uncompress()
  94       * @param string $ip An IPv6 address
  95       * @return string The compressed IPv6 address
  96       */
  97      public static function compress(string $ip)
  98      {
  99          // Prepare the IP to be compressed
 100          $ip = self::uncompress($ip);
 101          $ip_parts = self::split_v6_v4($ip);
 102  
 103          // Replace all leading zeros
 104          $ip_parts[0] = (string) preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]);
 105  
 106          // Find bunches of zeros
 107          if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) {
 108              $max = 0;
 109              $pos = null;
 110              foreach ($matches[0] as $match) {
 111                  if (strlen($match[0]) > $max) {
 112                      $max = strlen($match[0]);
 113                      $pos = $match[1];
 114                  }
 115              }
 116  
 117              assert($pos !== null, 'For PHPStan: Since the regex matched, there is at least one match. And because the pattern is non-empty, the loop will always end with $pos ≥ 1.');
 118              $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max);
 119          }
 120  
 121          if ($ip_parts[1] !== '') {
 122              return implode(':', $ip_parts);
 123          }
 124  
 125          return $ip_parts[0];
 126      }
 127  
 128      /**
 129       * Splits an IPv6 address into the IPv6 and IPv4 representation parts
 130       *
 131       * RFC 4291 allows you to represent the last two parts of an IPv6 address
 132       * using the standard IPv4 representation
 133       *
 134       * Example:  0:0:0:0:0:0:13.1.68.3
 135       *           0:0:0:0:0:FFFF:129.144.52.38
 136       *
 137       * @param string $ip An IPv6 address
 138       * @return array{string, string} [0] contains the IPv6 represented part, and [1] the IPv4 represented part
 139       */
 140      private static function split_v6_v4(string $ip): array
 141      {
 142          if (strpos($ip, '.') !== false) {
 143              $pos = strrpos($ip, ':');
 144              assert($pos !== false, 'For PHPStan: IPv6 address must contain colon, since split_v6_v4 is only ever called after uncompress.');
 145              $ipv6_part = substr($ip, 0, $pos);
 146              $ipv4_part = substr($ip, $pos + 1);
 147              return [$ipv6_part, $ipv4_part];
 148          }
 149  
 150          return [$ip, ''];
 151      }
 152  
 153      /**
 154       * Checks an IPv6 address
 155       *
 156       * Checks if the given IP is a valid IPv6 address
 157       *
 158       * @param string $ip An IPv6 address
 159       * @return bool true if $ip is a valid IPv6 address
 160       */
 161      public static function check_ipv6(string $ip)
 162      {
 163          $ip = self::uncompress($ip);
 164          [$ipv6, $ipv4] = self::split_v6_v4($ip);
 165          $ipv6 = explode(':', $ipv6);
 166          $ipv4 = explode('.', $ipv4);
 167          if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) {
 168              foreach ($ipv6 as $ipv6_part) {
 169                  // The section can't be empty
 170                  if ($ipv6_part === '') {
 171                      return false;
 172                  }
 173  
 174                  // Nor can it be over four characters
 175                  if (strlen($ipv6_part) > 4) {
 176                      return false;
 177                  }
 178  
 179                  // Remove leading zeros (this is safe because of the above)
 180                  $ipv6_part = ltrim($ipv6_part, '0');
 181                  if ($ipv6_part === '') {
 182                      $ipv6_part = '0';
 183                  }
 184  
 185                  // Check the value is valid
 186                  $value = hexdec($ipv6_part);
 187                  if ($value < 0 || $value > 0xFFFF) {
 188                      return false;
 189                  }
 190                  assert(is_int($value), 'For PHPStan: $value is only float when $ipv6_part > PHP_INT_MAX');
 191                  if (dechex($value) !== strtolower($ipv6_part)) {
 192                      return false;
 193                  }
 194              }
 195              if (count($ipv4) === 4) {
 196                  foreach ($ipv4 as $ipv4_part) {
 197                      $value = (int) $ipv4_part;
 198                      if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) {
 199                          return false;
 200                      }
 201                  }
 202              }
 203              return true;
 204          }
 205  
 206          return false;
 207      }
 208  
 209      /**
 210       * Checks if the given IP is a valid IPv6 address
 211       *
 212       * @codeCoverageIgnore
 213       * @deprecated Use {@see IPv6::check_ipv6()} instead
 214       * @see check_ipv6
 215       * @param string $ip An IPv6 address
 216       * @return bool true if $ip is a valid IPv6 address
 217       */
 218      public static function checkIPv6(string $ip)
 219      {
 220          return self::check_ipv6($ip);
 221      }
 222  }
 223  
 224  class_alias('SimplePie\Net\IPv6', 'SimplePie_Net_IPv6');


Generated : Fri Oct 10 08:20:03 2025 Cross-referenced by PHPXref