diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bc04b..f9f7998 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - New method `matches($ip, $mask)` to perform wildcard mask matching common in network Access Control Lists and OSPF dynamic routing [#51](https://github.com/rlanvin/php-ip/pull/51) - IPv4Block: Allow to specify the prefix also as an old-style netmask [#53](https://github.com/rlanvin/php-ip/pull/53) +- New method `IPv4::isNetmask()` tests whether the IPv4 address is a valid subnet mask. [#55](https://github.com/rlanvin/php-ip/pull/55) ## [2.0.0] - 2019-09-01 diff --git a/src/IPv4.php b/src/IPv4.php index 6acb33a..99df9aa 100644 --- a/src/IPv4.php +++ b/src/IPv4.php @@ -50,6 +50,11 @@ class IPv4 extends IP */ protected static $link_local_block = '169.254.0.0/16'; + /** + * @var bool + */ + protected $is_netmask; + /** * {@inheritdoc} */ @@ -73,4 +78,26 @@ public function reversePointer(): string return implode('.', $octets).'.in-addr.arpa.'; } + + /** + * Return true if the IP address is a valid sub network mask, for instance: '255.255.252.0'. + * + * @return bool + */ + public function isNetmask(): bool + { + if ($this->is_netmask !== null) { + return $this->is_netmask; + } + + if (gmp_cmp($this->ip, 0) === 0) { + $this->is_netmask = true; + } else { + $y = $this->bit_negate()->plus(1); + $z = $this->bit_negate()->bit_and($y); + $this->is_netmask = gmp_cmp($z->ip, 0) === 0; + } + + return $this->is_netmask; + } } diff --git a/tests/IPv4Test.php b/tests/IPv4Test.php index 1d39b55..616685c 100644 --- a/tests/IPv4Test.php +++ b/tests/IPv4Test.php @@ -219,6 +219,27 @@ public function getMatchesData(): array return $data; } + /** + * Data provider for testIsNetmask(). + * + * @return array + */ + public function netmaskProvider(): array + { + return [ + //IP address is valid netmask + ['0.0.0.0', true], + ['64.0.0.0', false], + ['128.0.0.0', true], + ['127.0.0.0', false], + ['255.255.0.0', true], + ['255.0.255.252', false], + ['255.255.255.0', true], + ['255.255.255.224', true], + ['255.255.255.252', true], + ]; + } + /** * @dataProvider validAddresses */ @@ -517,4 +538,15 @@ public function testDefaultMaskValueMatchesEntireIpAddress() $this->assertTrue($ip->matches('172.16.3.5')); $this->assertFalse($ip->matches('172.16.3.4')); } + + /** + * @dataProvider netmaskProvider + * + * @param $ip + * @param bool $expectation + */ + public function testIsNetMask($ip, bool $expectation) + { + $this->assertEquals($expectation, IPv4::create($ip)->isNetmask()); + } }