protected static function newFromString()

in src/ip/PhutilIPv6Address.php [20:129]


  protected static function newFromString($str) {
    $parts = explode(':', $str);
    if (count($parts) > 8) {
      throw new Exception(
        pht(
          'IP address "%s" is not properly formatted: is has too many '.
          'parts. Expected a maximum of 7 colons, like "%s".',
          $str,
          '1:2:3:4:a:b:c:d'));
    }

    if (count($parts) < 3) {
      throw new Exception(
        pht(
          'IP address "%s" is not properly formated: it has too few '.
          'parts. Expected a minimum of 2 colons, like "%s".',
          $str,
          '::1'));
    }

    // Look for leading or trailing empty parts. These are valid if the string
    // begins or ends like "::", "::1", or "1::", but not valid otherwise.
    $has_omission = false;
    if ($str === '::') {
      $parts = array(null);
      $has_omission = true;
    } else if ($parts[0] === '') {
      if ($parts[1] === '') {
        unset($parts[1]);
        $parts[0] = null;
        $parts = array_values($parts);
        $has_omission = true;
      } else {
        throw new Exception(
          pht(
            'IP address "%s" is not properly formatted: an address with '.
            'omitted leading sements must begin with "::".',
            $str));
      }
    } else if (last($parts) === '') {
      if ($parts[count($parts) - 2] === '') {
        array_pop($parts);
        $parts[count($parts) - 1] = null;
        $parts = array_values($parts);
        $has_omission = true;
      } else {
        throw new Exception(
          pht(
            'IP address "%s" is not properly formatted: an address with '.
            'omitted trailing segments must end with "::".',
            $str));
      }
    }

    foreach ($parts as $idx => $part) {
      if ($part !== '') {
        continue;
      }

      if ($has_omission) {
        throw new Exception(
          pht(
            'IP address "%s" is not properly formatted: an address may '.
            'only contain a maximum of one subsequence omitted with "::".',
            $str));
      }

      $has_omission = true;
      $parts[$idx] = null;
    }

    if (!$has_omission) {
      if (count($parts) !== 8) {
        throw new Exception(
          pht(
            'IP address "%s" is not properly formatted: an address must '.
            'contain exactly 8 segments, or omit a subsequence of segments '.
            'with "::".',
            $str));
      }
    }

    $values = array();
    foreach ($parts as $idx => $part) {
      // This is a "::" segment, so fill in any missing values with 0.
      if ($part === null) {
        for ($ii = count($parts); $ii <= 8; $ii++) {
          $values[] = 0;
        }
        continue;
      }

      if (!preg_match('/^[0-9a-fA-F]{1,4}\z/', $part)) {
        throw new Exception(
          pht(
            'IP address "%s" is not properly formatted: the segments of '.
            'an address must be hexadecimal values between "0000" and "ffff", '.
            'inclusive. Segment "%s" is not.',
            $str,
            $part));
      }

      $values[] = (int)hexdec($part);
    }

    $obj = new self();
    $obj->values = $values;

    return $obj;
  }