Created
April 1, 2015 12:01
-
-
Save PeeHaa/3e7015617ee8cc32569d to your computer and use it in GitHub Desktop.
ipv6 handling opcachegui
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class IPv6Address | |
{ | |
/** | |
* @var int[] | |
*/ | |
private $words; | |
/** | |
* @var string | |
*/ | |
private $binary; | |
/** | |
* @var string | |
*/ | |
private $fullString; | |
/** | |
* @var string | |
*/ | |
private $rfc5952String; | |
/** | |
* @param string[] $blocks | |
* @param int $offset | |
* @param int[] $words | |
* @return int[] | |
*/ | |
private function parseBlocksWithNoEmptyBlockToWords($blocks, $offset = 0, $words = []) | |
{ | |
foreach ($blocks as $i => $block) { | |
if ($block === '') { | |
throw new \InvalidArgumentException('Invalid address: cannot contain multiple empty blocks'); | |
} | |
if (!preg_match('/^[0-9a-f]{1,4}$/i', $block)) { | |
throw new \InvalidArgumentException('Invalid address: invalid data "' . $block . '" at block ' . ($offset + $i + 1)); | |
} | |
$words[$offset + $i] = hexdec($block); | |
} | |
return $words; | |
} | |
/** | |
* @param string $address | |
* @return int[] | |
*/ | |
private function parseAddressWithEmptyBlockOnLeftToWords($address) | |
{ | |
$blocks = explode(':', substr($address, 2)); | |
$offset = 8 - count($blocks); | |
return $this->parseBlocksWithNoEmptyBlockToWords($blocks, $offset, array_fill(0, $offset, 0)); | |
} | |
/** | |
* @param string $address | |
* @return int[] | |
*/ | |
private function parseAddressWithEmptyBlockOnRightToWords($address) | |
{ | |
return array_pad($this->parseBlocksWithNoEmptyBlockToWords(explode(':', substr($address, 0, -2))), 8, 0); | |
} | |
/** | |
* @param string $address | |
* @return int[] | |
* @throws \InvalidArgumentException | |
*/ | |
protected function parseAddressToWords($address) | |
{ | |
if ($address === '::') { | |
$words = [0, 0, 0, 0, 0, 0, 0, 0]; | |
} else if (substr($address, 0, 2) === '::') { | |
$words = $this->parseAddressWithEmptyBlockOnLeftToWords($address); | |
} else if (substr($address, -2) === '::') { | |
$words = $this->parseAddressWithEmptyBlockOnRightToWords($address); | |
} else { | |
$haveEmptyBlock = false; | |
$blocks = explode(':', $address); | |
$words = []; | |
foreach ($blocks as $i => $block) { | |
if ($block === '') { | |
if ($haveEmptyBlock) { | |
throw new \InvalidArgumentException('Invalid address: cannot contain multiple empty blocks'); | |
} | |
$haveEmptyBlock = true; | |
$words = array_pad($words, 9 - (count($blocks) - $i), 0); | |
} else if (preg_match('/^[0-9a-f]{1,4}$/i', $block)) { | |
$words[] = hexdec($block); | |
} else { | |
throw new \InvalidArgumentException('Invalid address: invalid data "' . $block . '" at block ' . ($i + 1)); | |
} | |
} | |
} | |
if (count($words) !== 8) { | |
throw new \InvalidArgumentException('Invalid address: must contain exactly 8 blocks'); | |
} | |
return $words; | |
} | |
/** | |
* @param string $address | |
* @throws \InvalidArgumentException | |
*/ | |
public function __construct($address) | |
{ | |
$this->words = $this->parseAddressToWords($address); | |
} | |
/** | |
* @return string | |
*/ | |
public function __toString() | |
{ | |
return $this->getRFC5952String(); | |
} | |
/** | |
* @return string | |
*/ | |
public function getBinary() | |
{ | |
return $this->binary ?: $this->binary = pack('n*', $this->words); | |
} | |
/** | |
* @return int[] | |
*/ | |
public function getWords() | |
{ | |
return $this->words; | |
} | |
/** | |
* @return string | |
*/ | |
public function getFullString() | |
{ | |
return $this->fullString ?: $this->fullString = implode(':', array_map(function($word) { | |
return str_pad(dechex($word), 4, '0', STR_PAD_LEFT); | |
}, $this->words)); | |
} | |
/** | |
* @return string | |
*/ | |
public function getRFC5952String() | |
{ | |
if (isset($this->rfc5952String)) { | |
return $this->rfc5952String; | |
} | |
$longestZeroBlockIndex = -1; | |
$longestZeroBlockLength = -1; | |
$currentZeroBlockIndex = -1; | |
$currentZeroBlockLength = -1; | |
foreach ($this->words as $i => $word) { | |
if ($word !== 0) { | |
if ($currentZeroBlockIndex !== -1 && $currentZeroBlockLength > $longestZeroBlockLength) { | |
$longestZeroBlockIndex = $currentZeroBlockIndex; | |
$longestZeroBlockLength = $currentZeroBlockLength; | |
$currentZeroBlockIndex = -1; | |
} | |
} else if ($currentZeroBlockIndex === -1) { | |
$currentZeroBlockIndex = $i; | |
$currentZeroBlockLength = 1; | |
} else { | |
$currentZeroBlockLength++; | |
} | |
} | |
if ($currentZeroBlockIndex !== -1 && $currentZeroBlockLength > $longestZeroBlockLength) { | |
$longestZeroBlockIndex = $currentZeroBlockIndex; | |
$longestZeroBlockLength = $currentZeroBlockLength; | |
} | |
$blocks = []; | |
if ($longestZeroBlockLength === 8) { | |
$blocks = ['', '', '']; | |
} else if ($longestZeroBlockLength > 1) { | |
for ($i = 0; $i < $longestZeroBlockIndex; $i++) { | |
$blocks[] = dechex($this->words[$i]); | |
} | |
$blocks[] = ''; | |
if ($longestZeroBlockIndex === 0 || $longestZeroBlockIndex + $longestZeroBlockLength === 8) { | |
$blocks[] = ''; | |
} | |
for ($i = $longestZeroBlockIndex + $longestZeroBlockLength; $i < 8; $i++) { | |
$blocks[] = dechex($this->words[$i]); | |
} | |
} else { | |
$blocks = array_map('dechex', $this->words); | |
} | |
return $this->rfc5952String = implode(':', $blocks); | |
} | |
/** | |
* @param IPv6Address|string $address | |
* @return bool | |
*/ | |
public function equalTo($address) | |
{ | |
if (!$address instanceof self) { | |
$address = new self($address); | |
} | |
return $address->getWords() === $this->words; | |
} | |
/** | |
* @param IPv6Address|string $address | |
* @return bool | |
*/ | |
public function lessThan($address) | |
{ | |
if (!$address instanceof self) { | |
$address = new self($address); | |
} | |
foreach ($address->words as $i => $word) { | |
if ($word < $this->words[$i]) { | |
return false; | |
} else if ($this->words[$i] < $word) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* @param IPv6Address|string $address | |
* @return bool | |
*/ | |
public function greaterThan($address) | |
{ | |
if (!$address instanceof self) { | |
$address = new self($address); | |
} | |
return $address->lessThan($this); | |
} | |
/** | |
* @param IPv6Address|string $start | |
* @param IPv6Address|string $end | |
* @return bool | |
*/ | |
public function inRange($start, $end) | |
{ | |
return ($this->greaterThan($start) || $this->equalTo($start)) | |
&& ($this->lessThan($end) || $this->equalTo($end)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment