Advertisement
Guest User

IPv6Address

a guest
Apr 1st, 2015
300
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.37 KB | None | 0 0
  1. <?php
  2.  
  3. class IPv6Address
  4. {
  5.     /**
  6.      * @var int[]
  7.      */
  8.     private $words;
  9.  
  10.     /**
  11.      * @var string
  12.      */
  13.     private $binary;
  14.  
  15.     /**
  16.      * @var string
  17.      */
  18.     private $fullString;
  19.  
  20.     /**
  21.      * @var string
  22.      */
  23.     private $rfc5952String;
  24.  
  25.     /**
  26.      * @param string[] $blocks
  27.      * @param int $offset
  28.      * @param int[] $words
  29.      * @return int[]
  30.      */
  31.     private function parseBlocksWithNoEmptyBlockToWords($blocks, $offset = 0, $words = [])
  32.     {
  33.         foreach ($blocks as $i => $block) {
  34.             if ($block === '') {
  35.                 throw new \InvalidArgumentException('Invalid address: cannot contain multiple empty blocks');
  36.             }
  37.  
  38.             if (!preg_match('/^[0-9a-f]{1,4}$/i', $block)) {
  39.                 throw new \InvalidArgumentException('Invalid address: invalid data "' . $block . '" at block ' . ($offset + $i + 1));
  40.             }
  41.  
  42.             $words[$offset + $i] = hexdec($block);
  43.         }
  44.  
  45.         return $words;
  46.     }
  47.  
  48.     /**
  49.      * @param string $address
  50.      * @return int[]
  51.      */
  52.     private function parseAddressWithEmptyBlockOnLeftToWords($address)
  53.     {
  54.         $blocks = explode(':', substr($address, 2));
  55.         $offset = 8 - count($blocks);
  56.  
  57.         return $this->parseBlocksWithNoEmptyBlockToWords($blocks, $offset, array_fill(0, $offset, 0));
  58.     }
  59.  
  60.     /**
  61.      * @param string $address
  62.      * @return int[]
  63.      */
  64.     private function parseAddressWithEmptyBlockOnRightToWords($address)
  65.     {
  66.         return array_pad($this->parseBlocksWithNoEmptyBlockToWords(explode(':', substr($address, 0, -2))), 8, 0);
  67.     }
  68.  
  69.     /**
  70.      * @param string $address
  71.      * @return int[]
  72.      * @throws \InvalidArgumentException
  73.      */
  74.     protected function parseAddressToWords($address)
  75.     {
  76.         if ($address === '::') {
  77.             $words = [0, 0, 0, 0, 0, 0, 0, 0];
  78.         } else if (substr($address, 0, 2) === '::') {
  79.             $words = $this->parseAddressWithEmptyBlockOnLeftToWords($address);
  80.         } else if (substr($address, -2) === '::') {
  81.             $words = $this->parseAddressWithEmptyBlockOnRightToWords($address);
  82.         } else {
  83.             $haveEmptyBlock = false;
  84.             $blocks = explode(':', $address);
  85.             $words = [];
  86.  
  87.             foreach ($blocks as $i => $block) {
  88.                 if ($block === '') {
  89.                     if ($haveEmptyBlock) {
  90.                         throw new \InvalidArgumentException('Invalid address: cannot contain multiple empty blocks');
  91.                     }
  92.  
  93.                     $haveEmptyBlock = true;
  94.                     $words = array_pad($words, 9 - (count($blocks) - $i), 0);
  95.                 } else if (preg_match('/^[0-9a-f]{1,4}$/i', $block)) {
  96.                     $words[] = hexdec($block);
  97.                 } else {
  98.                     throw new \InvalidArgumentException('Invalid address: invalid data "' . $block . '" at block ' . ($i + 1));
  99.                 }
  100.             }
  101.         }
  102.  
  103.         if (count($words) !== 8) {
  104.             throw new \InvalidArgumentException('Invalid address: must contain exactly 8 blocks');
  105.         }
  106.  
  107.         return $words;
  108.     }
  109.  
  110.     /**
  111.      * @param string $address
  112.      * @throws \InvalidArgumentException
  113.      */
  114.     public function __construct($address)
  115.     {
  116.         $this->words = $this->parseAddressToWords($address);
  117.     }
  118.  
  119.     /**
  120.      * @return string
  121.      */
  122.     public function __toString()
  123.     {
  124.         return $this->getRFC5952String();
  125.     }
  126.  
  127.     /**
  128.      * @return string
  129.      */
  130.     public function getBinary()
  131.     {
  132.         return $this->binary ?: $this->binary = pack('n*', $this->words);
  133.     }
  134.  
  135.     /**
  136.      * @return int[]
  137.      */
  138.     public function getWords()
  139.     {
  140.         return $this->words;
  141.     }
  142.  
  143.     /**
  144.      * @return string
  145.      */
  146.     public function getFullString()
  147.     {
  148.         return $this->fullString ?: $this->fullString = implode(':', array_map(function($word) {
  149.             return str_pad(dechex($word), 4, '0', STR_PAD_LEFT);
  150.         }, $this->words));
  151.     }
  152.  
  153.     /**
  154.      * @return string
  155.      */
  156.     public function getRFC5952String()
  157.     {
  158.         if (isset($this->rfc5952String)) {
  159.             return $this->rfc5952String;
  160.         }
  161.  
  162.         $longestZeroBlockIndex = -1;
  163.         $longestZeroBlockLength = -1;
  164.         $currentZeroBlockIndex = -1;
  165.         $currentZeroBlockLength = -1;
  166.  
  167.         foreach ($this->words as $i => $word) {
  168.             if ($word !== 0) {
  169.                 if ($currentZeroBlockIndex !== -1 && $currentZeroBlockLength > $longestZeroBlockLength) {
  170.                     $longestZeroBlockIndex = $currentZeroBlockIndex;
  171.                     $longestZeroBlockLength = $currentZeroBlockLength;
  172.                     $currentZeroBlockIndex = -1;
  173.                 }
  174.             } else if ($currentZeroBlockIndex === -1) {
  175.                 $currentZeroBlockIndex = $i;
  176.                 $currentZeroBlockLength = 1;
  177.             } else {
  178.                 $currentZeroBlockLength++;
  179.             }
  180.         }
  181.  
  182.         if ($currentZeroBlockIndex !== -1 && $currentZeroBlockLength > $longestZeroBlockLength) {
  183.             $longestZeroBlockIndex = $currentZeroBlockIndex;
  184.             $longestZeroBlockLength = $currentZeroBlockLength;
  185.         }
  186.  
  187.         $blocks = [];
  188.  
  189.         if ($longestZeroBlockLength === 8) {
  190.             $blocks = ['', '', ''];
  191.         } else if ($longestZeroBlockLength > 1) {
  192.             for ($i = 0; $i < $longestZeroBlockIndex; $i++) {
  193.                 $blocks[] = dechex($this->words[$i]);
  194.             }
  195.  
  196.             $blocks[] = '';
  197.             if ($longestZeroBlockIndex === 0 || $longestZeroBlockIndex + $longestZeroBlockLength === 8) {
  198.                 $blocks[] = '';
  199.             }
  200.  
  201.             for ($i = $longestZeroBlockIndex + $longestZeroBlockLength; $i < 8; $i++) {
  202.                 $blocks[] = dechex($this->words[$i]);
  203.             }
  204.         } else {
  205.             $blocks = array_map('dechex', $this->words);
  206.         }
  207.  
  208.         return $this->rfc5952String = implode(':', $blocks);
  209.     }
  210.  
  211.     /**
  212.      * @param IPv6Address|string $address
  213.      * @return bool
  214.      */
  215.     public function equalTo($address)
  216.     {
  217.         if (!$address instanceof self) {
  218.             $address = new self($address);
  219.         }
  220.  
  221.         return $address->getWords() === $this->words;
  222.     }
  223.  
  224.     /**
  225.      * @param IPv6Address|string $address
  226.      * @return bool
  227.      */
  228.     public function lessThan($address)
  229.     {
  230.         if (!$address instanceof self) {
  231.             $address = new self($address);
  232.         }
  233.  
  234.         foreach ($address->words as $i => $word) {
  235.             if ($word < $this->words[$i]) {
  236.                 return false;
  237.             } else if ($this->words[$i] < $word) {
  238.                 return true;
  239.             }
  240.         }
  241.  
  242.         return false;
  243.     }
  244.  
  245.     /**
  246.      * @param IPv6Address|string $address
  247.      * @return bool
  248.      */
  249.     public function greaterThan($address)
  250.     {
  251.         if (!$address instanceof self) {
  252.             $address = new self($address);
  253.         }
  254.  
  255.         return $address->lessThan($this);
  256.     }
  257.  
  258.     /**
  259.      * @param IPv6Address|string $start
  260.      * @param IPv6Address|string $end
  261.      * @return bool
  262.      */
  263.     public function inRange($start, $end)
  264.     {
  265.         return ($this->greaterThan($start) || $this->equalTo($start))
  266.             && ($this->lessThan($end) || $this->equalTo($end));
  267.     }
  268. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement