Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
- Bugfix: classify full `fc00::/7` block as private use.
- Add (or update) community standards: `SECURITY.md`, `CODE_OF_CONDUCT.md` and
`CONTRIBUTING.md`
- Rename `isPublicUse()` to `isGloballyReachable()`, conforming to the official
wording used in the IANA special-purpose address registries ("Public Use"
does not appear in them). Keep `isPublicUse()` as a deprecated alias.

## `6.0.0`

Expand Down
10 changes: 9 additions & 1 deletion src/IpInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,22 @@ public function isBenchmarking(): bool;
*/
public function isDocumentation(): bool;

/**
* Superseded by `isGloballyReachable()` which conforms to official wording;
* "Public Use" does not appear in the IANA special-purpose registries.
*
* @deprecated
*/
public function isPublicUse(): bool;

/**
* Whether the IP appears to be publicly/globally routable. Please refer to
* the IANA Special-Purpose Address Registry documents.
*
* @see https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
* @see https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv6-special-registry.xhtml
*/
public function isPublicUse(): bool;
public function isGloballyReachable(): bool;

/** Implement string casting for IP objects. */
public function __toString(): string;
Expand Down
5 changes: 5 additions & 0 deletions src/Version/IPv4.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ public function isDocumentation(): bool
}

public function isPublicUse(): bool
{
return $this->isGloballyReachable();
}
Comment on lines 104 to +107
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing @deprecated on concrete implementations

The @deprecated annotation exists on IpInterface::isPublicUse() but is absent from the concrete implementations in IPv4, IPv6, and Multi. IDEs and static analysers (PHPStan, Psalm) resolve deprecation warnings from the declared type at the call site. When user code holds a variable typed as the concrete class rather than IpInterface, the tooling will silently accept isPublicUse() calls without any deprecation notice, defeating the purpose of the annotation.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


public function isGloballyReachable(): bool
{
// Both 192.0.0.9 and 192.0.0.10 are globally routable, despite being in the future reserved block.
if (in_array(Binary::toHex($this->getBinary()), ['c0000009', 'c000000a'], true)) {
Expand Down
5 changes: 5 additions & 0 deletions src/Version/IPv6.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public function isDocumentation(): bool
}

public function isPublicUse(): bool
{
return $this->isGloballyReachable();
}

public function isGloballyReachable(): bool
{
return self::MULTICAST_GLOBAL === $this->getMulticastScope() || $this->isUnicastGlobal();
}
Expand Down
9 changes: 7 additions & 2 deletions src/Version/Multi.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,15 @@ public function isDocumentation(): bool
}

public function isPublicUse(): bool
{
return $this->isGloballyReachable();
}

public function isGloballyReachable(): bool
{
return $this->isEmbedded()
? (new IPv4($this->getShortBinary()))->isPublicUse()
: parent::isPublicUse();
? (new IPv4($this->getShortBinary()))->isGloballyReachable()
: parent::isGloballyReachable();
}

public function isUniqueLocal(): bool
Expand Down
30 changes: 15 additions & 15 deletions tests/DataProvider/IPv4.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,9 @@ public static function getDocumentationIpAddresses()
}

/** @return list<array{string, bool}> */
public static function getPublicUseIpAddresses()
public static function getGloballyReachableIpAddresses()
{
return self::getCategoryOfIpAddresses(self::PUBLIC_USE_V4);
return self::getCategoryOfIpAddresses(self::GLOBALLY_REACHABLE_V4);
}

