index/src/Net/IPAddressRange.php

98 lines
2.4 KiB
PHP

<?php
// IPAddressRange.php
// Created: 2021-04-26
// Updated: 2023-01-01
namespace Index\Net;
use InvalidArgumentException;
use JsonSerializable;
use Stringable;
use Index\IEquatable;
final class IPAddressRange implements JsonSerializable, Stringable, IEquatable {
private IPAddress $base;
private int $mask;
public function __construct(IPAddress $base, int $mask) {
$this->base = $base;
$this->mask = $mask;
}
public function getBaseAddress(): IPAddress {
return $this->base;
}
public function getMask(): int {
return $this->mask;
}
public function getCIDR(): string {
return ((string)$this->base) . '/' . $this->getMask();
}
public function __toString(): string {
return $this->getCIDR();
}
public function equals(mixed $other): bool {
return $other instanceof IPAddressRange
&& $this->mask === $other->mask
&& $this->base->equals($other->base);
}
public function match(IPAddress $address): bool {
$width = $this->base->getWidth();
if($address->getWidth() !== $width)
return false;
if($this->mask < 1)
return true;
$base = $this->base->getRaw();
$match = $address->getRaw();
$remaining = $this->mask;
for($i = 0; $i < $width && $remaining > 0; ++$i) {
$b = ord($base[$i]);
$m = ord($match[$i]);
$remainingMod = $remaining % 8;
if($remainingMod) {
$mask = 0xFF << (8 - $remainingMod);
$b &= $mask;
$m &= $mask;
}
if($b !== $m)
return false;
$remaining -= 8;
}
return true;
}
public function jsonSerialize(): mixed {
return $this->getCIDR();
}
public function __serialize(): array {
return [
'b' => $this->base->getRaw(),
'm' => $this->mask,
];
}
public function __unserialize(array $serialized): void {
$this->base = new IPAddress($serialized['b']);
$this->mask = $serialized['m'];
}
public static function parse(string $cidr): IPAddressRange {
$parts = explode('/', $cidr, 2);
if(empty($parts[0]) || empty($parts[1]))
throw new InvalidArgumentException('$cidr is not a valid CIDR range.');
return new static(IPAddress::parse($parts[0]), (int)$parts[1]);
}
}