Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@PeeHaa
Created April 1, 2015 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PeeHaa/3e7015617ee8cc32569d to your computer and use it in GitHub Desktop.
Save PeeHaa/3e7015617ee8cc32569d to your computer and use it in GitHub Desktop.
ipv6 handling opcachegui
<?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