/** @return list<array{string, bool}> */
Expand Down Expand Up @@ -226,16 +226,16 @@ public static function getCategorizedIpAddresses()
'172.31.254.253' => self::PRIVATE_USE,
'169.254.253.242' => self::LINK_LOCAL,
'192.0.2.183' => self::DOCUMENTATION,
'192.1.2.183' => self::PUBLIC_USE,
'192.1.2.183' => self::GLOBALLY_REACHABLE_V4,
'192.168.254.253' => self::PRIVATE_USE,
'198.51.100.0' => self::DOCUMENTATION,
'203.0.113.0' => self::DOCUMENTATION,
'203.2.113.0' => self::PUBLIC_USE,
'203.2.113.0' => self::GLOBALLY_REACHABLE_V4,
'255.255.255.255' => self::BROADCAST,
'198.18.0.0' => self::BENCHMARKING,
'198.18.54.2' => self::BENCHMARKING,
'224.0.0.0' => self::PUBLIC_USE | self::MULTICAST_IPV4,
'239.255.255.255' => self::PUBLIC_USE | self::MULTICAST_IPV4,
'224.0.0.0' => self::GLOBALLY_REACHABLE_V4 | self::MULTICAST_IPV4,
'239.255.255.255' => self::GLOBALLY_REACHABLE_V4 | self::MULTICAST_IPV4,
'0.0.0.0' => self::UNSPECIFIED,
'10.0.0.0' => self::PRIVATE_USE,
'10.255.255.255' => self::PRIVATE_USE,
Expand All @@ -249,16 +249,16 @@ public static function getCategorizedIpAddresses()
'169.254.255.255' => self::LINK_LOCAL,
'100.64.91.200' => self::SHARED,
'251.0.12.101' => self::FUTURE_RESERVED,
'192.18.0.0' => self::PUBLIC_USE,
'192.18.0.0' => self::GLOBALLY_REACHABLE_V4,
'198.19.255.255' => self::BENCHMARKING,
'129.129.154.203' => self::PUBLIC_USE,
'239.248.153.114' => self::PUBLIC_USE | self::MULTICAST_IPV4,
'85.101.159.135' => self::PUBLIC_USE,
'72.64.156.77' => self::PUBLIC_USE,
'162.199.210.167' => self::PUBLIC_USE,
'2.12.191.95' => self::PUBLIC_USE,
'83.125.176.74' => self::PUBLIC_USE,
'224.0.65.129' => self::PUBLIC_USE | self::MULTICAST_IPV4,
'129.129.154.203' => self::GLOBALLY_REACHABLE_V4,
'239.248.153.114' => self::GLOBALLY_REACHABLE_V4 | self::MULTICAST_IPV4,
'85.101.159.135' => self::GLOBALLY_REACHABLE_V4,
'72.64.156.77' => self::GLOBALLY_REACHABLE_V4,
'162.199.210.167' => self::GLOBALLY_REACHABLE_V4,
'2.12.191.95' => self::GLOBALLY_REACHABLE_V4,
'83.125.176.74' => self::GLOBALLY_REACHABLE_V4,
'224.0.65.129' => self::GLOBALLY_REACHABLE_V4 | self::MULTICAST_IPV4,
];
}

Expand Down
56 changes: 28 additions & 28 deletions tests/DataProvider/IPv6.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,18 +212,18 @@ public static function getDocumentationIpAddresses()
}

/** @return list<array{string, bool}> */
public static function getPublicUseIpAddresses()
public static function getGloballyReachableIpAddresses()
{
return self::getCategoryOfIpAddresses(self::PUBLIC_USE_V6);
return self::getCategoryOfIpAddresses(self::GLOBALLY_REACHABLE_V6);
}

/** @return list<array{string, bool}> */
public static function getPublicUseIpAddressesExcludingMapped()
public static function getGloballyReachableIpAddressesExcludingMapped()
{
// Exclude IPv4-embedded addresses embedded using the Mapped strategy,
// they may fail the test because the IPv4 equivalent is not public (eg,
// "::ffff:7f00:1").
return self::getCategoryOfIpAddresses(self::PUBLIC_USE_V6, self::MAPPED);
return self::getCategoryOfIpAddresses(self::GLOBALLY_REACHABLE_V6, self::MAPPED);
}

