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]); } }