Skip to content

Instantly share code, notes, and snippets.

@miken32
Last active November 27, 2025 13:47
Show Gist options
  • Select an option

  • Save miken32/f402317ce71bf7156e4a4a645e6a500b to your computer and use it in GitHub Desktop.

Select an option

Save miken32/f402317ce71bf7156e4a4a645e6a500b to your computer and use it in GitHub Desktop.
Laravel CIDR validation rule
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use const FILTER_FLAG_IPV4;
use const FILTER_FLAG_IPV6;
use const FILTER_VALIDATE_INT;
use const FILTER_VALIDATE_IP;
class Cidr implements ValidationRule
{
/** @var bool whether or not the rule has been called with network constraints */
private bool $has_bits;
/**
* @param int|null $ipv4minbits The minimum number of bits allowed in an IPv4 network
* @param int|null $ipv4maxbits The maximum number of bits allowed in an IPv4 network
* @param int|null $ipv6minbits The minimum number of bits allowed in an IPv6 network
* @param int|null $ipv6maxbits The maximum number of bits allowed in an IPv6 network
*/
public function __construct(
public ?int $ipv4minbits = null,
public ?int $ipv4maxbits = null,
public ?int $ipv6minbits = null,
public ?int $ipv6maxbits = null,
)
{
$this->has_bits = func_num_args() > 0;
}
/**
* @param string $attribute The attribute being validated
* @param mixed $value The current value of the attribute
* @param Closure $fail Closure to be run in case of failure
* @return void
*/
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$mask = null;
$valid_mask = true;
if (str_contains($value, "/")) {
[$value, $mask] = explode("/", $value);
} elseif ($this->has_bits) {
// if we specify a bit constraint, assume the bits are required
$fail($this->message());
}
if (str_contains($value, ":")) {
// ipv6
if ($mask !== null) {
$valid_mask = filter_var(
$mask,
FILTER_VALIDATE_INT,
["options" => ["min_range" => $this->ipv6minbits ?? 0, "max_range" => $this->ipv6maxbits ?? 128]]
);
}
$valid_address = filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
} else {
// ipv4
if ($mask !== null) {
$valid_mask = filter_var(
$mask,
FILTER_VALIDATE_INT,
["options" => ["min_range" => $this->ipv4minbits ?? 0, "max_range" => $this->ipv4maxbits ?? 32]]
);
if ($valid_mask) {
$long = ip2long($value);
$mask = -1 << (32 - $mask);
$valid_mask = long2ip($long & $mask) === $value;
}
}
$valid_address = filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
}
if (!$valid_address || !$valid_mask) {
$fail($this->message());
}
}
public function message(): string
{
return !$this->has_bits
? __("The :attribute must be a valid IP address or CIDR subnet.")
: sprintf(
__("The :attribute must be a valid CIDR subnet with a mask of %d-%d (IPv4) or %d-%d (IPv6) bits."),
$this->ipv4minbits ?? 0,
$this->ipv4maxbits ?? 32,
$this->ipv6minbits ?? 0,
$this->ipv6maxbits ?? 128
);
}
}
@devmaufh
Copy link
Copy Markdown

Thanks! you saved me a lot of time. 🚀

@jonnott
Copy link
Copy Markdown

jonnott commented May 26, 2023

@miken32 I've updated this class for Laravel 10.x (new ValidationRule contract) and PHP 8.1 (str_contains()) .. are you interested in having this to update?

@miken32
Copy link
Copy Markdown
Author

miken32 commented May 26, 2023

@jonnott nah, I've updated months ago in our project, just didn't remember I'd posted this here.

@jonnott
Copy link
Copy Markdown

jonnott commented May 26, 2023

@jonnott nah, I've updated months ago in our project, just didn't remember I'd posted this here.

Sweet, I've grabbed your new version. Thnx!

@lrljoe
Copy link
Copy Markdown

lrljoe commented Jun 12, 2023

Appreciate this, I've used the basis but have a version/rule to check if an IP is in one or more CIDR subnets, not sure if it'd be useful to chuck a link on here or if you'd find that useful.

@jonnott
Copy link
Copy Markdown

jonnott commented Jun 12, 2023

I think this could be cool as a little package like https://github.com/Propaganistas/Laravel-Phone

@miken32 would you be up for collab on this? i.e. if I turned it into a similar package + co-maintain .. ?

@justasSendrauskas
Copy link
Copy Markdown

how is it going to put it into a repo guys?

@jonnott
Copy link
Copy Markdown

jonnott commented Sep 21, 2023

how is it going to put it into a repo guys?

Are you keen for a package then?

@justasSendrauskas
Copy link
Copy Markdown

how is it going to put it into a repo guys?

Are you keen for a package then?

sure

@justasSendrauskas
Copy link
Copy Markdown

wait did we move anywhere with a repo? @miken32 since you are the author that would makes sense for you to do it...

@miken32
Copy link
Copy Markdown
Author

miken32 commented Oct 4, 2023

Yeah I can do that @justasSendrauskas give me a couple of days

@miken32
Copy link
Copy Markdown
Author

miken32 commented Oct 18, 2023

@justasSendrauskas @jonnott @lrljoe I put up a package miken32/network-rules if you want to have a look. Consolidated a bunch of one-off code I was using internally and added a bunch of new validations as well. Still marked as pre-release for now.

@justasSendrauskas
Copy link
Copy Markdown

@miken32 brilliant! thank you very much

@jonnott
Copy link
Copy Markdown

jonnott commented Oct 20, 2023

Very excited for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment