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 ingetPatterns()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
-
DomainAliasSortTestextended:one.two.example.com:80fixture: 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:80fixture: 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
DomainAliasPatternResolverhardening. 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.