Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ipv4 Subnet Calculator Laravel Package

markrogoyski/ipv4-subnet-calculator

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer. There is no service provider, no config file, and nothing to publish — it is a plain, dependency-free library:

composer require markrogoyski/ipv4-subnet-calculator

The first thing to open is src/IPv4SubnetCalculator.php. It is a single class with a small, obvious surface area, and the README's method table is effectively the whole API. Skim tests/ once for the edge cases (/31, /32, 0.0.0.0) before you trust it in production.

The namespace and class share a name, which trips people up on the first use:

use IPv4SubnetCalculator\IPv4SubnetCalculator;

$subnet = new IPv4SubnetCalculator('10.20.30.41', 24);

$subnet->getIPAddress();         // 10.20.30.41
$subnet->getSubnetMask();        // 255.255.255.0
$subnet->getWildcardMask();      // 0.0.0.255
$subnet->getNetworkAddress();    // 10.20.30.0
$subnet->getBroadcastAddress();  // 10.20.30.255
$subnet->getFirstHost();         // 10.20.30.1
$subnet->getLastHost();          // 10.20.30.254
$subnet->getNumberIPAddresses(); // 256
$subnet->getNumberHostAddresses(); // 254

First real use case in a Laravel app: an admin screen or inventory importer where someone types 10.20.30.41/24 and you need to normalise it to a network address, broadcast address, and usable host range before persisting. That is a ten-line controller method with this package.

Implementation Patterns

Wrap the constructor in one place. CIDR strings arrive as "10.0.0.0/24" but the constructor wants an IP plus a bit count. Parse once, in a small helper, so nobody re-implements the explode:

namespace App\Support;

use IPv4SubnetCalculator\IPv4SubnetCalculator;

final class Subnet
{
    public static function fromCidr(string $cidr): IPv4SubnetCalculator
    {
        [$ip, $bits] = array_pad(explode('/', trim($cidr), 2), 2, '32');

        return new IPv4SubnetCalculator($ip, (int) $bits);
    }
}

Guard at the boundary, not everywhere. In a FormRequest or a custom validation rule, validate the shape yourself and let the library throw only on genuinely broken input:

public function passes($attribute, $value): bool
{
    [$ip, $bits] = array_pad(explode('/', $value, 2), 2, null);

    return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false
        && $bits !== null
        && ctype_digit($bits)
        && (int) $bits <= 32;
}

Persist ranges as integers, not strings. String IPs cannot be range-queried efficiently. Store network_long and broadcast_long columns (unsignedInteger, populated with ip2long()) alongside the CIDR, then:

Device::whereBetween('ip_long', [
    ip2long($subnet->getNetworkAddress()),
    ip2long($subnet->getBroadcastAddress()),
])->get();

Use getAllSubnetDetails() for API responses. It returns the whole computed set as an array, which maps straight onto a JSON resource and saves you listing ten getters:

return response()->json($subnet->getAllSubnetDetails());

Enumerate child subnets in an Artisan command. This is where the package earns its keep day-to-day — planning VLANs, generating seed data, or sanity-checking an allocation:

$parent = new IPv4SubnetCalculator('10.0.0.0', 22);
$start  = ip2long($parent->getNetworkAddress());
$step   = 2 ** (32 - 24);

for ($offset = 0; $offset < $parent->getNumberIPAddresses(); $offset += $step) {
    $child = new IPv4SubnetCalculator(long2ip($start + $offset), 24);
    $this->line($child->getNetworkAddress() . '/24');
}

Pair it with IpUtils for containment checks. The package computes ranges but does not answer "is this IP inside that subnet". Laravel already ships Symfony's helper for that:

use Symfony\Component\HttpFoundation\IpUtils;

IpUtils::checkIp('10.20.30.77', '10.20.30.0/24'); // true

Compute the ranges with this package, test membership with IpUtils, and you avoid writing your own bitwise comparisons.

Cache derived values. Subnet maths is cheap but not free in a seeder loop over thousands of rows. Cache::remember("subnet:{$cidr}", now()->addDay(), fn () => $subnet->getAllSubnetDetails()) is usually enough.

Gotchas and Tips

  • Do not register it as a singleton. The class is stateful — it holds the IP and mask you gave it and every getter reads from those properties. A bound singleton will silently return the first subnet's data for every subsequent request in the same worker. Construct a fresh instance per lookup, or use the setters deliberately inside a single unit of work.
  • getNumberIPAddresses() ≠ getNumberHostAddresses(). The first is 2^(32-n) including network and broadcast; the second subtracts two. Mixing them up is the classic off-by-two in IPAM code.
  • Check /31 and /32 behaviour explicitly. The classic host formula yields 0 or negative for point-to-point links and single addresses. Verify what getNumberHostAddresses() and getFirstHost() return for these before you use them to generate firewall rules or DHCP scopes.
  • No IPv6, at all. The name says IPv4 and it means it. If your config might contain a v6 CIDR, reject it at validation rather than letting it blow up deeper in.
  • No contains() / overlaps() / sort(). Comparing two ranges means ip2long() on the network and broadcast addresses yourself. Overlap is $aStart <= $bEnd && $bStart <= $aEnd.
  • Sort IPs numerically, never as strings. sort(['10.0.0.10', '10.0.0.9']) puts .10 first. Use ip2long(), sort, then long2ip().
  • Setters mutate in place. Reusing one instance across a loop is fine and fast, but the old values are gone — don't hold a reference to an instance you're mutating elsewhere.
  • Exceptions over isValid(). Invalid IPs/masks surface as exceptions when set. Wrap the construction site in try/catch at the boundary (FormRequest, console command) instead of scattering isValid() guards through your domain code.
  • getClass() / getClassDetails() are historical. Class A/B/C has no bearing on modern CIDR allocation — use them for display only, never for logic.
  • Wildcard mask is the inverted mask. 0.0.0.255 is correct for ACLs and wrong for a netmask field. Pick the getter that matches the field you're filling.
  • Pin the version. The constructor and some getter names have shifted across major versions. Once you're on it, constrain in composer.json so a minor release doesn't quietly change your array keys from getAllSubnetDetails().
  • Alias the import if it reads badly. use IPv4SubnetCalculator\IPv4SubnetCalculator as SubnetCalculator; keeps call sites legible, and it is trivially easy to unit test — pure input, pure output, no I/O, no container, no mocking.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings