Conversation
All v3.2 roadmap items. Fully additive — no breaking changes for v3.1 callers; two notable tolerance expansions (inputs previously rejected as invalid now parse) that existing "strict whitespace" validators may want to know about. Streaming batch parsing: - Parse::parseStream(iterable, string): Generator<ParsedEmailAddress> — lazy parsing that yields one typed address at a time. Each input item may itself contain multiple separator-delimited addresses. Use for large batches where holding every parsed result in memory is undesirable. Severity classification: - ValidationSeverity backed enum (Critical / Warning / Info) with stable string backing values. - ParseErrorCode::severity() classifies every code. 13 codes are Warning (UTF-8 gating, C0/C1 controls, empty-quoted, FQDN, IP global-range, length limits, punycode conversion) — all structural/unparseable failures are Critical. - ParsedEmailAddress::invalidSeverity() returns the derived severity or null when the address is valid. - Rationale: callers can now distinguish "unparseable input" from "well-formed but policy-rejected" and choose to accept Warning-level failures in non-SMTP contexts. RFC 5322 §4.4 obs-route support: - ParseOptions::$allowObsRoute rule property (readonly, default false; enabled by default in rfc5322() and rfc2822() presets). - withAllowObsRoute() fluent builder. - New STATE_OBS_ROUTE absorbs `@host1,@host2:` source-route prefixes inside angle-addr, then resumes normal addr-spec parsing on the ':' terminator. - Captured route is exposed as ParsedEmailAddress::$obsRoute (null when no route was consumed). Also present as the `obs_route` key on the legacy array output. - Incomplete obs-route (`<@host>` with no colon before `>`) is flagged invalid with ParseErrorCode::IncompleteAddress. CFWS tolerance (RFC 5322 §3.2.2): - Folding whitespace is now absorbed via look-ahead in the whitespace handler at four positions that were previously rejected: * Trailing CFWS on local-part dot-atom: "local @Domain" * Leading CFWS on domain dot-atom: "local@ domain" * Leading CFWS inside angle-addr: "< local@domain>" * Trailing CFWS inside angle-addr before '>': "<local@domain >" - Folded whitespace (LF + WSP) is handled as part of the same run. - Comments in these positions were already supported in v3.0. - The look-ahead is positional: only whitespace directly preceding an '@', '>', or the first atext inside an angle-addr is absorbed. Whitespace in other positions (e.g. between atext tokens in a dot-atom) still errors per existing behavior. Tests: 42 tests / 445 assertions (up from 36 / 426 in v3.1). Project coverage: 89.61% lines (up from 88.78%). Docs: CHANGELOG v3.2.0, UPGRADE v3.1 → v3.2 with migration notes for the CFWS and obs-route tolerance changes, ROADMAP items flipped to [x], README updated with parseStream example and allowObsRoute rule property. Tooling: composer stan now runs with --memory-limit=512M to accommodate the larger codebase.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #51 +/- ##
============================================
+ Coverage 88.59% 89.34% +0.74%
- Complexity 323 357 +34
============================================
Files 5 6 +1
Lines 833 910 +77
============================================
+ Hits 738 813 +75
- Misses 95 97 +2
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes all five v3.2 roadmap items. Fully additive for v3.1 callers — no breaking API changes. Two notable tolerance expansions: previously-rejected inputs now parse successfully (CFWS around
@and inside<…>; obs-route insiderfc5322()/rfc2822()presets).What's new
Streaming batch parsing
Parse::parseStream(iterable $input, string $encoding): Generator<ParsedEmailAddress>— yields one typed address at a time. Each input item may itself contain multiple separator-delimited addresses. Use for large batches where holding every result in memory is undesirable.Severity classification
ValidationSeveritybacked enum:Critical/Warning/Infowith stable backing values.ParseErrorCode::severity(): ValidationSeverity— every code classified. 13 codes areWarning(UTF-8 gating, C0/C1 controls, empty-quoted, FQDN, IP global-range, length limits, punycode conversion); all others areCritical.ParsedEmailAddress::invalidSeverity(): ?ValidationSeverity— derived;nullwhen valid.Callers can now distinguish unparseable input from well-formed but policy-rejected and choose to accept
Warning-level failures in non-SMTP contexts:RFC 5322 §4.4 obs-route support
ParseOptions::$allowObsRoute(readonly; defaultfalse; enabled inrfc5322()andrfc2822()).withAllowObsRoute()fluent builder.STATE_OBS_ROUTEstate absorbs source-route prefixes (<@host1,@host2:user@host3>), then resumes normal addr-spec parsing at the:terminator.ParsedEmailAddress::$obsRoute;nullwhen no route was consumed.<@host>with no:before>) →ParseErrorCode::IncompleteAddress.CFWS tolerance (RFC 5322 §3.2.2)
Folding whitespace now absorbed via look-ahead in four positions that were previously rejected:
local @domainlocal@ domain< local@domain>>:<local@domain >Folded whitespace (LF + WSP) is handled as part of the same run. The look-ahead is positional: whitespace is only absorbed when the next significant character is
@,>, or the first atext inside angle-addr.Migration notes for v3.1 users
Only the two tolerance expansions could surprise callers:
@being rejected, they now registerinvalid=false.<@host:addr>is now accepted inrfc5322()/rfc2822(). Opt out via->withAllowObsRoute(false).Everything else is pure addition. See UPGRADE.md.
Test plan
composer cipasses (cs:check, PHPStan level 6 at 512M, 42 tests / 445 assertions)ValidationSeverity,ParsedEmailAddress,ParseResult,ParseErrorCode,LengthLimits; 99.32% onParseOptions; 86.69% onParseParseErrorCodecase, obs-route success/multi-host/display-name/batch/disabled-by-default/incomplete/empty-addr-spec, CFWS in six distinct positions including multi-line foldingalignReasonCodeOne()was extended to strip the newobs_routefield from actual output when expected YAML doesn't specify itDocs updated
CHANGELOG.md— v3.2.0 entry with Added / Changed sectionsUPGRADE.md— v3.1 → v3.2 with migration notes for CFWS and obs-route tolerance changesROADMAP.md— v3.2 items flipped to[x]README.md—parseStreamexample in Basic Usage;allowObsRouterow in rule properties table