Skip to content

Parse Safety#121

Merged
zanbaldwin merged 3 commits into
6.xfrom
z/parse-safety
Jun 16, 2026
Merged

Parse Safety#121
zanbaldwin merged 3 commits into
6.xfrom
z/parse-safety

Conversation

@zanbaldwin

Copy link
Copy Markdown
Member

No description provided.

@zanbaldwin zanbaldwin self-assigned this Jun 16, 2026
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces strict, single-purpose named constructors (fromProtocol, fromBinary, fromHex and their tryFrom* variants) for IPv4, IPv6, and Multi, backed by a new FactoryInterface, and deprecates the ambiguous factory() entry point that silently accepted raw binary sequences as a security footgun.

  • FactoryInterface is added under Darsyn\IP\Contracts as @experimental and wired into Version4Interface / Version6Interface, surfacing the new constructors through the existing type hierarchy.
  • InvalidBinaryException (extends InvalidIpAddressException) is introduced to distinguish binary-length failures from protocol-notation failures, and tests are comprehensively updated to drive the new constructors.
  • IPv6::fromEmbedded is re-implemented via Multi::tryFromProtocol ?? Multi::fromBinary, preserving the old factory() fallback semantics without calling the deprecated method.

Confidence Score: 3/5

Not safe to merge as-is: adding FactoryInterface to Version4Interface and Version6Interface breaks any third-party implementing class within the 6.x range.

The core logic of the new constructors is sound and well-tested, but extending stable public interfaces with FactoryInterface imposes 7 new required methods on any class implementing Version4Interface or Version6Interface — a PHP fatal error for existing consumers upgrading within 6.x. Additionally, Multi's tryFrom* methods can propagate PackingException uncaught when a custom embedding strategy rejects a 4-byte binary, breaking the never-throws guarantee those methods are supposed to provide.

src/Version/Version4Interface.php and src/Version/Version6Interface.php (BC-breaking interface extension); src/Version/Multi.php (PackingException escape from tryFrom* methods).

Important Files Changed

Filename Overview
src/Version/Version4Interface.php Extends FactoryInterface into a previously-stable interface, introducing a BC break for any third-party implementing class in 6.x.
src/Version/Version6Interface.php Same BC-breaking extension of FactoryInterface as Version4Interface; affects any implementing class outside the library.
src/Version/Multi.php Adds fromProtocol/fromBinary/fromHex strict constructors with strategy support; tryFrom* methods can propagate uncaught PackingException with custom strategies.
src/Version/IPv4.php Adds strict fromProtocol/fromBinary/fromHex constructors and tryFrom* variants; passthrough guard and over-broad catch were flagged in previous review threads.
src/Version/IPv6.php Adds strict constructors mirroring IPv4; fromEmbedded updated to use tryFromProtocol ?? fromBinary, preserving old factory() semantics correctly.
src/Contracts/FactoryInterface.php New @experimental interface defining fromProtocol/fromBinary/fromHex and their tryFrom* variants; clean design, but Multi's strategy parameter is invisible through this interface.
src/Exception/InvalidBinaryException.php New exception subclassing InvalidIpAddressException for binary-specific parse failures; simple and correct.
src/IpInterface.php Adds @deprecated docblock to factory(); no functional change.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class FactoryInterface {
        <<interface, experimental>>
        +fromProtocol(string ip) static
        +tryFromProtocol(string ip) static|null
        +fromBinary(string binary) static
        +tryFromBinary(string binary) static|null
        +fromHex(string hex) static
        +tryFromHex(string hex) static|null
        +isValid(string ip) bool
    }

    class IpInterface {
        <<interface>>
        +factory(string ip) static [deprecated]
    }

    class Version4Interface {
        <<interface>>
    }

    class Version6Interface {
        <<interface>>
    }

    class IPv4 {
        +fromProtocol(string ip)
        +fromBinary(string binary)
        +fromHex(string hex)
    }

    class IPv6 {
        +fromProtocol(string ip)
        +fromBinary(string binary)
        +fromEmbedded(string ip, strategy) [updated]
    }

    class Multi {
        +fromProtocol(string ip, strategy)
        +fromBinary(string binary, strategy)
        +fromHex(string hex, strategy)
        +isValid(string ip, strategy) bool
    }

    class InvalidBinaryException {
        <<new>>
    }

    class InvalidIpAddressException

    IpInterface <|-- Version4Interface
    IpInterface <|-- Version6Interface
    FactoryInterface <|-- Version4Interface
    FactoryInterface <|-- Version6Interface
    Version4Interface <|.. IPv4
    Version6Interface <|.. IPv6
    IPv6 <|-- Multi
    InvalidIpAddressException <|-- InvalidBinaryException
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
classDiagram
    class FactoryInterface {
        <<interface, experimental>>
        +fromProtocol(string ip) static
        +tryFromProtocol(string ip) static|null
        +fromBinary(string binary) static
        +tryFromBinary(string binary) static|null
        +fromHex(string hex) static
        +tryFromHex(string hex) static|null
        +isValid(string ip) bool
    }

    class IpInterface {
        <<interface>>
        +factory(string ip) static [deprecated]
    }

    class Version4Interface {
        <<interface>>
    }

    class Version6Interface {
        <<interface>>
    }

    class IPv4 {
        +fromProtocol(string ip)
        +fromBinary(string binary)
        +fromHex(string hex)
    }

    class IPv6 {
        +fromProtocol(string ip)
        +fromBinary(string binary)
        +fromEmbedded(string ip, strategy) [updated]
    }

    class Multi {
        +fromProtocol(string ip, strategy)
        +fromBinary(string binary, strategy)
        +fromHex(string hex, strategy)
        +isValid(string ip, strategy) bool
    }

    class InvalidBinaryException {
        <<new>>
    }

    class InvalidIpAddressException

    IpInterface <|-- Version4Interface
    IpInterface <|-- Version6Interface
    FactoryInterface <|-- Version4Interface
    FactoryInterface <|-- Version6Interface
    Version4Interface <|.. IPv4
    Version6Interface <|.. IPv6
    IPv6 <|-- Multi
    InvalidIpAddressException <|-- InvalidBinaryException
Loading

Reviews (2): Last reviewed commit: "fix(factory): 🩹 reject invalid-length b..." | Re-trigger Greptile

Comment thread src/Version/Multi.php Outdated
Comment thread src/Version/IPv6.php
Comment thread src/Version/IPv4.php
Comment thread src/Version/IPv4.php
Greptile caught this one! Good one, Greptile!
@zanbaldwin zanbaldwin merged commit 0a3ab5e into 6.x Jun 16, 2026
24 checks passed
@zanbaldwin zanbaldwin deleted the z/parse-safety branch June 16, 2026 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant