Issue #3618623: Add a 4.x branch compatible with Domain 4.x and Drupal 12

Adds the 4.x branch: Domain 4.x, Drupal 11.4 and Drupal 12, and the project's own PHPStan configuration at level 3.

Domain 4.x API

Domain 4.0.0 removed the static DomainAccessManager::getAccessValues() and ::getAllValue(), now on the DomainAccessFields utility class in domain_access, and DomainStorage::createHostname(), now DomainResolver::getRequestHostname(). The first two were called in 16 places across DomainPathHelper, DomainPathFormHelper and DomainPathItem, the third in two FunctionalJavascript tests.

Drupal 12

  • core_version_requirement: ^11.4 || ^12 on both info files, drupal/domain: ^4.0@dev.
  • DomainPathUniqueAliasTest and DomainPathFieldConstraintsTest had no #[RunTestsInSeparateProcesses], which throws an exception in Drupal 12.
  • Every test class declares its group as an attribute. PHPUnit 12 no longer reads doc-comment metadata, so the 15 classes whose only group was @group would have lost it; the 35 now-redundant @group tags are removed.
  • Native return types on 31 overridden methods across 17 classes: void on the constraint validators, plus getPathByAlias(): string, postSave(): bool, formElement(): array, create(): static, updateEntityAlias(): ?array and the rest. These are the forward-compatibility warnings a lane running with --fail-on-deprecation fails on, and they were ours. Each type is the one core or Pathauto named, checked against what the method actually returns.
  • module_set_weight() is deprecated in Drupal 11.5, and the ModuleWeight service replacing it does not exist on the 11.4 floor, so the call is chosen at runtime through DeprecationHelper::backwardsCompatibleCall(). The service is addressed by its id string rather than ModuleWeight::class, because referencing a class core does not ship yet is an unresolved symbol for the current lane's PHPStan.

Legacy hook wrappers

domain_path.module and domain_path_pathauto.module held nothing but #[LegacyHook] wrappers, one per hook, around hook classes that already exist. On an ^11.4 floor those wrappers never run, so both files are deleted, as Domain did on its own 4.x branch. Core still ships .module files and still collects procedural hooks in Drupal 12, so this is a floor cleanup, not a Drupal 12 requirement.

PHPStan level 3

The project shipped no phpstan.neon, so the job ran at the GitLab template default: level 0, reportUnmatchedIgnoredErrors: false. It now has its own at level 3 with bleedingEdge and an entity_mapping.neon, and keeps the template's wider fileExtensions so the install files stay in the analysis.

Level 3 reported 67 findings. All 67 are fixed in the code, with no baseline and no new ignores. The single biggest root cause was two test base classes returning EntityStorageInterface from pathAliasStorage(), which widened away the entity mapping and alone accounted for 30 of them. Restoring unmatched-ignore reporting also exposed 55 dead @phpstan-ignore-next-line comments, all removed.

CI

  • OPT_IN_TEST_NEXT_MAJOR: 1, OPT_IN_TEST_PREVIOUS_MAJOR: 0.
  • _LENIENT_ALLOW_LIST: 'pathauto,token,ctools'. Pathauto has no Drupal 12 release yet, and it requires token and ctools, whose own core requirements stop at Drupal 11 and would block the whole resolution.
  • Pathauto is installed from its development branch on the next-major lane only, because release 1.15.0 cannot be autoloaded on Drupal 12 (see below). The current lane keeps testing against the release.
  • A .deprecation-ignore.txt covering only what dependencies emit. Four of the six patterns are scoped by namespace, so the same warning still fails the lane when it comes from this project. Two cannot be: module_set_weight() (called unguarded by pathauto.install) and image_style_options() (called by token's TokenTokenInfoHooks) carry no caller in their message, so they are pinned to the exact deprecated function and the file says so. Every pattern is asserted against the real message and against a message from this project's own namespaces.
  • The test hostnames move from the phpunit job onto .phpunit-base, so every lane built from it resolves them, and the redundant services: override is dropped (the template's .testing-job-base already provides the database and both chrome services).

Pathauto and Drupal 12

Pathauto 1.15.0, the current release, declares PathautoPatternListBuilder::getDefaultOperations() with the single-argument signature. Drupal 12 added a second, required $cacheability argument to ConfigEntityListBuilder::getDefaultOperations(), so merely autoloading that class is a fatal error and every test installing Pathauto dies in setUp(). On the first run of this MR that was 12 of the 34 classes, and nothing in this project can fix it.

Pathauto has already fixed it on 8.x-1.x, which composer sees as 1.x-dev, and the corrected signature (?CacheableMetadata $cacheability = NULL) is compatible with Drupal 11.4 too. Until Pathauto tags a release carrying the fix, composer (next major) rewrites the constraint so the lane tests the combination that will actually exist, the same way it already uses drupal/core and drupal/domain dev versions. The block in .gitlab-ci.yml is marked for removal once that release exists.

Verified locally

Against Drupal 11.4.4 with Domain 4.x and pathauto installed:

  • PHPStan level 3 with this MR's phpstan.neon: [OK] No errors.
  • phpcs with the GitLab template's phpcs.xml.dist: 0 errors, 0 warnings.
  • Kernel suites, both modules: 68 tests, 733 assertions, all passing. The 8 remaining deprecations all come from domain, domain_access, pathauto and token, and are the ones .deprecation-ignore.txt covers; none come from this project.

CI

Both lanes are green on pipeline 932872, each running 34 classes and 94 test methods with no failures: phpunit on Drupal 11.4 and phpunit (next major) on Drupal 12, the latter played by hand because it is a manual job. phpstan, phpstan (next major), phpcs, cspell, eslint, stylelint and composer-lint all pass too.

Functional and FunctionalJavascript need a served multi-domain docroot and go to CI. The next-major lane is manual and has to be played by hand.

The first next-major run also caught four Functional classes of our own passing t('Save') to submitForm(). Drupal 12 type-hints the button label as string|int|float|bool, so a TranslatableMarkup is a TypeError; those seven call sites now pass the plain label, which is core's own idiom and clears the matching DrupalPractice warnings as well.

Edited by Frank Mably

Merge request reports

Loading