Skip to content

Feature/new rectors 2#6

Closed
bbrala wants to merge 23 commits into
feature/new-rectors-1from
feature/new-rectors-2
Closed

Feature/new rectors 2#6
bbrala wants to merge 23 commits into
feature/new-rectors-1from
feature/new-rectors-2

Conversation

@bbrala
Copy link
Copy Markdown
Owner

@bbrala bbrala commented May 27, 2026

This pull request adds three new Rector rules to support recent and upcoming deprecations/removals in Drupal core, and wires them into the configuration for Drupal 11 and 12 migrations. The new rules automate removal of obsolete code patterns and provide BC-safe rewrites where possible. The most important changes are:


New Rector Rules:

  • Added RemovePhpUnitCompatibilityTraitRector to remove use Drupal\Tests\PhpUnitCompatibilityTrait; from test classes. This rule is gated to only run for Drupal 12+ where the trait is deleted from core and its presence causes fatal errors. It is off by default and must be explicitly enabled for D12 migrations. (src/Drupal11/Rector/Deprecation/RemovePhpUnitCompatibilityTraitRector.php, config/drupal-11/drupal-11.4-deprecations.php, CHANGELOG.md) [1] [2] [3] [4]

  • Added RemoveAliasManagerCacheMethodCallsRector to remove calls to AliasManager::setCacheKey() and AliasManager::writeCache(), which are deprecated in Drupal 11.3 and removed in 13.0 with no replacement. The rule only removes these methods when called on the correct class/interface to avoid false positives. (src/Drupal11/Rector/Deprecation/RemoveAliasManagerCacheMethodCallsRector.php, config/drupal-11/drupal-11.3-deprecations.php, config/coverage-registry.php, stubs/Drupal/path_alias/AliasManager.php, CHANGELOG.md) [1] [2] [3] [4] [5] [6]

  • Added ReplaceLocaleTranslationPathConfigRector to rewrite config reads for the deprecated locale.settings:translation.path to use Settings::get('locale_translation_path', 'public://translations'). The rule wraps the rewrite in a BC helper so it works on both old and new Drupal versions, but requires site owners to migrate any custom settings to settings.php before running. (src/Drupal11/Rector/Deprecation/ReplaceLocaleTranslationPathConfigRector.php, config/drupal-11/drupal-11.4-deprecations.php, CHANGELOG.md) [1] [2] [3] [4]


Configuration Updates:

  • Registered the new rules in the appropriate deprecations config files for Drupal 11.3 and 11.4, with gating and documentation for each. (config/drupal-11/drupal-11.3-deprecations.php, config/drupal-11/drupal-11.4-deprecations.php) [1] [2] [3] [4]

  • Updated the coverage registry to include the new deprecation messages for RemoveAliasManagerCacheMethodCallsRector. (config/coverage-registry.php)


These changes help automate and de-risk upgrades to Drupal 12 and 13 by proactively removing or rewriting code that will break or become obsolete in those versions.

bbrala added 23 commits May 25, 2026 18:40
Rewrites statement-level calls to the deprecated global hide() and show()
functions to direct $element['#printed'] = TRUE/FALSE assignment.

The functions are deprecated in drupal:11.4.0 and removed in drupal:13.0.0.
Expression-context uses (where the return value is captured) are skipped
because the original returns the element while the rewrite would not.

Live-tested against fpa, saml_sp, vertical_tabs_config, and
field_group_background_image — 4 modules, 10 calls transformed cleanly.

See https://www.drupal.org/node/2258355
See https://www.drupal.org/node/3261271 (change record)
Migrates removed Drupal/PHPUnit test framework methods to their
PHPUnit 11+ replacements:

- $this->expectDeprecation()         (PHPUnit no-arg form) → removed
- $this->expectDeprecation($msg)     (Drupal trait form)   → expectUserDeprecationMessage($msg)
- $this->expectDeprecationMessage($msg)        → expectUserDeprecationMessage($msg)
- $this->expectDeprecationMessageMatches($p)   → expectUserDeprecationMessageMatches($p)

Renames are BC-wrapped via DeprecationHelper::backwardsCompatibleCall()
so tests keep passing on both pre-11.4 (old methods) and 11.4+ (new
methods). The 0-arg PHPUnit form is unconditionally removed because
real-world contrib uses the 1-arg Drupal-trait form.

Live-tested against honeypot, key, entity_usage, and node_revision_delete.

ExpectDeprecationTrait is deprecated in drupal:11.4.0 and removed
in drupal:12.0.0.

See https://www.drupal.org/node/3550268
See https://www.drupal.org/node/3545276 (change record)
…e #3571874

Four BC class aliases in Drupal\block_content\Access were deprecated in
drupal:11.3.0 and removed in drupal:12.0.0. Add config-only RenameClassRector
entries mapping each to its canonical Drupal\Core\Access home:

- AccessGroupAnd
- DependentAccessInterface
- RefinableDependentAccessInterface
- RefinableDependentAccessTrait

