fix: #3588168 DomainAliasStorage::buildPatterns() now enumerates the complete wildcard pattern set

Summary

DomainAliasStorage::buildPatterns() enumerates wildcard match candidates for loadByHostname(). The hand-rolled loop misses some masks:

  • 4-segment hostnames: 13 of 14 useful patterns generated. Mask 9 (*.two.example.* — wildcards on both ends, literals in the middle) is unreachable.
  • 5-segment hostnames: 19 of 30 useful patterns generated. 11 masks unreachable.

Replace the loop with a deterministic bitmask enumeration that produces exactly 2^N − 2 patterns for an N-segment hostname (every wildcard mask except all-literal — the exact hostname, handled separately by getPatterns() — and all-wildcard, which would match anything and is not a useful candidate).

Issue: #3588168.

Practical impact in 3.x

Bounded. The validator's single-host-wildcard cap (still in place at 3.x — see #3588169 / !372 (merged)) means stored multi-host-wildcard aliases cannot exist via the normal save path. The previously-unreachable patterns this MR enumerates can only have a stored alias if config was imported via direct YAML edit or migrated from D6/D7. So the user-visible "aliases now match" effect is small for typical 3.x sites.

The full benefit lands once a future MR raises the host-wildcard cap (out of scope here, depends on DomainAliasPatternResolver::replaceWildcards() hardening). This MR is the prerequisite refactor: no point in raising the cap if the candidate enumeration is incomplete.

The change is still a strict improvement in 3.x:

  • Cleaner code (5-branch hand-roll → 1 bitmask loop), generalises to any segment count.
  • array_unique() call in getPatterns() is now provably unnecessary and dropped (the bitmask emits no duplicates).
  • Test fixtures lock in the new coverage so the next host-cap-raise MR doesn't have to re-derive what buildPatterns() should emit.

Test plan

  • DomainAliasSortTest extended:
    • one.two.example.com:80 fixture: all 14 wildcard masks (mask 9 added; the duplicate entry removed; mask 11 added). Mask 13/14 ordering reflects the new mask-numerical traversal.
    • New a.b.c.d.e:80 fixture: 91 patterns covering every mask for a 5-segment hostname.
  • Test passes locally (Drupal 11.x + MySQL, 8 assertions).
  • PHPCS / PHPStan / cspell clean.
  • CI phpunit run.

Out of scope

  • Raising the host-wildcard limit. Tracked separately; depends on DomainAliasPatternResolver hardening.
  • DomainAliasPatternResolver::replaceWildcards() substitution hardening (prerequisite for raising the cap).

Coordination

No conflict with #3588155 / !370 (merged) (sort + priority — already merged) or #3588169 / !372 (merged) (validator host/port split — already merged). This MR touches DomainAliasStorage::buildPatterns() (private, only caller is getPatterns()) and DomainAliasSortTest — different files / methods than either prior MR.

Edited by Frank Mably

Merge request reports

Loading