task: #3583423 Introduce DomainResolver service, make DomainStorage a pure data layer
Summary
Introduces a new DomainResolver service that owns request-coupled logic, leaving DomainStorage as a pure entity-storage handler with no RequestStack dependency. DomainNegotiator becomes a thin orchestrator that delegates resolution to DomainResolver and keeps state on DomainNegotiationContext. Six consumer classes that only read the active domain are switched from injecting DomainNegotiatorInterface to the lighter DomainNegotiationContext. The DOMAIN_MATCHED_* match-type constants move to DomainNegotiationContext (the outcome holder) with deprecated aliases preserved on the interface for BC.
Issue: #3583423.
Why
DomainStorage previously had RequestStack injected just to compute the request hostname / scheme. That coupling made it awkward to test, and conflated "load domain entities" with "resolve the active request". The split:
DomainResolver(new) — ownsgetRequestHostname(),getRequestScheme(),getRequestPath(),normalizeHostname(),loadByHttpHost(),isRegisteredHttpHost(),resolveDomain(),matchByPathPrefix(),matchPathPrefix(). Holds theRequestStack,domain.www_prefix,domain.allow_non_ascii,domain.path_prefixparameters.DomainStorage— keepscreateInstance()legacy plumbing butrequestStack,ignoreWwwPrefix,containerproperties become@deprecated.prepareHostname/createHostname/createMachineName/getDefaultSchemebecome deprecated wrappers that delegate todomain.resolveror to the newDrupal\domain\Utility\MachineName::fromString()helper.DomainNegotiator—setRequestDomain(),setActiveDomain(),negotiateActiveHostname(),negotiateByPathPrefix(),setHttpHost(),getHttpHost(),isRegisteredDomain()all become deprecated wrappers;negotiateActiveDomain()collapses to a singledomainResolver->resolveDomain($context)call.DomainNegotiationContext— gains theDOMAIN_MATCHED_NONE/EXACT/ALIASconstants as the canonical home (the constants conceptually belong on the outcome holder, not on the negotiator service interface).Domain::preCreate()absorbs the auto-fill logic (hostname/name/scheme/is_default) that used to live inDomainStorage::create().- Bug fix in passing:
$_SERVER['https'](lowercase, never set by anything) becomes$_SERVER['HTTPS']inDomainResolver::getRequestScheme(). - Bug fix in passing:
is_defaultdefault flips from(int)($default === FALSE)(always 0 becauseloadDefaultId()returnsNULL, notFALSE) to(int) is_null($default)(correct: 1 when no default exists yet).
Commits
6010947ctask: Introduce DomainResolver service, make DomainStorage a pure data layer- Adds
DomainResolver,DomainResolverInterface,Utility\MachineName. Deprecates the legacyDomainStorage/DomainNegotiatormethods that did request-handling. UpdatesDomain::preCreate()for the auto-fill move.
- Adds
2cef5cb4refactor: DomainResolver::resolveDomain() now mutates DomainNegotiationContext directly- Reshapes the resolver's main entry point from
(string $hostname): ?DomainInterfaceto(DomainNegotiationContext $context): void. The negotiator'snegotiateActiveDomain()becomes a thin delegation.
- Reshapes the resolver's main entry point from
c91944a4refactor: Switch consumers from DomainNegotiatorInterface to DomainNegotiationContext- Six classes that only need to read the active domain (or its id) are switched from the full
DomainNegotiatorInterfaceto the lighterDomainNegotiationContext:DomainCacheContext,DomainAccessManager,DomainAccessNodeHooks,DomainAccessEntityHooks,DomainConfigUiFormHooks,DomainSourceFormHooks. Reduces service-graph dependencies and circular-wiring risk.
- Six classes that only need to read the active domain (or its id) are switched from the full
37559068fix: Trigger deprecation notice on legacy negotiator entry points- Adds the missing
@trigger_error()calls toDomainNegotiator::setRequestDomain()andsetActiveDomain()for parity with the other deprecated methods on the class. Spells out loudly in both the negotiator andDomainNegotiatorInterfacethatsetRequestDomain()'s$hostnameand$resetarguments are no longer honored — the underlyingDomainResolver::resolveDomain()reads the live request hostname unconditionally. Anyone relying on the old behavior must push aRequestontorequest_stack(or callgetActiveDomain(TRUE)) instead.
- Adds the missing
ff2ad861test: Add DomainResolverTest kernel test- 13 kernel test methods covering each public method on
DomainResolverInterface(getRequestHostname,getRequestSchemehttp/https,getRequestPathwith and without an active request,normalizeHostnamewith and without the www_prefix container parameter,loadByHttpHostknown/unknown,isRegisteredHttpHostknown/unknown,resolveDomainexact-match and default-fallback). Closes the test-coverage gap for the resolver service.
- 13 kernel test methods covering each public method on
900080cdrefactor: Move DOMAIN_MATCHED_* constants to DomainNegotiationContext- The match-type constants are conceptually a property of the negotiation outcome, not the negotiator service. Move the canonical home to
DomainNegotiationContext; keep the constants onDomainNegotiatorInterfaceas deprecated aliases referencing the new location. Internal usages migrated;DomainAliasHooksdrops itsDomainNegotiatorInterfaceimport entirely (it now depends only onDomainNegotiationContext). External code that readsDomainNegotiatorInterface::DOMAIN_MATCHED_*continues to work via the alias — class-constant reference is fully transparent BC.
- The match-type constants are conceptually a property of the negotiation outcome, not the negotiator service. Move the canonical home to
Behavior changes
Strict superset for normal flows; deprecation warnings on legacy usage. The two notable changes:
DomainNegotiator::setRequestDomain($hostname, $reset)ignores both arguments. Existing callers passing a specific hostname now silently negotiate against the live request. The deprecation message explicitly calls this out and points at the migration path. Verified via drush spot-check (#3583423/!358 (merged) commit37559068) that the deprecation fires correctly.is_defaultdefault for the first domain is now correctly1instead of0(incidental bug fix).
Test plan
- PHPCS / PHPStan level 2 / cspell clean on changed files.
- All migrated kernel/functional tests pass locally (
DomainHookTest,DomainPrefixLanguageTest,DomainPrefixTest,DomainAliasPrefixTest,DomainAliasHostnameTest,DomainAliasPatternResolverTest,DomainAccessUnpublishedGrantsTest,DomainSourceRouteMatcherTest,DomainSourceRouterProviderTest). - New
DomainResolverTest(commit 5) — 13 tests, 59 assertions, OK. - Drush spot-check of all 7 deprecated methods on
DomainNegotiator— every@trigger_error()fires correctly with the standard Drupal-format message and change-record link. - CI phpunit (D11 + previous-major D10) — pipeline running.
Coordination
Rebased onto post-merge 3.x at 73e2c6a3. The recent merges (#3588155, #3588168, #3588169, #3588175, #3588176) all touched the alias-side of the pipeline; this MR's domain-side resolver/negotiator refactor doesn't conflict with any of them. Clean rebase.
Out of scope (separate issues)
- Removing the legacy deprecated wrappers on
DomainNegotiator/DomainStorageand the deprecated constant aliases onDomainNegotiatorInterface. To be done indomain:4.0.0. - Updating any external modules that still depend on
DomainNegotiatorInterfacefor read-only access. They keep working through the deprecated path; they should migrate toDomainNegotiationContextat their convenience.