1.0.0-alpha1
Pre-release1.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-3589155branch; the full Canvas test suite passed
(pipeline 825601). - Run across the whole of Drupal contrib via
project_analysis: code
branchmaster-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, theConstantToClassConstantRector
generalisation, and the extraMethodToMethodWithCheckRectorpatterns.
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(),
andFileSystemInterface::EXISTS_*→FileExistsenum. DrupalRectorSettingsservice. Three knobs for tuning runtime behaviour:
toggle BC wrapping on/off, set aminimumCoreVersionSupported(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 anyNode\Expr→Node\Exprtransformation
(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
onlyHookConvertRector, for running hook conversion as a separate second
pass.HookConvertRectorwrites the generatedsrc/Hook/*Hooks.phpto 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 withenableBackwardCompatibility()/
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\Drupalhack with a proper
setUp/tearDownpattern that resets the sharedDrupalRectorSettings
singleton between tests, preventing version-override leaks.createBcCallOnExpr()widened toNode\Exprin
src/Rector/AbstractDrupalCoreRector.php, so non-CallLike rewrites are
BC-wrapped too..gitattributesadded withexport-ignoreentries for.claude/,
.github/,tests/,fixtures/,docs/,scripts/,phpstan*.neon,
phpunit.xml,rector.php, etc. Keeps the Composer dist archive small and
clean. (Verify withgit 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]; MatchingRouteNotFoundException → ResourceNotFoundException |
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/ERROR → RequirementSeverity::* |
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_TOKEN → ImageStyleInterface::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.ymlwhen 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,@seeURL accuracy,
registration audit). Supportsallmode 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.ymlfrom the digests source..claude/scripts/setup-repos.sh: clonesrepos/drupal-digestsand
repos/drupal-corefor 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
-
ClassConstantToClassConstantRectorandMethodToMethodWithCheckRectornow
extendAbstractDrupalCoreRectorand auto-wrap theirExpr → Exprrewrites
viaDeprecationHelper::backwardsCompatibleCall(). Their configuration value
objects (ClassConstantToClassConstantConfiguration,
MethodToMethodWithCheckConfiguration) gain a requiredintroducedVersion
constructor argument and now implementVersionedConfigurationInterface.
This closes three D11 → D10 runtime regressions (Comment* enums in 11.4,
RequirementSeverityin 11.2,AliasManagermethod rename in 11.1) without
moving anything to a-breaking.phpset. -
MethodToMethodWithCheckRectorno longer attaches a "please confirm the
receiver type" TODO comment for themaybetype-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.phpdisables 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
DrupalRectorSettingssingleton.The
DrupalRectorSettingsclass default forbackwardCompatibilityEnabled
remainstrue(avoids silently changing behaviour for existing users who do
not call either method on the service); the shippedrector.phpinvokes
disableBackwardCompatibility()explicitly to surface the project-level
recommended default. -
PHPUnit test setup: tests now extend
AbstractDrupalRectorTestCase
instead ofAbstractRectorTestCasedirectly soDrupalRectorSettings
mutations don't leak between tests. -
composer.jsondev-dependency bumps:phpunit/phpunit^10.0→^12.5.24phpstan/phpstan^1.12 || ^2.0→^2.1.54phpstan/phpstan-deprecation-rules^1.2 || ^2.0→^2.0.4friendsofphp/php-cs-fixer^3.58→^3.95.1symplify/vendor-patches^11.0→^12.0.6cweagans/composer-patches^1.7.2→^2.0symfony/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
VERSIONbumped from10.99.x-devto11.99.x-devso 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-classmapentries added for
**/fixture/**,**/fixture-*/**,**/Fixture/**so fixtures don't pollute
consuming projects' class maps.
Removed
- Rector 1 support.
composer.jsonnow requiresrector/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 composerdocsscript that produced the file
is also removed.- Three single-purpose constant rectors collapsed into
ConstantToClassConstantRectorconfig entries:
ReplaceRequirementSeverityConstantsRector,
ReplaceJsonApiFilterConstantsRector,
ReplaceLocaleTranslationDefaultServerPatternRector.
Fixed
DrupalServiceRenameRectorBC wrapper fallback.refactorWithConfiguration
used to mutate the input$nodein place; the parent'screateBcCallOnExpr
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.ReplacePdoFetchConstantsRectornative-PDO receiver guard. The original
matcher rewrotesetFetchMode/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')).NodeStorageDeprecatedMethodsRectormissingcountDefaultLanguageRevisions:
the statement-level REMOVE_NODE case was missing entirely; now covered.ReplaceEntityOriginalPropertyRectornullsafe support: added
NullsafePropertyFetchto the node types and refactor logic so
$entity?->originalis rewritten to$entity?->getOriginal().ReplaceEditorLoadRectorargument count: added an arg-count guard so
editor_load()(no args) andeditor_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 noconfig/references in earlier
drafts; now all wired into the matchingdrupal-11.X-deprecations.php. AbstractDrupalCoreRector::createBcCallOnExprvisibility: 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()'sArrayItembranch).
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.