/** @return list<array{string, bool}> */
Expand Down Expand Up @@ -268,8 +268,8 @@ public static function getCategorizedIpAddresses()
'::' => self::UNSPECIFIED | self::UNICAST_OTHER | self::COMPATIBLE,
'::0' => self::UNSPECIFIED | self::UNICAST_OTHER | self::COMPATIBLE,
'::1' => self::LOOPBACK | self::UNICAST_OTHER | self::COMPATIBLE,
'::0.0.0.2' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE,
'1::' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'::0.0.0.2' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE,
'1::' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'fc00::' => self::PRIVATE_USE | self::UNIQUE_LOCAL | self::UNICAST_OTHER,
'fdff:ffff::' => self::PRIVATE_USE | self::UNIQUE_LOCAL | self::UNICAST_OTHER,
'fe80:ffff::' => self::LINK_LOCAL,
Expand All @@ -279,38 +279,38 @@ public static function getCategorizedIpAddresses()
'febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff' => self::LINK_LOCAL,
'fe80::ffff:ffff:ffff:ffff' => self::LINK_LOCAL,
'fe80:0:0:1::' => self::LINK_LOCAL,
'fec0::' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'fec0::' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'ff01::' => self::MULTICAST_INTERFACE_LOCAL,
'ff02::' => self::MULTICAST_LINK_LOCAL,
'ff03::' => self::MULTICAST_REALM_LOCAL,
'ff04::' => self::MULTICAST_ADMIN_LOCAL,
'ff05::' => self::MULTICAST_SITE_LOCAL,
'ff08::' => self::MULTICAST_ORGANIZATION_LOCAL,
'ff0e::' => self::PUBLIC_USE_V6 | self::MULTICAST_GLOBAL,
'ff0e::' => self::GLOBALLY_REACHABLE_V6 | self::MULTICAST_GLOBAL,
'2001:db8:85a3::8a2e:370:7334' => self::DOCUMENTATION | self::UNICAST_OTHER,
'2001:2::ac32:23ff:21' => self::PUBLIC_USE_V6 | self::BENCHMARKING | self::UNICAST_GLOBAL,
'102:304:506:708:90a:b0c:d0e:f10' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'2001:2::ac32:23ff:21' => self::GLOBALLY_REACHABLE_V6 | self::BENCHMARKING | self::UNICAST_GLOBAL,
'102:304:506:708:90a:b0c:d0e:f10' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'fd00::' => self::PRIVATE_USE | self::UNIQUE_LOCAL | self::UNICAST_OTHER,
'fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' => self::PRIVATE_USE | self::UNIQUE_LOCAL | self::UNICAST_OTHER,
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' => self::MULTICAST_OTHER,
'::ffff:1:0' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::MAPPED,
'::ffff:7f00:1' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::MAPPED | self::LOOPBACK_MAPPED,
'::ffff:1234:5678' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::MAPPED,
'0000:0000:0000:0000:0000:ffff:7f00:a001' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::MAPPED | self::LOOPBACK_MAPPED,
'2002::' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::DERIVED,
'2002:7f00:1::' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::DERIVED | self::LOOPBACK_DERIVED,
'2002:1234:4321:0:00:000:0000::' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::DERIVED,
'::7f00:1' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE | self::LOOPBACK_COMPATIBLE,
'::12.34.56.78' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE,
'0::000:0000:b12:cab' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE,
'1cc9:7d7f:2a9f:cabd:9186:2be5:bef1:6a54' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'b638:cc70:716:c4d4:f69c:4ee3:6c65:a0b2' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'140c:12f1:6e6f:c0bb:980e:3816:3e52:1193' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'7a30:bf4:4c6c:8dc1:e340:774d:6487:3822' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'6af8:1ceb:eaae:104a:829c:e76e:5802:13f8' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'3e48:c9fd:c569:f5dd:ee36:8075:691b:8234' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'cab2:4f27:790f:cf03:5241:9eff:aba5:bb5c' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'e896:8866:872b:bd4f:6d60:7aa8:ebe5:36f1' => self::PUBLIC_USE_V6 | self::UNICAST_GLOBAL,
'::ffff:1:0' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::MAPPED,
'::ffff:7f00:1' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::MAPPED | self::LOOPBACK_MAPPED,
'::ffff:1234:5678' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::MAPPED,
'0000:0000:0000:0000:0000:ffff:7f00:a001' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::MAPPED | self::LOOPBACK_MAPPED,
'2002::' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::DERIVED,
'2002:7f00:1::' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::DERIVED | self::LOOPBACK_DERIVED,
'2002:1234:4321:0:00:000:0000::' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::DERIVED,
'::7f00:1' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE | self::LOOPBACK_COMPATIBLE,
'::12.34.56.78' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE,
'0::000:0000:b12:cab' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL | self::COMPATIBLE,
'1cc9:7d7f:2a9f:cabd:9186:2be5:bef1:6a54' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'b638:cc70:716:c4d4:f69c:4ee3:6c65:a0b2' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'140c:12f1:6e6f:c0bb:980e:3816:3e52:1193' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'7a30:bf4:4c6c:8dc1:e340:774d:6487:3822' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'6af8:1ceb:eaae:104a:829c:e76e:5802:13f8' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'3e48:c9fd:c569:f5dd:ee36:8075:691b:8234' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'cab2:4f27:790f:cf03:5241:9eff:aba5:bb5c' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
'e896:8866:872b:bd4f:6d60:7aa8:ebe5:36f1' => self::GLOBALLY_REACHABLE_V6 | self::UNICAST_GLOBAL,
];
}

Expand Down
10 changes: 5 additions & 5 deletions tests/DataProvider/IpDataProviderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ interface IpDataProviderInterface
public const MULTICAST_IPV4 = 1 << 6;

// IPv4
public const PUBLIC_USE_V4 = 1 << 7;
public const GLOBALLY_REACHABLE_V4 = 1 << 7;
public const BROADCAST = 1 << 8;
public const SHARED = 1 << 9;
public const FUTURE_RESERVED = 1 << 10;

// IPv6
public const PUBLIC_USE_V6 = 1 << 11;
public const GLOBALLY_REACHABLE_V6 = 1 << 11;
public const MULTICAST_INTERFACE_LOCAL = 1 << 12;
public const MULTICAST_LINK_LOCAL = 1 << 13;
public const MULTICAST_REALM_LOCAL = 1 << 14;
Expand All @@ -42,9 +42,9 @@ interface IpDataProviderInterface
public const LOOPBACK_DERIVED = 1 << 28;

// Combinations
public const PUBLIC_USE = 0
| self::PUBLIC_USE_V4
| self::PUBLIC_USE_V6;
public const GLOBALLY_REACHABLE = 0
| self::GLOBALLY_REACHABLE_V4
| self::GLOBALLY_REACHABLE_V6;
public const LOOPBACK_EMBEDDED = 0
| self::LOOPBACK_MAPPED
| self::LOOPBACK_COMPATIBLE
Expand Down
4 changes: 2 additions & 2 deletions tests/DataProvider/Multi.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,9 @@ public static function getDocumentationIpAddresses()
}

/** @return list<array{string, bool}> */
public static function getPublicUseIpAddresses()
public static function getGloballyReachableIpAddresses()
{
return array_merge(IPv4::getPublicUseIpAddresses(), IPv6::getPublicUseIpAddressesExcludingMapped());
return array_merge(IPv4::getGloballyReachableIpAddresses(), IPv6::getGloballyReachableIpAddressesExcludingMapped());
}

/** @return list<array{string, bool, bool}> */
Expand Down
8 changes: 4 additions & 4 deletions tests/Version/IPv4Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,14 +415,14 @@ public function testIsDocumentation(string $value, bool $isDocumentation): void

/**
* @test
* @dataProvider \Darsyn\IP\Tests\DataProvider\IPv4::getPublicUseIpAddresses()
* @dataProvider \Darsyn\IP\Tests\DataProvider\IPv4::getGloballyReachableIpAddresses()
*/
#[PHPUnit\Test]
#[PHPUnit\DataProviderExternal(IPv4DataProvider::class, 'getPublicUseIpAddresses')]
public function testIsPublicUse(string $value, bool $isPublicUse): void
#[PHPUnit\DataProviderExternal(IPv4DataProvider::class, 'getGloballyReachableIpAddresses')]
public function testIsGloballyReachable(string $value, bool $isGloballyReachable): void
{
$ip = IP::factory($value);
$this->assertSame($isPublicUse, $ip->isPublicUse());
$this->assertSame($isGloballyReachable, $ip->isGloballyReachable());
}

/**
Comment on lines 415 to 428
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No test coverage for the deprecated isPublicUse() alias

All three test classes (IPv4Test, IPv6Test, MultiTest) had their testIsPublicUse methods renamed to testIsGloballyReachable, which now only exercises the new method. The deprecated isPublicUse() delegate is completely untested — a future refactor that accidentally breaks the delegation (e.g., returning a hardcoded value) would go undetected. A minimal parameterised test that calls isPublicUse() and asserts it matches isGloballyReachable() would guard the alias.

Expand Down
8 changes: 4 additions & 4 deletions tests/Version/IPv6Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -457,14 +457,14 @@ public function testIsDocumentation(string $value, bool $isDocumentation): void

/**
* @test
* @dataProvider \Darsyn\IP\Tests\DataProvider\IPv6::getPublicUseIpAddresses()
* @dataProvider \Darsyn\IP\Tests\DataProvider\IPv6::getGloballyReachableIpAddresses()
*/
#[PHPUnit\Test]
#[PHPUnit\DataProviderExternal(IPv6DataProvider::class, 'getPublicUseIpAddresses')]
public function testIsPublicUse(string $value, bool $isPublicUse): void
#[PHPUnit\DataProviderExternal(IPv6DataProvider::class, 'getGloballyReachableIpAddresses')]
public function testIsGloballyReachable(string $value, bool $isGloballyReachable): void
{
$ip = IP::factory($value);
$this->assertSame($isPublicUse, $ip->isPublicUse());
$this->assertSame($isGloballyReachable, $ip->isGloballyReachable());
}

/**
Expand Down
8 changes: 4 additions & 4 deletions tests/Version/MultiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,14 @@ public function testIsDocumentation(string $value, bool $isDocumentation): void

/**
* @test
* @dataProvider \Darsyn\IP\Tests\DataProvider\Multi::getPublicUseIpAddresses()
* @dataProvider \Darsyn\IP\Tests\DataProvider\Multi::getGloballyReachableIpAddresses()
*/
#[PHPUnit\Test]
#[PHPUnit\DataProviderExternal(MultiDataProvider::class, 'getPublicUseIpAddresses')]
public function testIsPublicUse(string $value, bool $isPublicUse): void
#[PHPUnit\DataProviderExternal(MultiDataProvider::class, 'getGloballyReachableIpAddresses')]
public function testIsGloballyReachable(string $value, bool $isGloballyReachable): void
{
$ip = IP::factory($value, new Strategy\Mapped());
$this->assertSame($isPublicUse, $ip->isPublicUse());
$this->assertSame($isGloballyReachable, $ip->isGloballyReachable());
}

/**
Expand Down