Skip to content

Fix PHP 8.6 mb_regex_encoding deprecation in domain validation (#57) - #58

Merged
mmucklo merged 1 commit into
masterfrom
fix/57-mb-regex-deprecation
Jul 13, 2026
Merged

Fix PHP 8.6 mb_regex_encoding deprecation in domain validation (#57)#58
mmucklo merged 1 commit into
masterfrom
fix/57-mb-regex-deprecation

Conversation

@mmucklo

@mmucklo mmucklo commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Closes #57.

Problem

validateDomainName() used mb_regex_encoding() + mb_split() to split the domain into labels. Both emit E_DEPRECATED under PHP 8.6 (the underlying oniguruma library is no longer maintained):

Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained

Fix

At that point the domain is already pure ASCII — it has been through idn_to_ascii() via normalizeDomainAscii() — so the multibyte machinery buys nothing:

  • mb_split('\.', $domain)explode('.', $domain)
  • mb_substr($part, …, $encoding) hyphen-boundary checks → substr()
  • dropped the now-unused $encoding parameter — the sole caller (Parse.php) never passed it, so it was always the 'UTF-8' default

Behavior is unchanged. Two now-stale psalm-baseline.xml entries (the removed mb_regex_encoding calls) are cleaned up.

Tests

Filled coverage gaps around the touched code and the MB/encoding paths it sits in — each assertion captured from the real parser output, not guessed:

Domain-label branches (testspec.yml)

  • trailing-hyphen label (baddomain-.com) — the substr($part, -1) half of the boundary check had no test
  • invalid-char label (bad_domain.com) — the per-label preg_match branch
  • over-255-octet domain (domain_too_long)

Encoding / MB (ParseTest.php)

  • non-UTF-8 tokenizer (ISO-8859-1, single-byte) and variable-width (Shift-JIS, 2-byte kanji) — proves mb_substr indexes by character, not byte
  • malformed-UTF-8 rejection (InvalidUtf8Encoding)
  • PunycodeConversionFailed and LocalPartCannotBeNormalized — previously referenced only in the severity-enum test, never triggered functionally
  • RFC 5321 §4.5.3.1 length boundaries — accept-at-max / reject-one-over for local part (64), domain label (63), and whole address (254)
  • mismatched caller-encoding domain fails gracefully (no crash)

Also documented in-source that the empty-label branch in validateDomainName is unreachable (the state machine rejects consecutive/edge dots first).

Verification

All green: composer test (91 tests, 3325 assertions), composer stan (PHPStan level 8), composer psalm, composer cs:check.

@codecov

codecov Bot commented Jul 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.06%. Comparing base (7fd7790) to head (ca0a0f8).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##             master      #58      +/-   ##
============================================
+ Coverage     91.99%   93.06%   +1.07%     
+ Complexity      382      380       -2     
============================================
  Files             6        6              
  Lines           987      981       -6     
============================================
+ Hits            908      913       +5     
+ Misses           79       68      -11     
Files with missing lines Coverage Δ
src/Parse.php 90.35% <100.00%> (+1.51%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

validateDomainName() called mb_regex_encoding()/mb_split(), both of which
emit E_DEPRECATED under PHP 8.6 (the underlying oniguruma library is
unmaintained). The domain is already ASCII at that point (post-punycode,
via normalizeDomainAscii()), so label splitting now uses explode('.') and
the hyphen-boundary checks use substr() instead of mb_substr(). The unused
$encoding parameter is dropped — the sole caller never passed it. Behavior
is unchanged. Two now-stale psalm-baseline entries are removed.

CI: extend the test matrix to PHP 8.5 (required) and 8.6 (experimental,
continue-on-error against nightly with --ignore-platform-req=php+), so
removed/deprecated functions surface early without nightly breakage blocking
PRs. Composer cache is now keyed per PHP version.

Test coverage for the touched code and the MB/encoding paths around it:
- testspec: trailing-hyphen label, invalid-char label, over-255 domain
- non-UTF-8 tokenizer (ISO-8859-1) and variable-width (Shift-JIS) parsing
- RFC 5321 length boundaries: accept-at-max, reject-one-over
- PunycodeConversionFailed and LocalPartCannotBeNormalized (previously
  never triggered by any functional test)
- mismatched caller-encoding domain fails gracefully
- malformed-UTF-8 handling gated on a runtime mbstring probe: 8.1/8.2
  preserve invalid bytes (rejected), 8.3+ substitute them during
  tokenization before the guard runs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mmucklo
mmucklo force-pushed the fix/57-mb-regex-deprecation branch from 0e841cd to ca0a0f8 Compare July 13, 2026 00:32
@mmucklo
mmucklo merged commit 8a78f35 into master Jul 13, 2026
13 checks passed
@mmucklo
mmucklo deleted the fix/57-mb-regex-deprecation branch July 13, 2026 00:37
mmucklo added a commit that referenced this pull request Jul 13, 2026
…58)

validateDomainName() called mb_regex_encoding()/mb_split(), both of which
emit E_DEPRECATED under PHP 8.6 (the underlying oniguruma library is
unmaintained). The domain is already ASCII at that point (post-punycode,
via normalizeDomainAscii()), so label splitting now uses explode('.') and
the hyphen-boundary checks use substr() instead of mb_substr(). The unused
$encoding parameter is dropped — the sole caller never passed it. Behavior
is unchanged. Two now-stale psalm-baseline entries are removed.

CI: extend the test matrix to PHP 8.5 (required) and 8.6 (experimental,
continue-on-error against nightly with --ignore-platform-req=php+), so
removed/deprecated functions surface early without nightly breakage blocking
PRs. Composer cache is now keyed per PHP version.

Test coverage for the touched code and the MB/encoding paths around it:
- testspec: trailing-hyphen label, invalid-char label, over-255 domain
- non-UTF-8 tokenizer (ISO-8859-1) and variable-width (Shift-JIS) parsing
- RFC 5321 length boundaries: accept-at-max, reject-one-over
- PunycodeConversionFailed and LocalPartCannotBeNormalized (previously
  never triggered by any functional test)
- mismatched caller-encoding domain fails gracefully
- malformed-UTF-8 handling gated on a runtime mbstring probe: 8.1/8.2
  preserve invalid bytes (rejected), 8.3+ substitute them during
  tokenization before the guard runs
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.

mb_regex_encoding is deprecated since PHP 8.6

1 participant