Skip to content

1.0.0-alpha1

Pre-release
Pre-release

Choose a tag to compare

@bbrala bbrala released this 01 Jun 09:04
· 106 commits to main since this release
26e189b

1.0.0-alpha1 - 2026-06-01

Overview

The first alpha of the 1.0 line. It adds full Drupal 11 deprecation coverage (11.0
through 11.4), a container-managed settings service that gives you explicit control
over backward-compatibility wrapping, and a documented set of Claude Code skills for
building further rectors. It also drops support for Rector 1.

Validated end-to-end against real code:

  • Applied to Drupal Canvas on the
    canvas-3589155 branch; the full Canvas test suite passed
    (pipeline 825601).
  • Run across the whole of Drupal contrib via
    project_analysis: code
    branch master-next,
    generated patches under
    results-next.
    Spot-checking those patches drove several of the late fixes below: the PDO
    receiver guard, the BC-wrapper mutation bug, the ConstantToClassConstantRector
    generalisation, and the extra MethodToMethodWithCheckRector patterns.

Highlights

  • Drupal 11.0–11.4 coverage. 72 rectors wired across five per-minor-version
    deprecation set lists, covering function → service / function → static / function
    → method-on-first-arg / class-constant → enum / class rename / argument removal
    / property → method / and configuration-key rewrites.
  • Drupal 10.3 additions. 4 new D10 rectors covering REQUEST_TIME,
    ThemeHandlerInterface::rebuildThemeData(), ModuleHandlerInterface::getName(),
    and FileSystemInterface::EXISTS_*FileExists enum.
  • DrupalRectorSettings service. Three knobs for tuning runtime behaviour:
    toggle BC wrapping on/off, set a minimumCoreVersionSupported (contrib modules),
    and override the detected Drupal version (tests). Registered via
    $rectorConfig->singleton(DrupalRectorSettings::class, …) in the shipped
    rector.php.
  • Generic data-driven rectors. A new shared rector
    (FunctionCallRemovalRector) and two extracted from per-version folders
    (DrupalServiceRenameRector, FunctionToFirstArgMethodRector) now live in
    src/Rector/Deprecation/ alongside the established shared rectors, so each
    Drupal version can register them via config-only entries rather than
    per-version subclasses. See the Generic shared rectors subsection below
    for a labelled list of what's new vs. refactored vs. unchanged.
  • BC wrapping covers all expression rewrites. Previously
    AbstractDrupalCoreRector::createBcCallOnExpr() only wrapped CallLike → CallLike
    rewrites; it now wraps any Node\ExprNode\Expr transformation
    (ClassConstFetch, PropertyFetch, etc.), so mixed-type rewrites get
    DeprecationHelper::backwardsCompatibleCall() automatically.
  • Claude Code skills. Four .claude/skills/ entries (/rector-discover,
    /rector-implement, /rector-qa, /rector-live-test) drive the conversion
    pipeline from dbuytaert/drupal-digests
    rules into shipped rector classes with tests. Includes the 700-line canonical
    conversion prompt and three reusable recipes.

Added

Infrastructure

  • rector-hook-convert.php: dedicated configuration that registers
    only HookConvertRector, for running hook conversion as a separate second
    pass. HookConvertRector writes the generated src/Hook/*Hooks.php to disk
    outside Rector's file pipeline, so bundling it with the deprecation sets would
    copy un-fixed hook bodies into the new class. Documented in the README
    ("Converting hooks to OOP hook classes"): run deprecations first, then this
    config.
  • DrupalRectorSettings (src/Services/DrupalRectorSettings.php): container-
    managed settings object with enableBackwardCompatibility() /
    disableBackwardCompatibility() / setMinimumCoreVersionSupported(string) /
    setDrupalVersion(?string) methods. Resolved via
    static::getContainer()->make(DrupalRectorSettings::class) in tests.
  • AbstractDrupalRectorTestCase (tests/src/AbstractDrupalRectorTestCase.php):
    replaces the prior namespaced-class \Drupal hack with a proper
    setUp/tearDown pattern that resets the shared DrupalRectorSettings
    singleton between tests, preventing version-override leaks.
  • createBcCallOnExpr() widened to Node\Expr in
    src/Rector/AbstractDrupalCoreRector.php, so non-CallLike rewrites are
    BC-wrapped too.
  • .gitattributes added with export-ignore entries for .claude/,
    .github/, tests/, fixtures/, docs/, scripts/, phpstan*.neon,
    phpunit.xml, rector.php, etc. Keeps the Composer dist archive small and
    clean. (Verify with git archive HEAD | tar t | grep -c .claude; expect 0.)
  • 25 PHPStan stubs under stubs/ covering core classes the rectors type-guard
    against (ModuleHandlerInterface, EntityInterface, ConfigEntityInterface,
    StatementPrefetchIterator, CachePluginBase, ThemeHandlerInterface,
    SessionManagerInterface, …).

Drupal 11 deprecation rules

Every rule wired across config/drupal-11/drupal-11.{0,1,2,3,4}-deprecations.php
is listed below. Each row links to the Drupal.org change record (or, if no
dedicated change record exists, the parent issue). Configurable generic rectors
(FunctionToServiceRector, FunctionToStaticRector, FunctionCallRemovalRector,
ConstantToClassConstantRector, ClassConstantToClassConstantRector,
MethodToMethodWithCheckRector, FunctionToFirstArgMethodRector,
RenameClassRector) may appear in multiple rows when they handle several
distinct deprecations in the same minor.

Drupal 11.0
Rule What it does Change record
GetNameToNameRector TestCase::getName()name() node/3217904
RemoveStateCacheSettingRector Remove $settings['state_cache']; state caching is permanently enabled node/2575105
ReplaceRequestTimeConstantRector REQUEST_TIME\Drupal::time()->getRequestTime() node/3395986
StripMigrationDependenciesExpandArgRector getMigrationDependencies($expand) → drop the boolean arg node/3442785
MigrateSqlGetMigrationPluginManagerRector Sql::getMigrationPluginManager()$this->migrationPluginManager property node/3282894
Drupal 11.1
Rule What it does Change record
PluginBaseIsConfigurableRector PluginBase::isConfigurable()instanceof ConfigurableInterface check node/2946122
RenameClassRector AliasWhitelist[Interface]AliasPrefixList[Interface]; MatchingRouteNotFoundExceptionResourceNotFoundException node/3467559
MethodToMethodWithCheckRector AliasManager::pathAliasWhitelistRebuild()pathAliasPrefixListRebuild() node/3467559
RemoveModuleHandlerDeprecatedMethodsRector ModuleHandler::writeCache() removed; getHookInfo() returns [] node/3368812
ReplaceLocaleConfigBatchFunctionsRector Rename locale_config_batch_set_config_langcodes() and locale_config_batch_refresh_name() node/3575254
RemoveUpdaterPostInstallMethodsRector Remove Updater::postInstall*() methods (no replacement) node/3461934
BlockContentTestBaseStringToArrayRector BlockContentTestBase::createBlockContentType($string) → array arg node/3473739
MovePointerToMouseOverRector movePointerTo($css)getSession()->getDriver()->mouseOver($xpath) node/3460567
ReplaceAddCachedDiscoveryMethodCallRector addMethodCall('addCachedDiscovery', …) on plugin.cache_clearer removed node/3442229
FunctionToStaticRector drupal_common_theme()ThemeCommonElements::commonElements() node/3488176
Drupal 11.2
Rule What it does Change record
StatementPrefetchIteratorFetchColumnRector StatementPrefetchIterator::fetchColumn()fetchField() node/3490312
MethodToMethodWithCheckRector CacheBackendInterface::invalidateAll()deleteAll() node/3500622
FunctionToServiceRector template_preprocess_{html,page,container,links,time,datetime_form,datetime_wrapper}()ThemePreprocess / DatePreprocess service methods node/3504125
FunctionCallRemovalRector Remove template_preprocess(), update_clear_update_disk_cache(), update_delete_file_if_stale(), _update_manager_{cache_directory,extract_directory,unique_identifier}() node/3501136
RemoveModuleHandlerAddModuleCallsRector Remove ModuleHandler::addModule() / addProfile() (no-ops) node/3550193
RemoveHandlerBaseDefineExtraOptionsRector Remove HandlerBase::defineExtraOptions() overrides (dead code) node/3486781
FunctionToStaticRector drupal_requirements_severity()RequirementSeverity::maxSeverityFromRequirements() node/3497049
FunctionToServiceRector views_field_default_views_data() and _views_field_get_entity_type_storage() → services node/3489415
ConstantToClassConstantRector Global REQUIREMENT_INFO/OK/WARNING/ERRORRequirementSeverity::* node/3575841
RemoveTwigNodeTransTagArgumentRector Drop the 6th $tag argument from TwigNodeTrans constructor calls node/3474692
ReplaceAlphadecimalToIntNullRector Number::alphadecimalToInt(null|'')0 node/3494472
ReplaceFieldgroupToFieldsetRector Render-array '#type' => 'fieldgroup''fieldset' node/3515272
ReplacePdoFetchConstantsRector \PDO::FETCH_*\Drupal\Core\Database\Statement\FetchAs::* (StatementInterface receiver only) node/3488338
ReplaceDateTimeRangeConstantsRector DateTimeRangeConstantsInterface::{BOTH,START_DATE,END_DATE} → enum ->value; datetime_type_field_views_data_helper() → service node/3574901
FunctionToFirstArgMethodRector file_get_content_headers($file)$file->getDownloadHeaders() node/3494172
ReplaceSessionWritesWithRequestSessionRector $_SESSION['key'] = $value\Drupal::request()->getSession()->set(...) node/3518914
ReplaceEditorLoadRector editor_load($format_id)entityTypeManager()->getStorage('editor')->load($format_id) node/3509245
ReplaceEntityOriginalPropertyRector $entity->original magic property → $entity->getOriginal() / setOriginal(); nullsafe-aware node/3571065
RenameStopProceduralHookScanRector #[StopProceduralHookScan]#[ProceduralHookScanStop] node/3495943
RenameClassRector Drupal\Core\Entity\Query\Sql\pgsql\*Drupal\pgsql\Entity\Query\*; jsonapi ResourceResponseValidator move node/3488572
RemoveCacheTagChecksumAssertionsRector Remove CacheTagChecksumCount / CacheTagIsValidCount performance-trait assertions node/3511149
RemoveRootFromCreateConnectionOptionsFromUrlRector Connection::createConnectionOptionsFromUrl(); drop $root parameter node/3511287
ClassConstantToClassConstantRector SystemManager::REQUIREMENT_*RequirementSeverity::* node/3410939
Drupal 11.3
Rule What it does Change record
ReplaceCommentManagerGetCountNewCommentsRector CommentManager::getCountNewComments()HistoryManager::getCountNewComments() node/3551729
LoadAllIncludesRector ModuleHandler::loadAllIncludes() → explicit foreach (getModuleList() …) loadInclude() node/3536432
NodeStorageDeprecatedMethodsRector NodeStorage::revisionIds() / userRevisionIds() → entity query chains; countDefaultLanguageRevisions() removed node/3519187
FunctionToServiceRector node_mass_update()NodeBulkUpdate::process() node/3533083
FunctionToServiceRector template_preprocess_layout()LayoutDiscoveryThemeHooks::preprocessLayout() node/3504125
ReplaceTwigExtensionRector twig_extension()'.html.twig' string literal node/1685492
ReplaceNodeModuleProceduralFunctionsRector node_type_get_names()entity_type.bundle.info service; node_get_type_label($node)$node->getBundleEntity()->label() node/3516778
FunctionCallRemovalRector Remove block_content_add_body_field() calls (replaced by config) node/3535528
FunctionToFirstArgMethodRector comment_uri($comment)$comment->permalink() node/2010202
ReplaceNodeAccessViewAllNodesRector node_access_view_all_nodes() → entity-type-manager access-control chain node/3038909
FunctionToServiceRector responsive_image_*() helpers → responsive_image.builder service node/3548329
ReplaceNodeAddBodyFieldRector node_add_body_field()$this->createBodyField() (BodyFieldCreationTrait) node/3516778
ReplaceUserSessionNamePropertyRector $userSession->name read → getAccountName() node/3513877
FunctionToStaticRector file_system_settings_submit()FileSystemSettingsForm::submit() node/3534091
FileManagedFileSubmitRector 'file_managed_file_submit' string callback → [ManagedFile::class, 'submit'] array callable node/3534091
ConstantToClassConstantRector Global JSONAPI_FILTER_AMONG_*\Drupal\jsonapi\JsonApiFilter::AMONG_* node/3495601
ReplaceNodeSetPreviewModeRector DRUPAL_DISABLED/OPTIONAL/REQUIRED (and integers) in setPreviewMode()NodePreviewMode enum node/3538666
FileSystemBasenameToNativeRector FileSystem::basename() → PHP native basename() node/3530869
ErrorCurrentErrorHandlerRector Error::currentErrorHandler() → PHP get_error_handler() node/3529500
ReplaceThemeGetSettingRector theme_get_setting()ThemeSettingsProvider service; _system_default_theme_features()ThemeSettingsProvider::DEFAULT_THEME_FEATURES node/3573896
RemoveRootFromConvertDbUrlRector Database::convertDbUrlToConnectionInfo($url, $root, …); drop the $root arg node/3511287
RenameClassRector workspaces.association service / WorkspaceAssociation*workspaces.tracker / WorkspaceTracker* node/3551450
Drupal 11.4
Rule What it does Change record
ViewsPluginHandlerManagerRector Views::pluginManager() / handlerManager()plugin.manager.views.* services node/3566982
FunctionToServiceRector node_access_grants()NodeGrantsHelper::nodeAccessGrants() node/3578055
NodeAccessRebuildFunctionsRector node_access_rebuild() / node_access_needs_rebuild()NodeAccessRebuild service node/3534610
FilterFormatFunctionsToServiceRector filter_formats(), filter_get_roles_by_format(), filter_get_formats_by_role(), filter_default_format(), filter_fallback_format()FilterFormatRepositoryInterface service node/3035368
MediaFilterFormatEditFormValidateRector media_filter_format_edit_form_validate()MediaHooks::formatEditFormValidate() node/3566774
DeprecatedFilterFunctionsRector _filter_autop() / _filter_html_escape() / _filter_html_image_secure_process()plugin.manager.filter createInstance chain node/3566774
ReplaceSessionManagerDeleteRector SessionManager::delete($uid)UserSessionRepositoryInterface::deleteAll($uid) node/3570851
ClassConstantToClassConstantRector CommentItemInterface::{FORM_BELOW,FORM_SEPARATE_PAGE,HIDDEN,CLOSED,OPEN} and CommentInterface::ANONYMOUS_*FormLocation / CommentingStatus / AnonymousContact enums node/3550054
FunctionToServiceRector language_configuration_element_submit()LanguageConfiguration::submit() node/3574727
FunctionCallRemovalRector Remove field_ui_form_manage_field_form_submit(), automated_cron_settings_submit(), syslog_facility_list(), syslog_logging_settings_submit(), taxonomy_build_node_index(), taxonomy_delete_node_index(), views_ui contextual-suppress functions node/3566774
RemoveSetUriCallbackRector Remove EntityTypeInterface::setUriCallback() calls; use link_templates instead node/3575062
ReplaceRecipeRunnerInstallModuleRector RecipeRunner::installModule($module)installModules([$module]) node/3579527
ReplaceSystemPerformanceGzipKeyRector system.performance css.gzip / js.gzip config keys → css.compress / js.compress node/3526344
RemoveViewsRowCacheKeysRector Remove CachePluginBase::getRowCacheKeys() / getRowId() overrides node/3564958
RemoveCacheExpireOverrideRector Remove CachePluginBase::cacheExpire() subclass overrides node/3576855
RemoveTrustDataCallRector Strip ->trustData() from Config fluent chains node/3348180
RemoveConfigSaveTrustedDataArgRector Config::save($has_trusted_data); drop the boolean arg node/3348180
RemoveLinkWidgetValidateTitleElementRector Remove LinkWidget::validateTitleElement(); now handled by LinkTitleRequiredConstraint node/3554139
RemoveAutomatedCronSubmitHandlerRector Drop 'automated_cron_settings_submit' from $form['#submit']; #config_target now handles config save node/3566774
ReplaceViewsProceduralFunctionsRector views_view_is_{enabled,disabled}(), views_{enable,disable}_view(), views_get_view*() → entity-storage / Views::* static calls node/3572594
GetOriginalClassToGetDecoratedClassesRector EntityTypeInterface::getOriginalClass()getDecoratedClasses() array access node/3557464
UseEntityTypeHasIntegerIdRector getEntityTypeIdKeyType() === 'integer', entityTypeSupportsComments(), hasIntegerId($entityType)EntityTypeInterface::hasIntegerId() node/3566814
FunctionToServiceRector editor_filter_xss()EditorXssFilterInterface service node/3568144
FunctionToStaticRector _media_library_configure_form_display() / _media_library_configure_view_display() → static helpers node/3566774
FunctionToStaticRector language_configuration_element_submit() static-call form → LanguageConfiguration::submit() node/3574727
FunctionToServiceRector contextual_links_to_id() / contextual_id_to_links()ContextualLinksSerializer node/3568087
FunctionToServiceRector views_add_contextual_links()views.contextual_links service node/3382344
ConstantToClassConstantRector IMAGE_DERIVATIVE_TOKENImageStyleInterface::TOKEN node/3567619
ReplaceEntityReferenceRecursiveLimitRector EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT → literal 20 node/2940605
SystemRegionFunctionsRector system_region_list() / system_default_region()theme.region.manager service node/3015812
CheckMarkupToProcessedTextRector check_markup() → processed-text render array node/3588040
RemoveFilterTipsLongParamRector FilterInterface::tips(); drop the $long parameter node/3567879
SystemSortThemesRector 'system_sort_themes' string callback → Closure (closure callable) node/3566774
LocaleCompareIncToServiceRector locale_translation_flush_projects() / locale_translation_build_projects() / locale_translation_check_projects*() etc. → locale.project and LocaleSource services node/3037031

Drupal 10.3 deprecation rules

Wired in config/drupal-10/drupal-10.3-deprecations.php.

Rule What it does Change record
MethodToMethodWithCheckRector RendererInterface::renderPlain()renderInIsolation() node/3407994
FunctionToStaticRector file_icon_class()IconMimeTypes::getIconClass(); file_icon_map()IconMimeTypes::getGenericMimeType() node/3411269
ReplaceRebuildThemeDataRector ThemeHandlerInterface::rebuildThemeData()extension.list.theme service node/3413196
ReplaceModuleHandlerGetNameRector ModuleHandlerInterface::getName()extension.list.module service node/3310017
ClassConstantToClassConstantRector FileSystemInterface::EXISTS_*\Drupal\Core\File\FileExists enum node/3426517

Generic shared rectors

All eight live under src/Rector/Deprecation/. Each row labels what changed in
this release: (new) introduced in this PR, (refactored) existing class
restructured, (enhanced) new feature added, (moved) relocated from a
per-version folder, (pre-existing) unchanged but used by new D10.3 / D11
configs.

Rector Status in this release Notes
FunctionCallRemovalRector (new) Removes deprecated function-call statements that have no direct replacement. Supports an accumulating configuration so per-version config files each contribute their own function list.
ConstantToClassConstantRector (refactored) Now extends AbstractDrupalCoreRector so it participates in BC wrapping when a VersionedConfiguration is supplied. Three previously-bespoke rectors (ReplaceRequirementSeverityConstantsRector, ReplaceJsonApiFilterConstantsRector, ReplaceLocaleTranslationDefaultServerPatternRector) were collapsed into config entries against this class.
FunctionToServiceRector (enhanced) New useClassSyntax option on FunctionToServiceConfiguration emits \Drupal::service(ClassName::class) instead of a string service ID.
MethodToMethodWithCheckRector (enhanced) Additional patterns matched (statement-level, assignment, fluent-chain) so it covers more configurations found during contrib-wide analysis.
DrupalServiceRenameRector (moved + bugfix) Lifted from src/Drupal8/ into src/Rector/Deprecation/. The previous in-place $node mutation that broke the BC wrapper fallback is fixed: now clones before mutating, with a regression fixture. The legacy Drupal8\Rector\Deprecation\DrupalServiceRenameRector is kept as a thin subclass that re-validates the D8 configuration value object.
FunctionToFirstArgMethodRector (moved) Lifted from src/Drupal9/ into src/Rector/Deprecation/. The legacy Drupal9\Rector\Deprecation\FunctionToFirstArgMethodRector is kept as a thin subclass.
FunctionToStaticRector (pre-existing) Unchanged class; new configurations added in D10.3 and D11.{1,2,3,4} configs.
ClassConstantToClassConstantRector (pre-existing) Unchanged class (introduced in PR #282); new configurations added in D10.3 (FileSystemInterface::EXISTS_*FileExists enum) and D11.4 (CommentItemInterface::* enums).

Tooling and scripts

  • .claude/skills/rector-discover/SKILL.md: lists unimplemented
    drupal-digests rules by implementation phase; regenerates
    docs/rector-index.yml when stale.
  • .claude/skills/rector-implement/SKILL.md: 14-step canonical conversion
    workflow from a digest rule to a finished rector class + test + fixture +
    config entry, with type-guard and version-gating quality gates.
  • .claude/skills/rector-qa/SKILL.md: five-pass quality review (type
    guards, fixture coverage, BC decision correctness, @see URL accuracy,
    registration audit). Supports all mode for branch-wide scans.
  • .claude/skills/rector-live-test/SKILL.md + setup-rector-test.sh:
    finds real contrib modules that use the deprecated API a rector targets and
    runs the rector against them to validate end-to-end. Uses
    search.tresbien.tech as primary source with
    GitLab API fallback.
  • .claude/scripts/generate-rector-index.php: regenerates
    docs/rector-index.yml from the digests source.
  • .claude/scripts/setup-repos.sh: clones repos/drupal-digests and
    repos/drupal-core for local development of new rectors.
  • scripts/check-rector-coverage.php: evaluates real-world coverage by
    matching shipped rectors against real contrib patches.
  • scripts/generate-deprecation-map.php: extracts @trigger_error
    deprecations from Drupal core into a structured YAML map.

Changed

  • ClassConstantToClassConstantRector and MethodToMethodWithCheckRector now
    extend AbstractDrupalCoreRector and auto-wrap their Expr → Expr rewrites
    via DeprecationHelper::backwardsCompatibleCall(). Their configuration value
    objects (ClassConstantToClassConstantConfiguration,
    MethodToMethodWithCheckConfiguration) gain a required introducedVersion
    constructor argument and now implement VersionedConfigurationInterface.
    This closes three D11 → D10 runtime regressions (Comment* enums in 11.4,
    RequirementSeverity in 11.2, AliasManager method rename in 11.1) without
    moving anything to a -breaking.php set.

  • MethodToMethodWithCheckRector no longer attaches a "please confirm the
    receiver type" TODO comment for the maybe type-inference case. The
    comment-on-parent-statement mechanism relied on parent-node tracking that
    Rector 2.x removed; the BC wrap makes both code paths runtime-safe by
    selecting the right call based on \Drupal::VERSION, which addresses the
    underlying concern.

  • Shipped rector.php disables BC wrapping by default. New users get clean
    one-version rewrites out of the box. Contrib modules and projects that need to
    run on multiple Drupal versions should call ->enableBackwardCompatibility()
    and optionally ->setMinimumCoreVersionSupported(...) on the
    DrupalRectorSettings singleton.

    The DrupalRectorSettings class default for backwardCompatibilityEnabled
    remains true (avoids silently changing behaviour for existing users who do
    not call either method on the service); the shipped rector.php invokes
    disableBackwardCompatibility() explicitly to surface the project-level
    recommended default.

  • PHPUnit test setup: tests now extend AbstractDrupalRectorTestCase
    instead of AbstractRectorTestCase directly so DrupalRectorSettings
    mutations don't leak between tests.

  • composer.json dev-dependency bumps:

    • phpunit/phpunit ^10.0^12.5.24
    • phpstan/phpstan ^1.12 || ^2.0^2.1.54
    • phpstan/phpstan-deprecation-rules ^1.2 || ^2.0^2.0.4
    • friendsofphp/php-cs-fixer ^3.58^3.95.1
    • symplify/vendor-patches ^11.0^12.0.6
    • cweagans/composer-patches ^1.7.2^2.0
    • symfony/yaml ^5 || ^6 || ^7^5 || ^6 || ^7.4.8
  • CI matrix simplified to PHP 8.3 + Rector 2 (previously also tested PHP 8.2

    • Rector 1).
  • Drupal stub VERSION bumped from 10.99.x-dev to 11.99.x-dev so D11
    rules fire in tests by default; D11 tests opt out via
    DrupalRectorSettings::setDrupalVersion('1.0.0') for below-version cases.

  • Composer autoload exclude-from-classmap entries added for
    **/fixture/**, **/fixture-*/**, **/Fixture/** so fixtures don't pollute
    consuming projects' class maps.

Removed

  • Rector 1 support. composer.json now requires rector/rector:^2. Drupal
    10 EOL is August 2026; Rector 1 was already EOL upstream.
  • docs/rules_overview.md (1,210 lines, auto-generated). Replaced by
    on-demand generation via .claude/scripts/generate-rector-index.php
    docs/rector-index.yml. The composer docs script that produced the file
    is also removed.
  • Three single-purpose constant rectors collapsed into
    ConstantToClassConstantRector config entries:
    ReplaceRequirementSeverityConstantsRector,
    ReplaceJsonApiFilterConstantsRector,
    ReplaceLocaleTranslationDefaultServerPatternRector.

Fixed

  • DrupalServiceRenameRector BC wrapper fallback. refactorWithConfiguration
    used to mutate the input $node in place; the parent's createBcCallOnExpr
    reused that same already-mutated node as the deprecated branch of the
    DeprecationHelper::backwardsCompatibleCall(...), so both branches ended
    up calling the renamed service and the legacy fallback was silently lost. Now
    clones before mutating; a regression fixture exercises the BC wrapping.
  • ReplacePdoFetchConstantsRector native-PDO receiver guard. The original
    matcher rewrote setFetchMode / fetch* calls on any object whose
    argument was \PDO::FETCH_*, which would break native PDO statements. Now
    guards the receiver with
    isObjectType($node->var, new ObjectType('Drupal\Core\Database\StatementInterface')).
  • NodeStorageDeprecatedMethodsRector missing countDefaultLanguageRevisions:
    the statement-level REMOVE_NODE case was missing entirely; now covered.
  • ReplaceEntityOriginalPropertyRector nullsafe support: added
    NullsafePropertyFetch to the node types and refactor logic so
    $entity?->original is rewritten to $entity?->getOriginal().
  • ReplaceEditorLoadRector argument count: added an arg-count guard so
    editor_load() (no args) and editor_load($a, $b) (two args) are not
    incorrectly transformed.
  • RemoveCacheExpireOverrideRector: handles Time/Tag/None cache plugin
    subclasses, FQCN imports, aliased imports, and ignores side-effects in the
    method body.
  • 8 unregistered rectors wired into deprecation configs: eight new D11
    rector classes shipped with tests but no config/ references in earlier
    drafts; now all wired into the matching drupal-11.X-deprecations.php.
  • AbstractDrupalCoreRector::createBcCallOnExpr visibility: changed from
    private to protected so subclasses can call it directly when they have to
    build manual BC wraps for non-Expr top-level nodes (e.g.,
    ReplacePdoFetchConstantsRector::refactor()'s ArrayItem branch).

Compatibility

Dimension Supported
PHP ^8.2 (CI runs 8.3)
Rector ^2 only; Rector 1 is dropped
Drupal core 8, 9, 10, 11 (10 and 11 are the primary targets; 8 and 9 rules retained for legacy projects)

Upgrade notes

Refresh rector.php

The shipped rector.php was rewritten to register the DrupalRectorSettings
singleton. Refresh your project's copy:

cp vendor/palantirnet/drupal-rector/rector.php .

If you have local customisations, merge them by hand. The notable new block is:

$rectorConfig->singleton(DrupalRectorSettings::class, fn () =>
    (new DrupalRectorSettings())
        ->disableBackwardCompatibility()
);

Contrib modules running against an older minimum Drupal version

If your module must keep running on (e.g.) Drupal 10.5 while the development
environment runs 11.x, enable BC wrapping and tell the settings the minimum core
version you support so the wrappers are emitted correctly:

$rectorConfig->singleton(DrupalRectorSettings::class, fn () =>
    (new DrupalRectorSettings())
        ->enableBackwardCompatibility()
        ->setMinimumCoreVersionSupported('10.5.0')
);

Cleaning up old BC wrappers

Once you raise your module's minimum supported Drupal version, the existing
DeprecationHelper::backwardsCompatibleCall() wrappers in your code become
redundant. Strip them with DeprecationHelperRemoveRector (commented example in
the shipped rector.php):

$rectorConfig->ruleWithConfiguration(DeprecationHelperRemoveRector::class, [
    new DeprecationHelperRemoveConfiguration('10.3.0'),
]);

This rewrites every wrapper whose introducedVersion is below 10.3.0 back to
the new API call directly.

Drupal 8 / 9 legacy rectors

Still included for legacy projects. The classes
Drupal8\Rector\Deprecation\DrupalServiceRenameRector and
Drupal9\Rector\Deprecation\FunctionToFirstArgMethodRector are thin subclasses
re-validating their D8/D9 configuration value objects; behaviour is unchanged.