Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3769563
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/Network/IPRange.php b/src/Network/IPRange.php
new file mode 100644
index 0000000..58f2c99
--- /dev/null
+++ b/src/Network/IPRange.php
@@ -0,0 +1,67 @@
+<?php
+declare(strict_types=1);
+
+namespace Keruald\OmniTools\Network;
+
+use Countable;
+use InvalidArgumentException;
+
+abstract class IPRange implements Countable {
+
+ ///
+ /// Constructors
+ ///
+
+ public static function from (string $format) : self {
+ $data = explode("/", $format, 2);
+
+ if (IP::isIPv4($data[0])) {
+ return new IPv4Range($data[0], (int)$data[1]);
+ }
+
+ if (IP::isIPv6($data[0])) {
+ return new IPv6Range($data[0], (int)$data[1]);
+ }
+
+ throw new InvalidArgumentException();
+ }
+
+ ///
+ /// Getters and setters
+ ///
+
+ public abstract function getBase () : string;
+ public abstract function setBase (string $base) : void;
+
+ public abstract function getNetworkBits () : int;
+ public abstract function setNetworkBits (int $networkBits) : void;
+
+ ///
+ /// Helper methods
+ ///
+
+ public abstract function contains (string $ip) : bool;
+ public abstract function getFirst () : string;
+ public abstract function getLast () : string;
+
+ ///
+ /// Countable methods
+ ///
+
+ public abstract function count () : int;
+
+ ///
+ /// Data sources
+ ///
+
+ /**
+ * @return IPRange[]
+ */
+ public static function getLoopbackRanges () : array {
+ return [
+ "IPv4" => self::from("127.0.0.0/8"),
+ "IPv6" => self::from("::1/128"),
+ ];
+ }
+
+}
diff --git a/src/Network/IPv4Range.php b/src/Network/IPv4Range.php
new file mode 100644
index 0000000..0a8d6f0
--- /dev/null
+++ b/src/Network/IPv4Range.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Keruald\OmniTools\Network;
+
+use Countable;
+use InvalidArgumentException;
+
+class IPv4Range extends IPRange {
+
+ /**
+ * @var string
+ */
+ private $base;
+
+ /**
+ * @var int
+ */
+ private $networkBits;
+
+ ///
+ /// Constructors
+ ///
+
+ public function __construct (string $base, int $networkBits) {
+ $this->setBase($base);
+ $this->setNetworkBits($networkBits);
+ }
+
+ ///
+ /// Getters and setters
+ ///
+
+ /**
+ * @return string
+ */
+ public function getBase () : string {
+ return $this->base;
+ }
+
+ /**
+ * @param string $base
+ */
+ public function setBase (string $base) : void {
+ if (!IP::isIPv4($base)) {
+ throw new InvalidArgumentException;
+ }
+
+ $this->base = $base;
+ }
+
+ /**
+ * @return int
+ */
+ public function getNetworkBits () : int {
+ return $this->networkBits;
+ }
+
+ /**
+ * @param int $networkBits
+ */
+ public function setNetworkBits (int $networkBits) : void {
+ if ($networkBits < 0 || $networkBits > 32) {
+ throw new InvalidArgumentException;
+ }
+
+ $this->networkBits = $networkBits;
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public function getFirst () : string {
+ return $this->base;
+ }
+
+ public function getLast () : string {
+ return long2ip(ip2long($this->base) + 2 ** $this->count() - 1);
+ }
+
+ public function contains (string $ip) : bool {
+ if (!IP::isIP($ip)) {
+ throw new InvalidArgumentException;
+ }
+
+ if (!IP::isIPv4($ip)) {
+ return false;
+ }
+
+ $ipAsLong = ip2long($ip);
+ $baseAsLong = ip2long($this->base);
+
+ return $ipAsLong >= $baseAsLong
+ && $ipAsLong <= $baseAsLong + $this->count() - 1;
+
+ return false;
+ }
+
+ ///
+ /// Countable interface
+ ///
+
+ public function count () : int {
+ return 32 - $this->networkBits;
+ }
+
+}
diff --git a/src/Network/IPv6.php b/src/Network/IPv6.php
new file mode 100644
index 0000000..faa475c
--- /dev/null
+++ b/src/Network/IPv6.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Keruald\OmniTools\Network;
+
+use InvalidArgumentException;
+
+class IPv6 extends IP {
+
+ /**
+ * @var string
+ */
+ private $ip;
+
+ ///
+ /// Constructors
+ ///
+
+ public function __construct (string $ip) {
+ $this->ip = $ip;
+ }
+
+ public static function from (string $ip) : self {
+ $ipv6 = new self($ip);
+
+ if (!$ipv6->isValid()) {
+ throw new InvalidArgumentException;
+ }
+
+ $ipv6->normalize();
+
+ return $ipv6;
+ }
+
+ public static function fromBinaryBits (array $bits) : self {
+ $fullBits = $bits + array_fill(0, 128, 0);
+ $hextets = [];
+
+ for ($i = 0 ; $i < 8 ; $i++) {
+ // Read 16 bits
+ $slice = implode("", array_slice($fullBits, $i * 16, 16));
+ $hextets[] = base_convert($slice, 2, 16);
+ }
+
+ return self::from(implode(":", $hextets));
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public function isValid () : bool {
+ return IP::isIPv6($this->ip);
+ }
+
+ public function increment (int $increment = 1) : self {
+ if ($increment === 0) {
+ return $this;
+ }
+
+ if ($increment < 0) {
+ throw new InvalidArgumentException("This method doesn't support decrementation.");
+ }
+
+ $ipAsNumericBinary = inet_pton($this->ip);
+
+ // See https://gist.github.com/little-apps/88bbd23576008a84e0b6
+ $i = strlen($ipAsNumericBinary) - 1;
+ $remainder = $increment;
+
+ while ($remainder > 0 && $i >= 0) {
+ $sum = ord($ipAsNumericBinary[$i]) + $remainder;
+ $remainder = $sum / 256;
+ $ipAsNumericBinary[$i] = chr($sum % 256);
+
+ --$i;
+ }
+
+ $this->ip = inet_ntop($ipAsNumericBinary);
+ return $this;
+ }
+
+ public function normalize () : self {
+ $this->ip = inet_ntop(inet_pton($this->ip));
+ return $this;
+ }
+
+ public function isNormalized() : bool {
+ return $this->ip === inet_ntop(inet_pton($this->ip));
+ }
+
+ ///
+ /// Magic methods
+ ///
+
+ public function __toString () : string {
+ return $this->ip;
+ }
+}
diff --git a/src/Network/IPv6Range.php b/src/Network/IPv6Range.php
new file mode 100644
index 0000000..ea4a79e
--- /dev/null
+++ b/src/Network/IPv6Range.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace Keruald\OmniTools\Network;
+
+use Countable;
+use InvalidArgumentException;
+
+class IPv6Range extends IPRange {
+
+ /**
+ * @var string
+ */
+ private $base;
+
+ /**
+ * @var int
+ */
+ private $networkBits;
+
+ ///
+ /// Constructors
+ ///
+
+ public function __construct (string $base, int $networkBits) {
+ $this->setBase($base);
+ $this->setNetworkBits($networkBits);
+ }
+
+ ///
+ /// Getters and setters
+ ///
+
+ /**
+ * @return string
+ */
+ public function getBase () : string {
+ return $this->base;
+ }
+
+ /**
+ * @param string $base
+ */
+ public function setBase (string $base) : void {
+ if (!IP::isIPv6($base)) {
+ throw new InvalidArgumentException;
+ }
+
+ $this->base = $base;
+ }
+
+ /**
+ * @return int
+ */
+ public function getNetworkBits () : int {
+ return $this->networkBits;
+ }
+
+ /**
+ * @param int $networkBits
+ */
+ public function setNetworkBits (int $networkBits) : void {
+ if ($networkBits < 0 || $networkBits > 128) {
+ throw new InvalidArgumentException;
+ }
+
+ $this->networkBits = $networkBits;
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public function getFirst () : string {
+ return $this->base;
+ }
+
+ public function getLast () : string {
+ if ($this->count() === 0) {
+ return $this->base;
+ }
+
+ $base = inet_pton($this->getFirst());
+ $mask = inet_pton($this->getInversedMask());
+ return inet_ntop($base | $mask);
+ }
+
+ private function getInversedMask () : string {
+ $bits = array_fill(0, $this->networkBits, 0) + array_fill(0, 128, 1);
+
+ return (string)IPv6::fromBinaryBits($bits);
+ }
+
+ public function contains (string $ip) : bool {
+ if (!IP::isIP($ip)) {
+ throw new InvalidArgumentException;
+ }
+
+ if (IP::isIPv4($ip)) {
+ $ip = "::ffff:" . $ip; // IPv4-mapped IPv6 address
+ }
+
+ $baseAsNumericBinary = inet_pton($this->getFirst());
+ $lastAsNumericBinary = inet_pton($this->getLast());
+ $ipAsNumericBinary = inet_pton($ip);
+
+ return strlen($ipAsNumericBinary) == strlen($baseAsNumericBinary)
+ && $ipAsNumericBinary >= $baseAsNumericBinary
+ && $ipAsNumericBinary <= $lastAsNumericBinary;
+ }
+
+ ///
+ /// Countable interface
+ ///
+
+ public function count () : int {
+ return 128 - $this->networkBits;
+ }
+
+}
diff --git a/tests/Network/IPv4RangeTest.php b/tests/Network/IPv4RangeTest.php
new file mode 100644
index 0000000..ee36916
--- /dev/null
+++ b/tests/Network/IPv4RangeTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Keruald\OmniTools\Tests\Network;
+
+use Keruald\OmniTools\Network\IPRange;
+use PHPUnit\Framework\TestCase;
+
+class IPv4RangeTest extends TestCase {
+
+ /**
+ * @var IPRange
+ */
+ protected $range;
+
+ ///
+ /// Fixtures
+ ///
+
+ protected function setUp () : void {
+ $this->range = IPRange::from("216.66.0.0/18");
+ }
+
+ ///
+ /// Tests
+ ///
+
+ public function testGetBase () : void {
+ $this->assertEquals("216.66.0.0", $this->range->getBase());
+ }
+
+ public function testGetNetworkBits () : void {
+ $this->assertEquals(18, $this->range->getNetworkBits());
+ }
+
+ public function testCount () : void {
+ $this->assertEquals(14, $this->range->count()); // 14 + 18 = 32 bits
+ }
+
+ public function testGetFirst () : void {
+ $this->assertEquals("216.66.0.0", $this->range->getFirst());
+ }
+
+ public function testGetLast () : void {
+ $this->assertEquals("216.66.63.255", $this->range->getLast());
+ }
+
+}
diff --git a/tests/Network/IPv6RangeTest.php b/tests/Network/IPv6RangeTest.php
new file mode 100644
index 0000000..86655c8
--- /dev/null
+++ b/tests/Network/IPv6RangeTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Keruald\OmniTools\Tests\Network;
+
+use Keruald\OmniTools\Network\IPRange;
+use PHPUnit\Framework\TestCase;
+
+class IPv6RangeTest extends TestCase {
+
+ /**
+ * @var IPRange
+ */
+ protected $range;
+
+ ///
+ /// Fixtures
+ ///
+
+ protected function setUp () : void {
+ $this->range = IPRange::from("2001:400::/23");
+ }
+
+ ///
+ /// Tests
+ ///
+
+ public function testGetBase () : void {
+ $this->assertEquals("2001:400::", $this->range->getBase());
+ }
+
+ public function testGetNetworkBits () : void {
+ $this->assertEquals(23, $this->range->getNetworkBits());
+ }
+
+ public function testCount () : void {
+ $this->assertEquals(105, $this->range->count()); // 23 + 105 = 128 bits
+ }
+
+ public function testGetFirst () : void {
+ $this->assertEquals("2001:400::", $this->range->getFirst());
+ }
+
+ public function testGetLast () : void {
+ $this->assertEquals("2001:5ff:ffff:ffff:ffff:ffff:ffff:ffff", $this->range->getLast());
+ }
+
+ public function testContains () : void {
+ $this->assertTrue($this->range->contains("2001:431::af"));
+ }
+
+ public function testContainsWorksWithIPv4MappedIPv6Address () : void {
+ $this->assertTrue(IPRange::from("::ffff:0.0.0.0/96")->contains("1.2.3.4"));
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 25, 15:52 (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2259717
Default Alt Text
(11 KB)
Attached To
Mode
rKOT Keruald OmniTools
Attached
Detach File
Event Timeline
Log In to Comment