Skip to content

Split pack() into canonical and non-canonical forms#122

Merged
zanbaldwin merged 1 commit into
6.xfrom
z/strategy-canonical-pack
Jun 16, 2026
Merged

Split pack() into canonical and non-canonical forms#122
zanbaldwin merged 1 commit into
6.xfrom
z/strategy-canonical-pack

Conversation

@zanbaldwin

Copy link
Copy Markdown
Member
  • Deprecate EmbeddingStrategyInterface::pack().
  • Introduce a temporary CanonicalEmbeddingInterface to declare the new packIntoCanonical() and packIntoNonCanonical() in order to preserve backwards compatibility.

@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 CanonicalEmbeddingInterface, a bridge interface that deprecates EmbeddingStrategyInterface::pack() and splits it into packIntoCanonical() (identical behaviour, zeros all non-IPv4 bits) and packIntoNonCanonical() (preserves non-IPv4 bits of an existing IPv6 address). All five concrete strategy classes (Compatible, Mapped, Derived, Teredo, Nat64) implement the new interface, and Multi.php adds a graceful-degradation helper for user-defined strategies that pre-date the bridge.

  • New interface and strategies: CanonicalEmbeddingInterface extends EmbeddingStrategyInterface and declares the two new packing methods; all built-in strategies implement it with correct bit-level arithmetic verified against RFC 6052, RFC 3056, and the Teredo specification.
  • Graceful degradation: Composite and Multi fall back to the deprecated pack() when a user-supplied strategy does not yet implement CanonicalEmbeddingInterface, suppressing the PHPStan warning with @phpstan-ignore.
  • Test coverage: Each strategy gains a full suite of tests for both new methods, including an explicit testPackIntoNonCanonicalReportsInvalidIpv4 test that asserts getSuppliedBinary() returns the bad IPv4 string (not the valid IPv6 string).

Confidence Score: 5/5

Safe to merge — the new methods are additive, all call sites in Multi.php are updated, and the deprecated pack() is preserved for backwards compatibility.

The core bit-level arithmetic in each strategy was traced against the relevant RFCs and confirmed correct for all allowed prefix lengths. Guards are properly split so exceptions carry the right binary string. The graceful-degradation paths in Composite and Multi are intentional and annotated. No call site was missed and the darsyn/ip-doctrine companion repo makes no direct use of pack().

No files require special attention.

Important Files Changed

Filename Overview
src/Strategy/CanonicalEmbeddingInterface.php New bridge interface cleanly extending EmbeddingStrategyInterface; PHPDoc and exception annotations are accurate.
src/Strategy/Nat64.php packIntoNonCanonical() correctly handles all allowed NAT64 prefix lengths (/32–/64, /96); suffix extraction via subString($ipv6, $bytes + 5) is accurate per RFC 6052 § 2.2, and the reserved octet is correctly forced to zero.
src/Strategy/Teredo.php packIntoNonCanonical() correctly preserves bytes 0-11 (prefix, server, flags, obfuscated port) and re-obfuscates the new client IPv4 with XOR 0xFFFFFFFF.
src/Strategy/Derived.php packIntoNonCanonical() correctly replaces only bits 16-47 (IPv4) while preserving the 6to4 prefix and SLA ID/interface ID fields; IPv4 validation is now a separate condition from the IPv6 check.
src/Strategy/Composite.php packIntoCanonical() delegates to the packer only; packIntoNonCanonical() correctly searches all strategies for the recognising one, mirroring extract(); graceful fallback to deprecated pack() for legacy strategies is correctly annotated.
src/Version/Multi.php All four pack() call sites correctly replaced via the new packIntoCanonical() helper with graceful degradation for user-defined strategies.

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class EmbeddingStrategyInterface {
        <<interface>>
        +isEmbedded(binary) bool
        +extract(binary) string
        +pack(binary) string
    }

    class CanonicalEmbeddingInterface {
        <<interface>>
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    EmbeddingStrategyInterface <|-- CanonicalEmbeddingInterface

    class Compatible {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Mapped {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Derived {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Teredo {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Nat64 {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Composite {
        -packer EmbeddingStrategyInterface
        -strategies list
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    CanonicalEmbeddingInterface <|.. Compatible
    CanonicalEmbeddingInterface <|.. Mapped
    CanonicalEmbeddingInterface <|.. Derived
    CanonicalEmbeddingInterface <|.. Teredo
    CanonicalEmbeddingInterface <|.. Nat64
    CanonicalEmbeddingInterface <|.. Composite
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 EmbeddingStrategyInterface {
        <<interface>>
        +isEmbedded(binary) bool
        +extract(binary) string
        +pack(binary) string
    }

    class CanonicalEmbeddingInterface {
        <<interface>>
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    EmbeddingStrategyInterface <|-- CanonicalEmbeddingInterface

    class Compatible {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Mapped {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Derived {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Teredo {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Nat64 {
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    class Composite {
        -packer EmbeddingStrategyInterface
        -strategies list
        +packIntoCanonical(ipv4) string
        +packIntoNonCanonical(ipv6, ipv4) string
    }

    CanonicalEmbeddingInterface <|.. Compatible
    CanonicalEmbeddingInterface <|.. Mapped
    CanonicalEmbeddingInterface <|.. Derived
    CanonicalEmbeddingInterface <|.. Teredo
    CanonicalEmbeddingInterface <|.. Nat64
    CanonicalEmbeddingInterface <|.. Composite
Loading

Reviews (2): Last reviewed commit: "feature(strategy): ✨ split pack() into c..." | Re-trigger Greptile

Comment thread src/Strategy/Derived.php Outdated
Comment thread src/Strategy/Derived.php Outdated
Comment thread src/Version/Multi.php Outdated
Deprecate `EmbeddingStrategyInterface::pack()` and introduce a temporary `CanonicalEmbeddingInterface` to declare the new `packIntoCanonical()` and `packIntoNonCanonical()` in order to preserve backwards compatibility.
@zanbaldwin zanbaldwin force-pushed the z/strategy-canonical-pack branch from c2f6d92 to d73eccb Compare June 16, 2026 22:47
@zanbaldwin zanbaldwin merged commit 5261b98 into 6.x Jun 16, 2026
24 checks passed
@zanbaldwin zanbaldwin deleted the z/strategy-canonical-pack branch June 16, 2026 22:48
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