• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

azjezz / psl / 22519606807

28 Feb 2026 11:11AM UTC coverage: 97.532% (-1.2%) from 98.733%
22519606807

push

github

web-flow
feat(network): rewrite networking stack with TLS, UDP, SOCKS5, CIDR, and IO utilities (#585)

860 of 937 new or added lines in 31 files covered. (91.78%)

15 existing lines in 6 files now uncovered.

7470 of 7659 relevant lines covered (97.53%)

42.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.32
/src/Psl/CIDR/Block.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Psl\CIDR;
6

7
use function chr;
8
use function count;
9
use function explode;
10
use function inet_pton;
11
use function str_pad;
12
use function str_repeat;
13
use function strlen;
14

15
/**
16
 * Represents a CIDR (Classless Inter-Domain Routing) block.
17
 *
18
 * Supports both IPv4 and IPv6 addresses. IPv4 addresses are internally
19
 * converted to IPv4-mapped IPv6 for unified comparison.
20
 *
21
 * Usage:
22
 *   $block = new Block('192.168.1.0/24');
23
 *   $block->contains('192.168.1.100'); // true
24
 *   $block->contains('192.168.2.1');   // false
25
 *
26
 * @psalm-immutable
27
 *
28
 * @mago-expect analysis:invalid-operand - bitwise on strings is okay.
29
 */
30
final readonly class Block
31
{
32
    private string $networkBytes;
33
    private string $maskBytes;
34

35
    /**
36
     * @param non-empty-string $cidr CIDR notation, e.g. "192.168.1.0/24" or "2001:db8::/32".
37
     *
38
     * @throws Exception\InvalidArgumentException If the CIDR notation is invalid.
39
     */
40
    public function __construct(string $cidr)
41
    {
42
        $parts = explode('/', $cidr, 2);
12✔
43
        if (count($parts) !== 2) {
12✔
44
            throw new Exception\InvalidArgumentException(
1✔
45
                "Invalid CIDR notation: '{$cidr}'. Expected format: 'address/prefix'.",
1✔
46
            );
1✔
47
        }
48

49
        [$address, $prefixStr] = $parts;
11✔
50

51
        $networkBytes = inet_pton($address);
11✔
52
        if ($networkBytes === false) {
11✔
53
            throw new Exception\InvalidArgumentException("Invalid IP address in CIDR notation: '{$address}'.");
1✔
54
        }
55

56
        // Normalize IPv4 to IPv4-mapped IPv6
57
        $prefix = (int) $prefixStr;
10✔
58
        if (strlen($networkBytes) === 4) {
10✔
59
            $networkBytes = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . $networkBytes;
7✔
60
            $prefix += 96;
7✔
61
        }
62

63
        if ($prefix < 0 || $prefix > 128) {
10✔
NEW
64
            throw new Exception\InvalidArgumentException(
×
NEW
65
                "Invalid prefix length: {$prefixStr}. Must be 0-32 for IPv4 or 0-128 for IPv6.",
×
NEW
66
            );
×
67
        }
68

69
        // Build the bitmask
70
        $fullBytes = (int) ($prefix / 8);
10✔
71
        $remainingBits = $prefix % 8;
10✔
72

73
        $mask = str_repeat("\xff", $fullBytes);
10✔
74
        if ($remainingBits > 0) {
10✔
75
            $mask .= chr((0xFF << (8 - $remainingBits)) & 0xFF);
1✔
76
        }
77

78
        $mask = str_pad($mask, 16, "\x00");
10✔
79

80
        $this->networkBytes = $networkBytes;
10✔
81
        $this->maskBytes = $mask;
10✔
82
    }
83

84
    /**
85
     * Check whether the given IP address falls within this CIDR block.
86
     *
87
     * @param non-empty-string $ip IPv4 or IPv6 address to check.
88
     */
89
    public function contains(string $ip): bool
90
    {
91
        $ipBytes = inet_pton($ip);
10✔
92
        if ($ipBytes === false) {
10✔
93
            return false;
1✔
94
        }
95

96
        // Normalize IPv4 to IPv4-mapped IPv6
97
        if (strlen($ipBytes) === 4) {
9✔
98
            $ipBytes = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . $ipBytes;
7✔
99
        }
100

101
        // Compare: (ip & mask) === (network & mask)
102
        return ($ipBytes & $this->maskBytes) === ($this->networkBytes & $this->maskBytes);
9✔
103
    }
104
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc