Declare Drupal 12 compatibility and turn on the next-major test lane

Turns the next-major lane on, fixes what it reports, and widens the core constraint. Reconciles the generated changes from [#3595431] rather than applying them blind.

Deprecations removed in Drupal 12

Both families were deprecated in 11.2, so the replacements exist on the module's ^11.3 floor:

  • REQUIREMENT_* constants become RequirementSeverity enum cases.
  • \PDO::FETCH_ASSOC becomes FetchAs::Associative.

Three things the bot patch got wrong or left behind:

  • RequirementsHook::runtime() compared $entry['severity'] ?? 0 against an enum case. An int default can never equal an enum, so that arm had quietly stopped meaning anything; now ?? NULL.
  • Generated use statements were prepended rather than sorted in.
  • CronVerifyTest::setUp() pulled in core/includes/install.inc purely for the constants. The enum autoloads, so the require is gone, along with docblocks naming the old constants.

The real Drupal 12 signature break

ConfigEntityListBuilder::getDefaultOperations() takes a required $cacheability in Drupal 12; 11.3 declares it commented-out and reads it via func_get_args(). The three list builders and hook_entity_operation now carry the parameter, with $cacheability ??= new CacheableMetadata() so the parent call is valid on both.

Test suite under PHPUnit 12

The Drupal 12 lane runs PHP 8.5 / PHPUnit 12, which is stricter than the current lane's PHP 8.3 / PHPUnit 11. Two consequences:

  • Six createMock() calls that never set an expectation are now createStub(). Each mock was checked for expects() first; the other 21 stay mocks.
  • AuditTrailLogger reported a dropped chained write with a bare error_log(). Under process isolation PHPUnit captures that off the child's stream and marks the test risky, and it cannot be suppressed from inside the test: an error_log() write escapes PHPUnit's output buffer, so neither an output expectation nor a nested buffer intercepts it (phpunit#2155, phpunit#1452). Redirecting the error_log ini works up to PHP 8.4 but not on 8.5, where the value is honored yet the write still goes to the SAPI logger.

    So the report now goes through DroppedEventReporterInterface. The default ErrorLogDroppedEventReporter still calls error_log(), so operator-visible behavior and the message text are unchanged; the service is dependency-free, which keeps the logger's graph clear of logger.factory (the [#3591791] cycle constraint). The kernel test registers a capturing reporter and now asserts the reports instead of hiding them: two drops, two reports, each naming the chain.

Pre-existing phpstan findings

44 findings unrelated to Drupal 12, which fail on 1.x today: phpstan-drupal 2.1.1 added the rules after the last green 1.x run on 2026-07-02, and the job is allow_failure, so it was red under a green badge. Fixed here because the next-major job reports the same set: promoted service properties on form and list-builder subclasses drop private/readonly for protected (DependencySerializationTrait cannot restore them), and the three list builders take EntityTypeManagerInterface and call getStorage() in the constructor, which is what the rule asks for and what createInstance() already did.

CI and version constraints

  • OPT_IN_TEST_NEXT_MAJOR: 1.
  • _LENIENT_ALLOW_LIST: 'key,paragraphs' — key pins ^9.1 || ^10 || ^11, paragraphs ^10.3 || ^11; without them the next-major lane cannot resolve. Each entry comes out when that project declares ^12.
  • drush/drush: ^13.0 || ^14 — drush 13 caps symfony/yaml at ^7 while core main needs ~8.1, which blocked the lane before anything installed. 14.x-dev allows ^7 || ^8.
  • core_version_requirement: ^11.3 || ^12 on all six info files, drupal/core: ^11.3 || ^12 in composer.json. README and the docs landing page said "for Drupal 11".

Verification

Every job green, read from the job logs rather than the badges, since phpcs / phpstan / cspell / stylelint are all allow_failure here. phpstan (next major): no errors. phpunit (next major): 448 tests, 2056 assertions, no failures, no risky tests. Locally: full kernel suite 31/31 on MySQL, phpcs clean across the module.

A new alpha still has to follow. Consumers pin ^1.0@alpha, which resolves to released tags, so a widened 1.x branch alone leaves them as blocked as before.

Edited by Frank Mably

Merge request reports

Loading