See https://www.drupal.org/node/3571874 and
https://www.drupal.org/node/3527501 (change record).
Introduce a rector → PHPStan-deprecation-message map so a future
upgrade_status PR can replace its hardcoded $rector_covered array
with a generated, version-aware registry.

Source of truth lives on the code, not in a sidecar file:

- Custom rector classes: public const PHPSTAN_MESSAGES = [...].
- Config-only registrations: // PHPSTAN_MESSAGES <Rector>: comment
  block above the ruleWithConfiguration() call.

Tooling:

- scripts/normalize-phpstan-message.php applies the three transforms
  upgrade_status's DeprecationAnalyzer::categorizeMessage() applies
  before isRectorCovered() (whitespace collapse, ": in" → ". Deprecated
  in", strip leading \\Drupal). Same transforms run on stored
  messages so the registry is comparison-ready.

- scripts/generate-coverage-registry.php walks src/ + config/, reads
  both shapes, normalizes, writes config/coverage-registry.php
  (rector short name → list of normalized messages).

Worked example added to ReplaceExpectDeprecationRector (captured via
synthetic probe against installed 11.4-dev). 3571874 config-only block
gets a TODO marker rather than a synthesized guess, since the aliases
are already gone from 11.4-dev core and a live capture requires a
11.3.x test env.

rector-live-test SKILL.md gets a new step 5 wiring the capture into
the existing live-test flow: while the contrib module is still
installed and the pre-transform file on disk, run PHPStan, normalize
the message, store on the class or in the config comment block, then
regenerate the registry.
…#3520946

Replaces $block->setConfigurationValue('items_per_page', 'none') with NULL
for Views block plugins. The string 'none' was deprecated in drupal:11.2.0
and removed in drupal:12.0.0; NULL is the canonical value for inheriting
the items-per-page setting from the view.

See https://www.drupal.org/node/3520946
See https://www.drupal.org/node/3522240 (change record)
Replaces deprecated DrupalTestCaseTrait::getDrupalRoot() instance calls with
direct $this->root property access. The method was deprecated in drupal:11.4.0
and removed in drupal:13.0.0. The rule targets subclasses of BrowserTestBase,
KernelTestBase, and UnitTestCase, which all inherit the $root property via
DrupalTestCaseTrait. Static calls and BuildTestBase (which overrides the method
with a non-deprecated implementation) are intentionally left untouched.

See https://www.drupal.org/node/3589047
See https://www.drupal.org/node/3574112 (change record)
…ctor

Captures the three real PHPStan deprecation messages this rector covers — one
per base test class (BrowserTestBase, KernelTestBase, UnitTestCase). The
BrowserTestBase variant was verified against automatic_updates 4.1.x; the
KernelTestBase variant against project_browser 2.1.x. The UnitTestCase variant
mirrors the deterministic PHPStan format.

Regenerates config/coverage-registry.php for upgrade_status consumption.
…538660

Replaces DRUPAL_DISABLED/OPTIONAL/REQUIRED with CommentPreviewMode enum cases
in CommentTestBase::setCommentPreview() calls. Passing an int to this method
was deprecated in drupal:11.3.0 and is removed in drupal:13.0.0; the new
CommentPreviewMode enum only exists on Drupal >= 11.3.0, so the replacement
is BC-wrapped via AbstractDrupalCoreRector + DeprecationHelper.

The type guard targets Drupal\Tests\comment\Functional\CommentTestBase
(the actual owner of setCommentPreview, not the NodeTypeInterface the
upstream digest incorrectly pointed at). A minimal CommentTestBase stub is
added so PHPStan can resolve the receiver type during rector tests.

A TODO PHPSTAN_MESSAGES comment is included because PHPStan emits no
deprecation for this call: setCommentPreview() itself is not @deprecated
(the trigger_error fires at runtime when an int is passed), and phpstan
does not flag file-scope const usage for DRUPAL_DISABLED/OPTIONAL/REQUIRED.

See https://www.drupal.org/node/3538660
See https://www.drupal.org/node/3538678 (change record)
Rewrites deprecated UiHelperTrait::drupalGet() $headers patterns:
- ['Header-Name: value'] → ['Header-Name' => 'value']
- ['Header-Name' => NULL] → ['Header-Name' => '']

Deprecated in drupal:11.1.0, removed in drupal:12.0.0. The associative
format has always been the documented contract; no new Drupal API is
required, so no BC wrapping is needed.

Type guard: Drupal\Tests\BrowserTestBase (UiHelperTrait fires the
deprecation; KernelTestBase uses HttpKernelUiHelperTrait, which does not).

Validated against pager_serializer 8.x-1.x (Functional/Views/StyleSerializerTest.php).
…sue #3448457

Rewrites EntityFormMode::create([..., 'description' => '', ...]) to use
NULL instead of an empty string. Setting the description property of an
EntityFormMode to '' was deprecated in drupal:11.2.0 and must be NULL in
drupal:12.0.0.

Matches both the short class name (use-imported) and the fully-qualified
\Drupal\Core\Entity\Entity\EntityFormMode::create() form via an isName()
static-call guard. Sibling classes (EntityViewMode), non-empty
descriptions, already-migrated NULL values, and other array keys are all
left untouched.

The replacement is plain PHP, so no BC wrapping is needed.

The deprecated pattern is genuinely rare in contrib (16 modules call
EntityFormMode::create() but none with 'description' => ''); validated
via synthetic probe.
…r issue #3529274

Rewrites \Drupal::classResolver(\Drupal\views\ViewsConfigUpdater::class)
to \Drupal::service(\Drupal\views\ViewsConfigUpdater::class). In
drupal:11.3.0 ViewsConfigUpdater was registered as a service;
classResolver() returns a fresh instance on each call, so state set via
setDeprecationsEnabled(FALSE) was lost across hook invocations.

The new call only resolves on Drupal >= 11.3.0 (the service isn't
registered on older versions), so the replacement is BC-wrapped via
DeprecationHelper::backwardsCompatibleCall().

Three layered isName guards ensure only the targeted call shape is
touched: receiver must be \Drupal, method must be classResolver, and the
single argument must be \Drupal\views\ViewsConfigUpdater::class. The
argument guard correctly disambiguates against module subclasses
sharing the short name (validated against entity_hierarchy, which uses
\Drupal\entity_hierarchy\Update\ViewsConfigUpdater and is correctly
skipped).

Live-tested against tripal.
…e #3571593

Rewrites chained \Drupal::config('locale.settings')->get('translation.path')
(and configFactory()/this->config() variants) to \Drupal\Core\Site\Settings::get(
'locale_translation_path', 'public://translations'). The config key was
deprecated in drupal:11.4.0 and is removed in drupal:13.0.0; the customised
translation path must be set as $settings['locale_translation_path'] in
settings.php. BC-wrapped via DeprecationHelper so the rewritten code still
runs on pre-11.4 Drupal — though users must migrate the value to settings.php
before running the rule, otherwise the new branch silently falls back to the
default.

Matches purely structurally: two literal keys ('locale.settings' and
'translation.path') must appear in the expected positions, mirroring
ReplaceSystemPerformanceGzipKeyRector. Standalone $config->get('translation.path'),
unrelated config names, and unrelated keys are left untouched.

PHPStan / upgrade_status cannot detect this deprecation — the deprecated symbol
is the config key, not a PHP API with @deprecated or trigger_error(). A TODO
PHPSTAN_MESSAGES comment in the source documents the gap.
…e #3496369

Removes calls to AliasManager::setCacheKey() and AliasManager::writeCache().
Both methods were deprecated in drupal:11.3.0 and are removed in drupal:13.0.0
with no replacement — they became no-ops when the path alias preload cache
was replaced by a Fiber-based bulk-lookup strategy. The receiver must be
typed as \Drupal\path_alias\AliasManager or AliasManagerInterface; this guard
prevents accidentally removing the unrelated ModuleHandler::writeCache() call.

Removes the entire expression statement, leaving surrounding code intact.
No BC wrapping needed since dropping a no-op call is safe on every Drupal
version that still exposes the methods. Includes PHPSTAN_MESSAGES for both
setCacheKey() and writeCache() captured from real contrib (drupal/redirect)
and a synthetic probe respectively, plus the regenerated coverage-registry.
…3582118

Removes use Drupal\Tests\PhpUnitCompatibilityTrait; from test class
declarations. The trait was a forward-compatibility shim for PHPUnit API
differences across versions; it is deleted from Drupal core in Drupal 12
via #3582118, at which point any test class still composing the trait
fatal-errors at autoload time because the trait class no longer exists.

Gated to Drupal 12 only — and deliberately off by default. The trait
still exists on Drupal 10 (and may still hold shim methods that tests
depend on) and is an empty no-op on Drupal 11. Because the trait
composition is a structural Class_ change, not an Expr → Expr rewrite,
it cannot be BC-wrapped with DeprecationHelper. Running the rule
prematurely on a D10-only codebase risks silently stripping a
composition that the tests still rely on. The rector therefore only
fires when DrupalRectorSettings::setDrupalVersion('12.0.0') or higher
is set; the stub default (11.99.x-dev) keeps it inert for normal
D11-focused runs.

Extends AbstractDrupalCoreRector for the version-gate machinery but
overrides supportBackwardsCompatibility() to return false explicitly,
since structural class composition has no DeprecationHelper-style BC
wrapper to emit.
@bbrala bbrala changed the base branch from feature/digest-rectors to feature/new-rectors-1 May 27, 2026 08:24
@bbrala bbrala force-pushed the feature/new-rectors-1 branch from bf7d2a8 to 15e96d3 Compare May 28, 2026 08:53
@bbrala bbrala closed this May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant