diff --git a/.claude/scripts/generate-rector-index.php b/.claude/scripts/generate-rector-index.php new file mode 100644 index 000000000..759c51ab3 --- /dev/null +++ b/.claude/scripts/generate-rector-index.php @@ -0,0 +1,545 @@ +#!/usr/bin/env php + '1a', + 'FunctionToFirstArgMethodRector' => '1a', + 'DrupalServiceRenameRector' => '1a', + 'FunctionToStaticRector' => '1b', + 'ClassConstantToClassConstantRector' => '1c', + 'ConstantToClassConstantRector' => '1c', + 'MethodToMethodWithCheckRector' => '2', + 'DeprecationHelperRemoveRector' => '2', + 'FunctionCallRemovalRector' => '3', + 'RenameClassRector' => 'unknown', +]; + +// Files with these prefixes add new capability (constructor args, type hints, DI wiring) +// rather than removing deprecated APIs. Keep them out of scope even when they mention @deprecated. +const FORWARD_COMPAT_PREFIXES = ['add-', 'fix-', 'guard-', 'pass-', 'update-']; + +main($argv); + +function main(array $argv): void +{ + $repoRoot = dirname(dirname(__DIR__)); + $digestsPath = $repoRoot . '/repos/drupal-digests'; + + foreach (array_slice($argv, 1) as $arg) { + if ($arg === '--help') { + echo file_get_contents(__FILE__); + exit(0); + } + if (str_starts_with($arg, '--digests-path=')) { + $digestsPath = expandPath(substr($arg, 15)); + } + } + $rulesDir = $digestsPath . '/rector/rules'; + + if (!is_dir($rulesDir)) { + fprintf(STDERR, "Error: digests rules directory not found: %s\n", $rulesDir); + fprintf(STDERR, "Pass --digests-path=PATH to specify the drupal-digests repo location.\n"); + exit(1); + } + + // Step 1: Build base entries from in-scope digest files. + $entries = scanDigestFiles($rulesDir); + + // Step 2: Mark implemented custom classes — scan src/Drupal*/Rector/Deprecation dirs, newest first. + $srcDirs = array_filter( + glob($repoRoot . '/src/Drupal*/Rector/Deprecation', GLOB_ONLYDIR), + fn($d) => preg_match('#/Drupal(\d+)/#', $d, $m) && (int) $m[1] >= 10 + ); + rsort($srcDirs); + $unmatched = []; + foreach ($srcDirs as $srcDir) { + $unmatched += scanImplementedClasses($srcDir, $repoRoot, $entries); + } + + // Build a reverse lookup: shortClassName => {class, files} for all known implemented classes. + // Used in Step 3 so config files can link pending issues to existing custom rectors. + $implementedClasses = buildClassMap($entries, $unmatched); + + // Step 3: Mark config-only / implemented entries found in any config/* subdir. + scanConfigFiles($repoRoot . '/config', $entries, $implementedClasses); + + // Step 4: Add unmatched custom classes (no digest file found). + foreach ($unmatched as $className => $data) { + $entries['class_' . $className] = $data; + } + + // Step 5: Write YAML. + $outputFile = $repoRoot . '/docs/rector-index.yml'; + writeYaml($entries, $outputFile); + + $counts = countByStatus($entries); + printf( + "Wrote %s\n implemented: %d config-only: %d pending: %d\n", + $outputFile, + $counts['implemented'], + $counts['config-only'], + $counts['pending'] + ); +} + +function expandPath(string $path): string +{ + if (str_starts_with($path, '~/')) { + $home = $_SERVER['HOME'] ?? getenv('HOME') ?: ''; + return $home . '/' . substr($path, 2); + } + return $path; +} + +/** + * Scans in-scope digest files, returns array keyed by issue number. + * + * @return array> + */ +function scanDigestFiles(string $rulesDir): array +{ + $entries = []; + + foreach (glob($rulesDir . '/*.php') as $file) { + $filename = basename($file); + + $issueNumber = extractIssueNumber($filename); + if ($issueNumber === null) { + continue; + } + + $content = file_get_contents($file); + + if (!isDeprecationDigest($filename, $content)) { + continue; + } + + $phase = classifyPhaseFromDigest($content, $filename); + + $entries[$issueNumber] = [ + 'issue' => $issueNumber, + 'digest_file' => $filename, + 'phase' => $phase, + 'status' => 'pending', + 'class' => null, + 'files' => [], + ]; + } + + ksort($entries); + return $entries; +} + +function extractIssueNumber(string $filename): ?string +{ + // Last hyphen-separated numeric group before .php + if (preg_match('/-(\d+)\.php$/', $filename, $matches)) { + return $matches[1]; + } + return null; +} + +/** + * Returns true when a digest file documents a Drupal deprecation or removal. + * + * Two-pass check: + * 1. Canonical prefixes (replace-deprecated-*, remove-deprecated-*, replace-removed-*, + * strip-removed-*) are always in scope — this includes Twig/library deprecations + * that don't use the "@deprecated drupal:" marker. + * 2. Non-canonical prefixes are included when the file content contains + * "@deprecated drupal:" or "deprecated in drupal:" — the standard Drupal core + * notation. This catches files like "rename-deprecated-*", "replace-filesysteminterface-*", + * "remove-overrides-of-deprecated-*", etc. + * + * Exception: files with forward-compatibility prefixes (add new constructor args, + * fix type signatures, etc.) are excluded even when they mention a deprecated signature. + */ +function isDeprecationDigest(string $filename, string $content): bool +{ + // Always in scope: canonical deprecation/removal/rename prefixes. + // rename- is included unconditionally because renaming a class or hook is inherently + // a breaking API change — there is no "forward-compat" meaning for a rename. + if (preg_match('/^(replace-deprecated|remove-deprecated|replace-removed|strip-removed|rename-)/', $filename)) { + return true; + } + + // Never in scope: forward-compatibility files (add args, fix signatures, etc.). + foreach (FORWARD_COMPAT_PREFIXES as $prefix) { + if (str_starts_with($filename, $prefix)) { + return false; + } + } + + // Also in scope: non-canonical prefix but explicitly Drupal-deprecated APIs. + return str_contains($content, '@deprecated drupal:') + || str_contains($content, 'deprecated in drupal:'); +} + +/** + * Classifies phase from digest file content by inspecting getNodeTypes(). + */ +function classifyPhaseFromDigest(string $content, string $filename): string +{ + if (!preg_match('/getNodeTypes\s*\(\)[^{]*\{[^}]*return\s*\[([^\]]+)\]/s', $content, $matches)) { + // No getNodeTypes found — fall back on filename prefix. + return classifyPhaseFromFilename($filename); + } + + $nodeTypes = $matches[1]; + + $hasFuncCall = str_contains($nodeTypes, 'FuncCall'); + $hasMethodCall = str_contains($nodeTypes, 'MethodCall') || str_contains($nodeTypes, 'NullsafeMethodCall'); + $hasStaticCall = str_contains($nodeTypes, 'StaticCall'); + $hasClassConst = str_contains($nodeTypes, 'ClassConstFetch') || str_contains($nodeTypes, 'ConstFetch'); + $hasExpression = str_contains($nodeTypes, 'Expression'); + $hasRemoveNode = str_contains($content, 'REMOVE_NODE'); + + // Phase 3: removes the node entirely. + if ($hasRemoveNode || ($hasExpression && !$hasFuncCall && !$hasMethodCall && !$hasStaticCall)) { + return '3'; + } + + // Pure FuncCall — Phase 1a or 1b. + if ($hasFuncCall && !$hasMethodCall && !$hasStaticCall && !$hasClassConst) { + return classifyFuncCallSubPhase($content); + } + + // ClassConstFetch / ConstFetch — Phase 1c. + if ($hasClassConst && !$hasFuncCall && !$hasMethodCall && !$hasStaticCall) { + return '1c'; + } + + // MethodCall/NullsafeMethodCall — Phase 2. + if ($hasMethodCall && !$hasFuncCall && !$hasStaticCall && !$hasClassConst && !$hasExpression) { + return '2'; + } + + // StaticCall — Phase 2 (custom transformation of a typed static call, same complexity as MethodCall). + if ($hasStaticCall && !$hasFuncCall && !$hasMethodCall && !$hasClassConst && !$hasExpression) { + return '2'; + } + + // Multiple or ambiguous — Phase 4. + return '4'; +} + +function classifyFuncCallSubPhase(string $content): string +{ + // Service call pattern: ::service( or ->service( or \Drupal::service. + if (preg_match('/\\\\?Drupal::service\b|->service\s*\(/i', $content)) { + return '1a'; + } + // Static call pattern: SomeClass::method(). + if (str_contains($content, '::')) { + return '1b'; + } + return '1a'; +} + +function classifyPhaseFromFilename(string $filename): string +{ + if (str_starts_with($filename, 'remove-deprecated') || str_starts_with($filename, 'strip-removed')) { + return '3'; + } + return 'unknown'; +} + +/** + * Scans custom rector classes, updates entries in-place, returns unmatched classes. + * + * @param array> $entries + * @return array> + */ +function scanImplementedClasses(string $srcDir, string $repoRoot, array &$entries): array +{ + $unmatched = []; + + $relSrcDir = ltrim(str_replace($repoRoot, '', $srcDir), '/'); + $relTestDir = preg_replace('#^src/(Drupal\d+)#', 'tests/src/$1', $relSrcDir); + + foreach (glob($srcDir . '/*.php') as $file) { + $content = file_get_contents($file); + + if (!preg_match('/class\s+(\w+)\s+extends/', $content, $classMatch)) { + continue; + } + $className = $classMatch[1]; + + $issueNumber = extractIssueFromSeeUrl($content); + + $phase = classifyPhaseFromClass($content); + + $relFile = $relSrcDir . '/' . basename($file); + $files = [$relFile]; + + // Add test file path if directory exists. + $testDir = $repoRoot . '/' . $relTestDir . '/' . $className; + if (is_dir($testDir)) { + $files[] = $relTestDir . '/' . $className . '/' . $className . 'Test.php'; + } + + if ($issueNumber !== null && isset($entries[$issueNumber])) { + $existing = $entries[$issueNumber]; + $entries[$issueNumber]['status'] = 'implemented'; + $entries[$issueNumber]['phase'] = $existing['phase'] === 'unknown' ? $phase : $existing['phase']; + + // Support multiple classes per issue (rare but occurs). + if ($existing['status'] === 'implemented' && !empty($existing['class'])) { + $prev = is_array($existing['class']) ? $existing['class'] : [$existing['class']]; + $entries[$issueNumber]['class'] = array_values(array_unique(array_merge($prev, [$className]))); + $entries[$issueNumber]['files'] = array_values(array_unique(array_merge($existing['files'], $files))); + } else { + $entries[$issueNumber]['class'] = $className; + $entries[$issueNumber]['files'] = $files; + } + } else { + // No matching digest entry — store for later. + $unmatched[$className] = [ + 'issue' => $issueNumber ?? 'unknown', + 'digest_file' => null, + 'phase' => $phase, + 'status' => 'implemented', + 'class' => $className, + 'files' => $files, + ]; + } + } + + return $unmatched; +} + +function extractIssueFromSeeUrl(string $content): ?string +{ + if (preg_match('#@see\s+https?://www\.drupal\.org/node/(\d+)#i', $content, $matches)) { + return $matches[1]; + } + return null; +} + +/** + * Classifies phase from a rector class file by inspecting getNodeTypes(). + */ +function classifyPhaseFromClass(string $content): string +{ + $isAbstractDrupalCore = str_contains($content, 'AbstractDrupalCoreRector'); + $hasRemoveNode = str_contains($content, 'REMOVE_NODE'); + + if (!preg_match('/getNodeTypes\s*\(\)[^{]*\{[^}]*return\s*\[([^\]]+)\]/s', $content, $matches)) { + return 'unknown'; + } + + $nodeTypes = $matches[1]; + $hasMethodCall = str_contains($nodeTypes, 'MethodCall') || str_contains($nodeTypes, 'NullsafeMethodCall'); + $hasExpression = str_contains($nodeTypes, 'Expression'); + + if ($hasRemoveNode || $hasExpression) { + return '3'; + } + + if ($hasMethodCall) { + return $isAbstractDrupalCore ? '2' : '2'; + } + + return '4'; +} + +/** + * Builds a map of shortClassName => {class, files} for all currently-implemented entries + * plus unmatched classes. Used to resolve custom rectors referenced in config files. + * + * @param array> $entries + * @param array> $unmatched + * @return array> + */ +function buildClassMap(array $entries, array $unmatched): array +{ + $map = []; + + foreach ($entries as $entry) { + if ($entry['status'] !== 'implemented') { + continue; + } + foreach ((array) $entry['class'] as $cls) { + if ($cls !== null) { + $map[$cls] = ['class' => $cls, 'files' => $entry['files']]; + } + } + } + + foreach ($unmatched as $className => $data) { + $map[$className] = ['class' => $className, 'files' => $data['files']]; + } + + return $map; +} + +/** + * Scans all config/* subdirectories and marks config-only / implemented entries. + * + * @param array> $entries + * @param array> $implementedClasses + */ +function scanConfigFiles(string $configRoot, array &$entries, array $implementedClasses): void +{ + foreach (glob($configRoot . '/*/', GLOB_ONLYDIR) as $configDir) { + $dirName = basename(rtrim($configDir, '/')); + foreach (glob($configDir . '*.php') as $configFile) { + // Skip aggregate "all deprecations" bundle files. + if (preg_match('/drupal-\d+-all-deprecations\.php$/', basename($configFile))) { + continue; + } + $content = file_get_contents($configFile); + $relFile = 'config/' . $dirName . '/' . basename($configFile); + parseConfigBlock($content, $relFile, $entries, $implementedClasses); + } + } +} + +/** + * Parses a config file, associating issue URL comments with rector class calls. + * + * Generic rectors (FunctionToServiceRector etc.) → config-only. + * Custom rectors already found in src/ → implemented (linked via $implementedClasses). + * + * @param array> $entries + * @param array> $implementedClasses shortClassName => {class, files} + */ +function parseConfigBlock(string $content, string $relFile, array &$entries, array $implementedClasses): void +{ + $lines = explode("\n", $content); + $pendingIssues = []; + + foreach ($lines as $line) { + $trimmed = ltrim($line); + + // Collect issue URL comments. + if (preg_match('#//\s+https?://www\.drupal\.org/node/(\d+)#', $trimmed, $m)) { + $pendingIssues[] = $m[1]; + continue; + } + + // On ruleWithConfiguration(...) or rule(...), process the pending issues. + $isRuleCall = preg_match('/\$rectorConfig->ruleWithConfiguration\s*\(\s*([A-Za-z0-9_\\\\]+)::class/', $trimmed, $m) + || preg_match('/\$rectorConfig->rule\s*\(\s*([A-Za-z0-9_\\\\]+)::class/', $trimmed, $m); + + if ($isRuleCall) { + $shortClass = extractShortClassName($m[1]); + + if (isset(GENERIC_RECTORS[$shortClass])) { + // Generic rector — mark as config-only. + $phase = GENERIC_RECTORS[$shortClass]; + foreach ($pendingIssues as $issue) { + if (isset($entries[$issue]) && $entries[$issue]['status'] === 'pending') { + $entries[$issue]['status'] = 'config-only'; + $entries[$issue]['phase'] = $entries[$issue]['phase'] === 'unknown' ? $phase : $entries[$issue]['phase']; + if (!in_array($relFile, $entries[$issue]['files'])) { + $entries[$issue]['files'][] = $relFile; + } + } + } + } elseif (isset($implementedClasses[$shortClass])) { + // Custom rector already implemented in src/ — mark as implemented. + $classData = $implementedClasses[$shortClass]; + foreach ($pendingIssues as $issue) { + if (isset($entries[$issue]) && $entries[$issue]['status'] === 'pending') { + $entries[$issue]['status'] = 'implemented'; + $entries[$issue]['class'] = $classData['class']; + $entries[$issue]['files'] = $classData['files']; + } + } + } + + $pendingIssues = []; + continue; + } + + // Non-comment, non-ruleWithConfiguration line — clear pending if it's not blank/whitespace. + if ($trimmed !== '' && !str_starts_with($trimmed, '//') && !str_starts_with($trimmed, '*')) { + // Keep pending issues through blank lines and continuation comments. + // Clear only when we hit something substantive that's NOT a rector call. + if (!str_starts_with($trimmed, 'new ') && !str_starts_with($trimmed, ']);')) { + $pendingIssues = []; + } + } + } +} + +function extractShortClassName(string $fqcn): string +{ + $parts = explode('\\', $fqcn); + return end($parts); +} + +/** + * Writes the index as YAML. + * + * @param array> $entries + */ +function writeYaml(array $entries, string $outputFile): void +{ + $counts = countByStatus($entries); + + $out = "# Generated by scripts/generate-rector-index.php — do not edit manually.\n"; + $out .= "# Re-generate with: php scripts/generate-rector-index.php\n"; + $out .= sprintf("generated: '%s'\n", date('Y-m-d H:i:s')); + $out .= "counts:\n"; + $out .= sprintf(" implemented: %d\n", $counts['implemented']); + $out .= sprintf(" config-only: %d\n", $counts['config-only']); + $out .= sprintf(" pending: %d\n", $counts['pending']); + $out .= "entries:\n"; + + foreach ($entries as $key => $entry) { + $out .= sprintf(" '%s':\n", $key); + $out .= sprintf(" issue: '%s'\n", $entry['issue']); + $out .= sprintf(" digest_file: %s\n", $entry['digest_file'] === null ? 'null' : "'" . $entry['digest_file'] . "'"); + $out .= sprintf(" phase: '%s'\n", $entry['phase']); + $out .= sprintf(" status: %s\n", $entry['status']); + if ($entry['class'] === null) { + $out .= " class: null\n"; + } elseif (is_array($entry['class'])) { + $out .= " class:\n"; + foreach ($entry['class'] as $cls) { + $out .= sprintf(" - %s\n", $cls); + } + } else { + $out .= sprintf(" class: %s\n", $entry['class']); + } + + if (empty($entry['files'])) { + $out .= " files: []\n"; + } else { + $out .= " files:\n"; + foreach ($entry['files'] as $file) { + $out .= sprintf(" - %s\n", $file); + } + } + } + + file_put_contents($outputFile, $out); +} + +/** + * @param array> $entries + * @return array + */ +function countByStatus(array $entries): array +{ + $counts = ['implemented' => 0, 'config-only' => 0, 'pending' => 0, 'unknown' => 0]; + foreach ($entries as $entry) { + $status = $entry['status']; + $counts[$status] = ($counts[$status] ?? 0) + 1; + } + return $counts; +} diff --git a/.claude/scripts/setup-repos.sh b/.claude/scripts/setup-repos.sh new file mode 100755 index 000000000..62a044a0d --- /dev/null +++ b/.claude/scripts/setup-repos.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Clones or updates the external repos that drupal-rector skills depend on. +# +# Usage: bash .claude/scripts/setup-repos.sh [--update] +# +# Without --update: skips repos that are already cloned. +# With --update: runs `git fetch` on existing clones. +# +# Repos are cloned into repos/ (gitignored) so they are accessible from +# inside the ddev container at /var/www/html/repos/. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPOS_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)/repos" +mkdir -p "$REPOS_DIR" + +clone_or_update() { + local name="$1" + local url="$2" + local branch="${3:-}" + local dest="$REPOS_DIR/$name" + + if [ -d "$dest/.git" ]; then + echo "==> Updating $name..." + git -C "$dest" fetch ${branch:+origin "$branch"} 2>&1 | tail -3 + git -C "$dest" reset --hard FETCH_HEAD + else + echo "==> Cloning $name..." + local clone_args=() + [ -n "$branch" ] && clone_args+=(--branch "$branch" --single-branch) + git clone "${clone_args[@]}" "$url" "$dest" + fi +} + +clone_or_update drupal-digests "https://github.com/dbuytaert/drupal-digests.git" +clone_or_update drupal-core "https://github.com/drupal/drupal.git" "11.x" + +echo "" +echo "Done. Repos available at:" +echo " repos/drupal-digests → /var/www/html/repos/drupal-digests (inside ddev)" +echo " repos/drupal-core → /var/www/html/repos/drupal-core (inside ddev)" diff --git a/.claude/skills/README.md b/.claude/skills/README.md new file mode 100644 index 000000000..3138e699f --- /dev/null +++ b/.claude/skills/README.md @@ -0,0 +1,105 @@ +# drupal-rector Claude Code Skills + +Claude Code skills for the drupal-rector development workflow. Each skill is a structured prompt that guides Claude through a specific task — invoke them with `/skill-name` in Claude Code. + +## Skills + +### `/rector-discover` + +Lists unimplemented rules from [drupal-digests](https://github.com/dbuytaert/drupal-digests), grouped by implementation phase. Use this to find what to work on next. + +``` +/rector-discover +/rector-discover --phase 2 +/rector-discover --phase 1a --limit 5 +``` + +Maintains `docs/rector-index.yml` as a derived index — regenerated automatically when absent or stale. + +--- + +### `/rector-implement ` + +Converts a single drupal-digests rule into a complete drupal-rector implementation with tests. Follows the 14-step canonical workflow in `.claude/skills/prompts/digest-to-rector-prompt.md` and enforces two additional quality gates: + +- **QG-A — Type Guard Audit**: ensures every `MethodCall`/`PropertyFetch` node is guarded by `isObjectType()` to avoid false positives on untyped code. +- **QG-B — Version-Gating Tests**: for BC-wrapped rectors (`AbstractDrupalCoreRector`), adds a `testBelowVersion()` test and a `fixture-below-version/` fixture proving the transformation is suppressed on older Drupal versions. + +``` +/rector-implement repos/drupal-digests/rector/rules/replace-deprecated-sessionmanager-delete-with-3577376.php +``` + +--- + +### `/rector-qa ` + +Four-pass quality review of an existing rector. Use before merging or when auditing existing implementations. + +| Pass | What it checks | +|------|---------------| +| 1 — Type Guard | `isObjectType()` guard present for all MethodCall/PropertyFetch nodes | +| 2 — Fixture Coverage | `basic.php.inc`, `no_change_unrelated.php.inc`, `fixture-below-version/` present as required | +| 3 — BC Decision | Base class (`AbstractRector` vs `AbstractDrupalCoreRector`) matches the deprecation's version and node type | +| 4 — @see URL | Docblock URL points to the correct Drupal.org issue or change record | + +Produces a `PASS / WARN / FAIL` verdict per pass and an overall merge-readiness summary. + +``` +/rector-qa ReplaceSessionManagerDeleteRector +``` + +--- + +### `/rector-live-test ` + +Finds real contrib modules that use the deprecated API a rector targets, then runs the rector against them to verify it transforms real-world code correctly. Uses [api.tresbien.tech](https://api.tresbien.tech) JSON API as primary search, falls back to the Drupal GitLab API. + +``` +/rector-live-test ReplaceSessionManagerDeleteRector +/rector-live-test 3577376 +``` + +Results report files changed per module and flag zero-match cases with a diagnosis table (untyped code, wrong module version, cache, node type mismatch). + +--- + + +## Supporting scripts + +Located in `.claude/scripts/` — shared utilities invoked by the skills above. + +| Script | Purpose | +|--------|---------| +| `setup-repos.sh` | Clones `repos/drupal-digests` and `repos/drupal-core` (shallow). Pass `--update` to refresh. | +| `generate-rector-index.php` | Regenerates `docs/rector-index.yml` from the digests source. | + +The live-test integration setup lives alongside its skill in `.claude/skills/rector-live-test/`: + +| File | Purpose | +|------|---------| +| `setup-rector-test.sh` | Creates a DDEV Drupal 11 project with all contrib test modules wired to the local rector branch. | +| `drupal-rector-test/` | Companion directory for the generated test project. | + +## Workflow + +A typical cycle for adding a new rector: + +``` +/rector-discover --phase 2 --limit 1 + → picks the next pending rule + +/rector-implement repos/drupal-digests/rector/rules/.php + → writes rector class, fixture, test, config; runs phpstan + phpunit + +/rector-live-test + → validates against real contrib modules + +/rector-qa + → final four-pass quality check before opening a PR +``` + +## Requirements + +- PHP 8.1+ +- [DDEV](https://ddev.com) (for `setup-rector-test.sh` and running tests inside the container) +- Clone `repos/drupal-digests` and `repos/drupal-core` via `bash .claude/scripts/setup-repos.sh` diff --git a/.claude/skills/prompts/analyse-test-results.md b/.claude/skills/prompts/analyse-test-results.md new file mode 100644 index 000000000..ced171ea6 --- /dev/null +++ b/.claude/skills/prompts/analyse-test-results.md @@ -0,0 +1,21 @@ +--- +Analyze the rector test log generated after running the rector-live-test. The user wants to know: +1. Which rectors might not be working correctly (failures, errors, unexpected behavior) +2. Which rectors should trigger but don't (missed transformations) + +Read the file in chunks (it's large, use offset/limit). Look for: +- Test FAILED or ERROR lines +- Rectors where "0 files changed" when changes were expected +- PHPStan errors or exceptions during rector runs +- Any patterns suggesting a rector ran but didn't apply its transformation +- Any "no changes" results that seem wrong +- Skipped tests or rectors with warnings + +Produce a structured report: +- Section 1: Rectors with failures/errors (list rector name + what went wrong) +- Section 2: Rectors that appear to not trigger when they should (list rector name + why you suspect it) +- Section 3: Any other notable issues + +Be specific with rector class names and test names where available. + +Add a table at the end of the report with key metrics like; rectors with changes, rectors wihtout changes, rectors skipped. diff --git a/.claude/skills/prompts/digest-to-rector-prompt.md b/.claude/skills/prompts/digest-to-rector-prompt.md new file mode 100644 index 000000000..90f5d06f3 --- /dev/null +++ b/.claude/skills/prompts/digest-to-rector-prompt.md @@ -0,0 +1,704 @@ +# Conversion Prompt: drupal-digests → drupal-rector + +This is a structured prompt for converting a single +[drupal-digests](https://github.com/dbuytaert/drupal-digests) rector rule into a +fully drupal-rector–compliant implementation with tests. + +**Usage:** Start a Claude Code session in the drupal-rector repository, then say: + +> Convert the drupal-digests rule at `[path-to-rule-file]` following the prompt in +> `.claude/skills/prompts/digest-to-rector-prompt.md`. + +The agent will read both this document and the target rule, then produce all output files. + +--- + +## Prerequisites + +The following directories must exist (created by the scaffold in U1): +- `src/Drupal11/Rector/Deprecation/` +- `tests/src/Drupal11/Rector/Deprecation/` + +If they are missing, run: `mkdir -p src/Drupal11/Rector/Deprecation tests/src/Drupal11/Rector/Deprecation` + +**Stub version:** The test stub at `stubs/Drupal/Drupal.php` has `VERSION = '11.99.x-dev'`. This +is the default version used by `AbstractDrupalCoreRector::installedDrupalVersion()` for any test +that does not set an explicit override. Do not revert it to `10.99.x-dev` — that would silently +disable all Drupal 11 rules in the test suite. + +For tests that need to simulate a specific Drupal version (e.g., to verify a rule does NOT fire +on an older version), use `DrupalRectorSettings::setDrupalVersion($version)` via the service +container. Cleanup is handled automatically by `AbstractDrupalRectorTestCase::tearDown()` — do +not add a `try`/`finally` block. Standard conversion tests do not need this — the stub default +(`11.99.x-dev`) is sufficient for normal fixture testing. + +--- + +## Step 1 — Confirm input + +You will be given a path to a drupal-digests rule file. Confirm the file exists and read it +completely. The file is typically at: +``` +[path-to-drupal-digests-repo]/rector/rules/[rule-filename].php +``` + +Extract from the file: +- **Class name** — the PHP class name (e.g., `FormLocationRector`) +- **Node types** — the array returned by `getNodeTypes()` (e.g., `[ClassConstFetch::class]`) +- **Refactor logic** — the full body of `refactor()` (or `refactorWithConfiguration()` if present) +- **CodeSample before** — the first string argument to `CodeSample` or `ConfiguredCodeSample` +- **CodeSample after** — the second string argument to `CodeSample` or `ConfiguredCodeSample` +- **Issue number** — the number from the filename or from the comment `// Source: https://www.drupal.org/node/[number]` + +--- + +## Step 1b — Split check + +If `refactor()` (or the rule's code samples) handles **more than one** deprecated name (function, method, or constant), pause before proceeding and ask: + +> For each deprecated name, could it be independently applied without the others? + +**Split them if:** +- Each has a different replacement pattern (e.g., one → string literal, another → service call) +- Each could be useful without the others +- Any individual one fits a generic rector from Step 4b — that one becomes a config entry, not a class method + +**Keep them together if:** +- They are semantically inseparable (always migrated as a unit, e.g., an old getter/setter pair) +- They share exactly the same replacement pattern (e.g., 10 procedural functions all mapping to service methods on the same class) + +**If splitting:** implement each piece separately — custom rector for patterns that need custom code, config entry for patterns that fit a generic rector — and use distinct, descriptive class names (e.g., `ReplaceTwigExtensionRector` not `TwigEngineFunctionsRector`). + +--- + +## Step 2 — Read the companion issue markdown + +The issue markdown is at: +``` +[path-to-drupal-digests-repo]/issues/drupal-core/[issue-number].md +``` + +Read it completely. Extract: +- **Introduced version** — from the `## Impact` section, e.g.: + `deprecated in drupal:11.4.0` → `'11.4.0'` +- **Removal version** — e.g., `removed in drupal:13.0.0` → `'13.0.0'` +- **New API FQCN** — the fully-qualified class name of the replacement API, from `## Upgrade` or `## Technical details` +- **Description** — one-sentence summary of what this rule does +- **Change record number** — scan for any `drupal.org/node/` link in the "Upgrade path", + "Change record", or "Technical details" sections. A link like + `[#3567879](https://www.drupal.org/node/3567879)` is the change record node number. + Note it separately from the issue number — they are usually different. + +If any of the above (including the change record number) are missing or ambiguous, proceed to Step 3. Otherwise skip Step 3. + +--- + +## Step 3 — Optional: fetch from Drupal.org (only if Step 2 was insufficient) + +If the introduced version, removal version, replacement FQCN, **or change record number** is not clear from the markdown: + +Fetch the Drupal.org issue page: +``` +https://www.drupal.org/node/[issue-number] +``` + +Look for: +- A "Change records for this issue" section or "Related change records" block — the linked node number is the change record. +- The `deprecated in drupal:X.Y.Z` wording and code examples for version/FQCN confirmation. + +--- + +## Step 4 — Classify the rule (BC decision) + +Answer these questions using the information gathered: + +**Q1: What node types does the rule process?** +- List each type from `getNodeTypes()`. + +**Q2: Is there an Expr → Expr transformation?** +- The authoritative check (from `AbstractDrupalCoreRector::refactor()` line 92) is: + `if ($node instanceof Node\Expr && $result instanceof Node\Expr)`. +- If **both** the input node and the returned node are `Node\Expr` subtypes → BC wrapping is **eligible**. +- `Node\Expr` subtypes include: `FuncCall`, `MethodCall`, `StaticCall`, `NullsafeMethodCall`, + `New_`, `Array_`, `ClassConstFetch`, `ConstFetch`, `String_`, `Int_`, `PropertyFetch`, and more. +- `Class_` (structural node) is **not** a `Node\Expr` → BC wrapping is not applicable. +- `ArrayItem` (`Node\Expr\ArrayItem`) extends `Node\Expr`, but the **ArrayItem node itself** cannot + be replaced by a `StaticCall` — doing so would remove the `key => value` structure from the + array. However, the **value inside** an ArrayItem can be wrapped in a BC call. Override + `refactor()` to handle this manually (see edge case note in Template B). + +**Q3: Was the deprecation introduced in Drupal >= 10.1.0?** +- Compare the introduced version from Step 2 against `10.1.0`. +- If introduced version >= `10.1.0` AND Q2 is eligible → BC wrapping is **potentially applicable**, but check Q4. +- Otherwise → BC wrapping does **not** apply. + +**Q4: Does the replacement code depend on a new Drupal API?** + +This is the key semantic question that overrides the structural eligibility from Q2/Q3. + +Ask: *Could the transformed code run unchanged on a Drupal version that predates the deprecation?* + +- **Yes, it depends on a new API** → BC wrapping IS needed. + The replacement calls a function, method, class, or constant that was introduced at the same + time as the deprecation. Running the new code on an older Drupal would cause a fatal error or + missing-symbol error. The BC wrapper lets contrib code work on both old and new Drupal + simultaneously. + > Example: `locale_config_batch_set_config_langcodes()` → `locale_config_batch_update_default_config_langcodes()`. + > The new function only exists on Drupal ≥ 11.1.0; calling it on 11.0.x would fail. + +- **No, the replacement is version-agnostic** → BC wrapping is **NOT** needed — use `AbstractRector`. + The replacement is pure PHP, uses only native PHP functions, or uses Drupal APIs that existed + long before this deprecation. The transformed code is safe to run on any Drupal version, so + there is nothing to guard with a version check. + > Example: `uasort($arr, 'system_sort_themes')` → `uasort($arr, static function ($a, $b) { … })`. + > The inline closure is pure PHP and works on every Drupal version; BC wrapping adds no value. + +**Decision:** +- Q2 eligible AND Q3 >= 10.1.0 AND Q4 = new API → Use `AbstractDrupalCoreRector` + `DrupalIntroducedVersionConfiguration` +- Q4 = version-agnostic replacement (or Q2/Q3 not met) → Use `AbstractRector` + +**Quick reference:** + +| Input node | Output node | Replacement type | Introduced | Base class | BC wrapping | +|---|---|---|---|---|---| +| `FuncCall` | `FuncCall` (renamed) | new Drupal function | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `FuncCall` | `StaticCall` | new static method | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `FuncCall` | `MethodCall` | new service method | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `MethodCall` | `MethodCall` | new method on same class | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `Array_` | `Array_` | new class constant/callable | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `New_` | `New_` | new class name | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `ClassConstFetch` | `ClassConstFetch` | new class constant | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes | +| `FuncCall` | `FuncCall` (modified args) | pure PHP / no new API | any | `AbstractRector` | No | +| `MethodCall` | `FuncCall` | native PHP function | any | `AbstractRector` | No | +| `FuncCall` | `StaticCall` | any | < 10.1.0 | `AbstractRector` | No | +| `ArrayItem` | `ArrayItem` | new Drupal API | >= 10.1.0 | `AbstractDrupalCoreRector` | Yes (wrap value, not the node) | +| `ArrayItem` | `ArrayItem` | version-agnostic | any | `AbstractRector` | No | +| `Class_` (structural) | `Class_` | any | any | `AbstractRector` | No (not an Expr) | + +--- + +## Step 4b — Check for existing generic rectors (BEFORE writing a custom class) + +Before generating a new PHP class, check whether the transformation can be expressed as a +configuration entry for an existing generic rector in `src/Rector/Deprecation/`. This is the +preferred path — it avoids creating new classes for patterns that drupal-rector already handles. + +**Check the decision table:** + +| Transformation pattern | Generic rector to use | +|---|---| +| Global function call removed entirely (no replacement) | `FunctionCallRemovalRector` | +| Global function → static class method | `FunctionToStaticRector` | +| Global function → `\Drupal::service('…')->method()` | `FunctionToServiceRector` | +| Global function → method on its first argument (e.g. `fn($obj)` → `$obj->method()`) | `FunctionToFirstArgMethodRector` | +| `\Drupal::service('old.id')` → `\Drupal::service('new.id')` | `DrupalServiceRenameRector` | +| Instance method renamed (with receiver type check) | `MethodToMethodWithCheckRector` | +| Class constant → different class constant | `ClassConstantToClassConstantRector` | +| Global constant → class constant | `ConstantToClassConstantRector` | +| Class/interface/trait renamed or moved to new namespace | `RenameClassRector` (from Rector core) | +| `DeprecationHelper::backwardsCompatibleCall()` wrapper removal | `DeprecationHelperRemoveRector` | +| Anything else | Write a custom class (continue to Step 5) | + +**If a generic rector matches, do this instead of Steps 5–10:** + +1. Add the configuration entry to `config/drupal-11/drupal-11.4-deprecations.php` (or the + appropriate versioned file), inside the matching `$rectorConfig->ruleWithConfiguration()` block. + +2. Add a fixture file to the existing generic rector's test directory: + `tests/src/Rector/Deprecation/[GenericRectorName]/fixture/[descriptive-name].php.inc` + +3. Add the configuration entry to the generic rector's test config: + `tests/src/Rector/Deprecation/[GenericRectorName]/config/configured_rule.php` + +4. Run the existing test suite: + ```bash + vendor/bin/phpunit tests/src/Rector/Deprecation/[GenericRectorName]/ + ``` + +5. Skip to Step 11 (fix-style) then Step 12 (phpstan) then Step 13 (test). + +**Configuration entry syntax by generic rector:** + +```php +// FunctionCallRemovalRector — removes the entire statement; no replacement +new FunctionCallRemovalConfiguration('[deprecatedFunctionName]'), + +// FunctionToStaticRector +new FunctionToStaticConfiguration('[introducedVersion]', '[deprecatedFunctionName]', '[ClassName]', '[methodName]'), +// optional 5th arg: arg reorder map, e.g. [0 => 1, 1 => 0] to swap first two args + +// FunctionToServiceRector +new FunctionToServiceConfiguration('[introducedVersion]', '[deprecatedFunctionName]', '[ServiceName]', '[serviceMethodName]'), +// ServiceName is a string literal: 'theme.registry' or 'Drupal\module\Hook\SomeHooks' + +// MethodToMethodWithCheckRector — receiver must be typed as the given interface/class +new MethodToMethodWithCheckConfiguration('[ReceiverClass\\FQCN]', '[oldMethodName]', '[newMethodName]'), +// no introducedVersion — applies unconditionally; no BC wrapping + +// ClassConstantToClassConstantRector +new ClassConstantToClassConstantConfiguration('[OldClass\\FQCN]', '[OLD_CONST]', '[NewClass\\FQCN]', '[NewConst]'), +// no introducedVersion — applies unconditionally; no BC wrapping + +// ConstantToClassConstantRector — replaces bare global constant (ConstFetch) with class constant +new ConstantToClassConfiguration('[GLOBAL_CONSTANT_NAME]', '[TargetClass\\FQCN]', '[CONST_NAME]', '[introducedVersion]'), +// introducedVersion is required; triggers DeprecationHelper BC wrapping for versions >= 10.1.0 + +// FunctionToFirstArgMethodRector — fn($obj) → $obj->method(); first arg must be the receiver +new FunctionToFirstArgMethodConfiguration('[introducedVersion]', '[deprecatedFunctionName]', '[methodName]'), +// introducedVersion triggers DeprecationHelper BC wrapping; omit (use D9 BC wrapper) for older entries + +// DrupalServiceRenameRector — \Drupal::service('old.id') → \Drupal::service('new.id') +new DrupalServiceRenameConfiguration('[introducedVersion]', '[deprecated.service.id]', '[new.service.id]'), +// introducedVersion triggers DeprecationHelper BC wrapping; omit (use D8 BC wrapper) for older entries + +// RenameClassRector — pass an associative array directly, not a configuration object +$rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + '[Old\\Class\\FQCN]' => '[New\\Class\\FQCN]', +]); +// use Rector\Renaming\Rector\Name\RenameClassRector; at top of config file +``` + +**If no generic rector matches, continue to Step 5 to generate a custom class.** + +--- + +## Step 5 — Derive the class name and file paths + +**Class name:** +- Use the class name from the digests rule file (not the filename). +- The class name typically already ends in `Rector`. If not, append it. + +**drupal-rector file paths (all relative to the drupal-rector repo root):** +``` +src/Drupal11/Rector/Deprecation/[ClassName].php +tests/src/Drupal11/Rector/Deprecation/[ClassName]/[ClassName]Test.php +tests/src/Drupal11/Rector/Deprecation/[ClassName]/config/configured_rule.php +tests/src/Drupal11/Rector/Deprecation/[ClassName]/fixture/basic.php.inc +``` + +--- + +## Step 6 — Generate the rule class + +Write `src/Drupal11/Rector/Deprecation/[ClassName].php`. + +### Template A: Simple rule (AbstractRector, no BC) + +Use when Step 4 concluded: BC wrapping does NOT apply. + +```php +> */ + public function getNodeTypes(): array + { + return [/* [node types from Step 1] */]; + } + + /** @param [NodeType] $node */ + public function refactor(Node $node): ?Node + { + // [copy refactor() body from the digests rule unchanged] + + // TYPE GUARD — required for every MethodCall/NullsafeMethodCall/PropertyFetch handler. + // Add an isObjectType() check so unrelated classes with the same method/property name + // are not accidentally transformed. Add it *after* the name check: + // + // if (!$this->isName($node->name, 'theMethod')) { return null; } + // if (!$this->isObjectType($node->var, new ObjectType('Fully\Qualified\InterfaceName'))) { return null; } + // + // Look up the FQCN in repos/drupal-core; prefer the interface over the concrete class. + // Omit only for FuncCall (global functions), ClassConst, or class-declaration nodes. + } +} +``` + +### Template B: BC-capable rule (AbstractDrupalCoreRector) + +Use when Step 4 concluded: BC wrapping APPLIES. + +```php +> */ + public function getNodeTypes(): array + { + return [/* [node types from Step 1] */]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration) + { + // [copy refactor() body from the digests rule — the base class handles BC wrapping automatically] + // Important: return the NEW call node. Do NOT call parent::refactor() or handle BC here. + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + '[description from Step 2]', + [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +[CodeSample before string from Step 1] +CODE_BEFORE, + <<<'CODE_AFTER' +[CodeSample after string from Step 1] +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('[introduced version from Step 2]')] + ), + ] + ); + } +} +``` + +**Adaptation notes:** +- Remove `final` keyword — drupal-rector classes are not final. +- Remove `use Rector\Config\RectorConfig` from the rule class (it belongs only in config files). +- Keep all private constants, arrays, and helper methods unchanged. +- For multi-node-type rules (two or more different node types in `getNodeTypes()`): + - **If both transformations are simple (no BC):** Keep them in one class, use `AbstractRector`. + Both node types go in `getNodeTypes()` and are handled by type-checking inside `refactor()`. + - **If one needs BC and the other doesn't:** Split into two separate rector classes. + `AbstractDrupalCoreRector::refactor()` assumes all transformations share the same BC + configuration, so mixing is not possible in a single class. + +**Shallow-clone warning (critical for correctness):** +PHP's `clone` is a shallow copy — child objects in arrays (e.g. `$node->args[]`, `$node->items[]`) +are the same object instances in both the original and the clone. If you mutate a child after +cloning the parent, the mutation appears on **both** the original and the cloned node. This breaks +BC wrapping: both the `fn() => ` and `fn() => ` sides of the +`DeprecationHelper::backwardsCompatibleCall()` will show the mutated (new) value. + +**Rule:** Always clone child nodes before mutating them: +```php +// WRONG — mutates the shared Arg object; BC call gets new value on both sides +$arg->value = $replacement; + +// CORRECT — clone the child, mutate the clone, put it back in the cloned parent +$newArg = clone $arg; +$newArg->value = $replacement; +$cloned->args[$index] = $newArg; +``` +This applies to any child node you modify: `Arg`, `ArrayItem`, `Node\Identifier`, etc. + +**ArrayItem edge case:** +`ArrayItem` (`Node\Expr\ArrayItem`) is an `Expr` node but the **node itself** cannot be replaced +by a `StaticCall` — that would destroy the `key => value` structure. However, the **value** inside +the ArrayItem can be wrapped in a BC call. Override `refactor()` and wrap only `$result->value`: +```php +public function refactor(Node $node): ?Node +{ + if ($node instanceof ArrayItem) { + foreach ($this->configuration as $configuration) { + if (!$this->rectorShouldApplyToDrupalVersion($configuration)) { + continue; + } + if ($this->isInBackwardsCompatibleCall($node)) { + continue; + } + $result = $this->refactorArrayItem($node); + if ($result === null) { + return null; + } + if ($this->supportBackwardsCompatibility($configuration)) { + // Wrap the VALUE in DeprecationHelper, not the ArrayItem itself. + $cloned = clone $result; + $cloned->value = $this->createBcCallOnExpr( + $node->value, + $result->value, + $configuration->getIntroducedVersion() + ); + return $cloned; + } + return $result; + } + return null; + } + // Let the parent handle BC wrapping for all other Expr nodes. + return parent::refactor($node); +} +``` +Result: `['fetch' => DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => FetchAs::Associative, fn() => \PDO::FETCH_ASSOC)]` — the array item is preserved, only its value is version-gated. + +--- + +## Step 7 — Generate the fixture file + +Write `tests/src/Drupal11/Rector/Deprecation/[ClassName]/fixture/basic.php.inc`. + +Format: +``` + +----- + +``` + +**Rules:** +- The `-----` separator must be on its own line with no surrounding whitespace. +- Remove `use` statements from the "after" section if the new code uses FQCNs (backslash-prefixed). +- For BC-wrapped rules: the "after" section must show the full + `\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, 'X.Y.Z', fn() => , fn() => )` + output rather than the plain transformed code. The exact format is produced by the rector at + runtime — if unsure, run `vendor/bin/phpunit` once and read the failure diff to copy the actual output. +- If the CodeSample before/after strings are not full PHP files, wrap them appropriately (add ``). +- Add realistic surrounding context if the snippet is very minimal (e.g., wrap a bare expression in a function body). + +--- + +## Step 8 — Generate the test class + +Write `tests/src/Drupal11/Rector/Deprecation/[ClassName]/[ClassName]Test.php`. + +**For simple rules (AbstractRector, no BC):** + +```php +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} +``` + +**For BC-capable rules (AbstractDrupalCoreRector):** Use the full `testAboveVersion` / +`testBelowVersion` form from the QG-B section of SKILL.md. Do NOT use the simple `test()` form +above — the version-gating tests are required for all BC-wrapped rectors. + +--- + +## Step 9 — Generate the test config + +Write `tests/src/Drupal11/Rector/Deprecation/[ClassName]/config/configured_rule.php`. + +### For simple rules (AbstractRector, no BC) + +```php +commentService->addDrupalRectorComment($node, 'Please verify this change manually.'); +``` + +--- + +## Step 10 — Write all files + +Using the write tool, create all four files at the paths derived in Step 5: +1. `src/Drupal11/Rector/Deprecation/[ClassName].php` +2. `tests/src/Drupal11/Rector/Deprecation/[ClassName]/[ClassName]Test.php` +3. `tests/src/Drupal11/Rector/Deprecation/[ClassName]/config/configured_rule.php` +4. `tests/src/Drupal11/Rector/Deprecation/[ClassName]/fixture/basic.php.inc` + +--- + +## Step 11 — Fix code style + +Run code style fixer on the generated rule class: + +```bash +ddev composer fix-style +``` + +This normalises import ordering, spacing, and other formatting conventions. + +--- + +## Step 12 — Run static analysis + +```bash +ddev composer phpstan +``` + +Fix any reported issues before proceeding. Common issues: +- Missing `@param` / `@return` types on overridden methods +- Incorrect type hints (e.g., `Node` vs a specific subtype) +- `refactorWithConfiguration()` must declare its return type as mixed or `Node|Node[]|null` + +--- + +## Step 13 — Run the test + +```bash +vendor/bin/phpunit tests/src/Drupal11/Rector/Deprecation/[ClassName]/ +``` + +**If tests pass:** The conversion is complete. Commit all four files together. + +**If tests fail:** Diagnose the failure: + +- **"Expected output does not match actual output"** — The fixture "after" section is wrong. + Run rector on the "before" section manually to see what it actually produces, then update the fixture. +- **"Class not found"** — Check the namespace declaration and file path match. +- **"Method not found"** — Verify the base class was chosen correctly (Step 4). +- **"Fixture has no before/after separator"** — The `-----` line is missing or has extra spaces. +- **phpstan errors** — Fix type declarations in the rule class and re-run. + +After fixing failures, update the conversion prompt (this file) with any decision rule that +prevented the failure from occurring, so future conversions avoid the same issue. + +--- + +## Step 14 — Done + +Leave committing to the human reviewer. Do not run any git commands. + +--- + +## Checklist + +Before marking a conversion complete, verify: + +- [ ] Step 4b was checked — a generic rector was used if the pattern matched, custom class only if it didn't +- [ ] (Custom class only) `declare(strict_types=1)` is present in the rule class +- [ ] (Custom class only) Namespace is `DrupalRector\Drupal11\Rector\Deprecation` +- [ ] (Custom class only) `final` keyword is removed +- [ ] (Custom class only) `use Rector\Config\RectorConfig` is NOT in the rule class +- [ ] (Custom class only) Base class matches the BC decision from Step 4 +- [ ] (Custom class only) `getNodeTypes()` lists all node types from the original rule +- [ ] Fixture `-----` separator is on its own line +- [ ] `vendor/bin/phpunit` passes for the relevant test directory +- [ ] `ddev composer fix-style` has been run +- [ ] `ddev composer phpstan` reports no errors for new/modified files diff --git a/.claude/skills/prompts/recipes/RECIPES.md b/.claude/skills/prompts/recipes/RECIPES.md new file mode 100644 index 000000000..3ced63a28 --- /dev/null +++ b/.claude/skills/prompts/recipes/RECIPES.md @@ -0,0 +1,85 @@ +# Recipe Index + +Recipes are fill-in-the-blank templates that replace the 14-step exploration workflow +in `digest-to-rector-prompt.md` for well-understood patterns. + +**How to use:** Identify which recipe matches the digest rule, extract the required values, +and follow only that recipe. Skip `digest-to-rector-prompt.md` entirely. + +--- + +## Routing guide + +Read the digest. Answer these questions in order: + +1. **What node type is transformed?** + - `FuncCall` → continue + - `MethodCall` / `NullsafeMethodCall` → go to **method-rename** or **custom** + - `ClassConstFetch` → go to **class-const-rename** or **global-const-to-class-const** + - `String_` → **custom** (no recipe yet) + - Multiple node types in one class → **custom** (`digest-to-rector-prompt.md`) + +2. **What does the replacement look like?** + - `fn()` is removed entirely, no replacement → [`config-only-template.md#functioncallremovalrector`](config-only-template.md#functioncallremovalrector) + - `SomeClass::staticMethod()` → [`config-only-template.md#functiontostaticroctor`](config-only-template.md#functiontostaticroctor) + - `\Drupal::service('string.id')->method()` → [`config-only-template.md#functiontoservicerector`](config-only-template.md#functiontoservicerector) + - `\Drupal::service(FqcnClass::class)->method()`, simple 1-to-1 → [`config-only-template.md#functiontoservicerector`](config-only-template.md#functiontoservicerector) (`FunctionToServiceConfiguration(..., true)`) + - `\Drupal::service(FqcnClass::class)->method()`, arg-count dispatch / chained / mixed → **func-to-class-service-bc** or **func-to-class-service-bc-multi** ✓ + - `$firstArg->method()` (method on first argument) → [`config-only-template.md#functiontofirstargmethodrector`](config-only-template.md#functiontofirstargmethodrector) + - `\Drupal::service('old.id')` → `\Drupal::service('new.id')` → [`config-only-template.md#drupalservicerenamerector`](config-only-template.md#drupalservicerenamerector) + - Something else → **custom** (`digest-to-rector-prompt.md`) + +3. **For MethodCall:** which generic rector applies? + - Rename with receiver type check → [`config-only-template.md#methodtomethodwithcheckrector`](config-only-template.md#methodtomethodwithcheckrector) + - Complex transformation → **custom** + +4. **For ClassConstFetch:** + - `OldClass::CONST` → `NewClass::CONST` → [`config-only-template.md#classconstanttoclassconstantrector`](config-only-template.md#classconstanttoclassconstantrector) + - `GLOBAL_CONST` → `SomeClass::CONST` → [`config-only-template.md#constanttoclassconstantrector`](config-only-template.md#constanttoclassconstantrector) + +--- + +## Recipe status + +| Recipe file | Pattern | Type | Status | +|---|---|---|---| +| `func-to-class-service-bc.md` | FuncCall → `Fqcn::class` service, complex (dispatch/chained) | custom class | ✅ done | +| `func-to-class-service-bc-multi.md` | FuncCall → `Fqcn::class` service, multiple with complex logic | custom class | ✅ done | +| [`config-only-template.md#functioncallremovalrector`](config-only-template.md#functioncallremovalrector) | FuncCall removed entirely | config-only | ✅ done | +| [`config-only-template.md#functiontostaticroctor`](config-only-template.md#functiontostaticroctor) | FuncCall → static method | config-only | ✅ done | +| [`config-only-template.md#functiontoservicerector`](config-only-template.md#functiontoservicerector) | FuncCall → `'service.id'` method or `Fqcn::class` simple 1-to-1 | config-only | ✅ done | +| [`config-only-template.md#functiontofirstargmethodrector`](config-only-template.md#functiontofirstargmethodrector) | FuncCall → method on first arg | config-only | ✅ done | +| [`config-only-template.md#drupalservicerenamerector`](config-only-template.md#drupalservicerenamerector) | `\Drupal::service('old')` → `'new'` | config-only | ✅ done | +| [`config-only-template.md#methodtomethodwithcheckrector`](config-only-template.md#methodtomethodwithcheckrector) | MethodCall rename with type check | config-only | ✅ done | +| [`config-only-template.md#classconstanttoclassconstantrector`](config-only-template.md#classconstanttoclassconstantrector) | `OldClass::CONST` → `NewClass::CONST` | config-only | ✅ done | +| [`config-only-template.md#constanttoclassconstantrector`](config-only-template.md#constanttoclassconstantrector) | `GLOBAL_CONST` → `Class::CONST` | config-only | ✅ done | + +--- + +## Notes on config-only recipes + +Config-only recipes do **not** create a new PHP class. They: + +1. Add one entry to an existing config file (e.g. `config/drupal-11/drupal-11.4-deprecations.php`) +2. Add one fixture file to the existing generic rector's test directory +3. Add the entry to that rector's test config +4. Run the existing test suite for that rector + +The config file to edit depends on `introducedVersion` — same lookup table as the +custom-class recipes. If the file does not yet import the generic rector class, add the +`use` statement. + +--- + +## What belongs in a recipe vs. the main prompt + +Use a recipe when **all** of these are true: +- The transformation pattern is fully determined (no ambiguity) +- The base class and BC-wrapping decision are obvious from the pattern +- The output is identical boilerplate except for 5–8 substitution values + +Use `digest-to-rector-prompt.md` when: +- The digest rule uses multiple node types that need different BC treatment +- The replacement logic is conditional (e.g., depends on surrounding AST context) +- The rule removes or restructures nodes rather than substituting them +- You are unsure which recipe applies diff --git a/.claude/skills/prompts/recipes/config-only-template.md b/.claude/skills/prompts/recipes/config-only-template.md new file mode 100644 index 000000000..98d2ba161 --- /dev/null +++ b/.claude/skills/prompts/recipes/config-only-template.md @@ -0,0 +1,191 @@ +# Config-only recipe template + +All config-only recipes share the same 5-step structure. Each recipe file specialises +the exact config syntax and fixture shape for one generic rector. + +--- + +## The 5 steps (same for every config-only recipe) + +### Step 1 — Identify the config file + +| introducedVersion | File | +|---|---| +| 11.4.x | `config/drupal-11/drupal-11.4-deprecations.php` | +| 11.3.x | `config/drupal-11/drupal-11.3-deprecations.php` | +| 11.2.x | `config/drupal-11/drupal-11.2-deprecations.php` | +| 11.1.x | `config/drupal-11/drupal-11.1-deprecations.php` | +| 11.0.x | `config/drupal-11/drupal-11.0-deprecations.php` | + +### Step 2 — Add the config entry + +See the specific recipe for the exact entry syntax. +Add the `use` statement for the configuration value object if it is not yet imported. + +### Step 3 — Add a fixture to the generic rector's test directory + +Path: `tests/src/Rector/Deprecation/{{GenericRectorName}}/fixture/{{descriptive-name}}.php.inc` + +Format (no BC wrapper — generic rectors do not produce one): +``` + +----- + +``` + +### Step 4 — Register the fixture in the generic rector's test config + +File: `tests/src/Rector/Deprecation/{{GenericRectorName}}/config/configured_rule.php` + +Add the configuration entry that was added to the deprecations config in Step 2. + +### Step 5 — Run quality checks and commit + +```bash +ddev composer fix-style +ddev composer phpstan +vendor/bin/phpunit tests/src/Rector/Deprecation/{{GenericRectorName}}/ +git add config/drupal-11/drupal-11.{{X}}-deprecations.php \ + tests/src/Rector/Deprecation/{{GenericRectorName}}/ +git commit -m "feat(Drupal11): Add {{GenericRectorName}} config for issue #{{issueNumber}}" +``` + +--- + +## Config entry syntax by generic rector + +### FunctionCallRemovalRector + +Values: `{{functionName}}` + +```php +new FunctionCallRemovalConfiguration('{{functionName}}'), +``` + +No replacement — the entire call statement is deleted. +Fixture "after" is the code with the statement removed entirely. + +--- + +### FunctionToStaticRector + +Values: `{{introducedVersion}}`, `{{functionName}}`, `{{ClassName}}`, `{{methodName}}` + +```php +new FunctionToStaticConfiguration('{{introducedVersion}}', '{{functionName}}', '{{ClassName}}', '{{methodName}}'), +``` + +Optional 5th argument: arg reorder map, e.g. `[0 => 1, 1 => 0]` to swap the first two args. +Fixture "after": `{{ClassName}}::{{methodName}}(args)` +BC-wrapped (introduced >= 10.1.0). + +--- + +### FunctionToServiceRector + +Values: `{{introducedVersion}}`, `{{functionName}}`, `{{serviceId}}`, `{{methodName}}` + +**String service ID** (dotted alias like `'file_system'`, `'renderer'`): +```php +new FunctionToServiceConfiguration('{{introducedVersion}}', '{{functionName}}', '{{serviceId}}', '{{methodName}}'), +``` +Fixture "after": `\Drupal::service('{{serviceId}}')->{{methodName}}(args)` + +**FQCN service ID** (class or interface name like `Drupal\node\NodeGrantsHelper`): +```php +new FunctionToServiceConfiguration('{{introducedVersion}}', '{{functionName}}', '{{fqcn}}', '{{methodName}}', true), +``` +Fixture "after": `\Drupal::service(\{{fqcn}}::class)->{{methodName}}(args)` + +Use `func-to-class-service-bc.md` only when the replacement has custom logic (arg-count dispatch, chained calls, method on first arg, etc.). For a plain 1-to-1 mapping, the 5th `true` argument handles it. + +BC-wrapped (introduced >= 10.1.0). + +--- + +### FunctionToFirstArgMethodRector + +Values: `{{introducedVersion}}`, `{{functionName}}`, `{{methodName}}` + +```php +new FunctionToFirstArgMethodConfiguration('{{introducedVersion}}', '{{functionName}}', '{{methodName}}'), +``` + +`{{functionName}}($obj, ...)` → `$obj->{{methodName}}(...remaining args)` +Fixture "after": `$firstArg->{{methodName}}(...)` +BC-wrapped (introduced >= 10.1.0). + +--- + +### DrupalServiceRenameRector + +Values: `{{introducedVersion}}`, `'{{oldServiceId}}'`, `'{{newServiceId}}'` + +```php +new DrupalServiceRenameConfiguration('{{introducedVersion}}', '{{oldServiceId}}', '{{newServiceId}}'), +``` + +Fixture "after": `\Drupal::service('{{newServiceId}}')` +BC-wrapped (introduced >= 10.1.0). + +--- + +### MethodToMethodWithCheckRector + +Values: `{{ReceiverClass}}`, `{{oldMethod}}`, `{{newMethod}}` + +```php +new MethodToMethodWithCheckConfiguration('{{ReceiverClass}}', '{{oldMethod}}', '{{newMethod}}'), +``` + +No `introducedVersion` — applies unconditionally, no BC wrapping. +`{{ReceiverClass}}` is the FQCN of the interface/class the receiver must be typed as. +Fixture "after": `$receiver->{{newMethod}}(args)` + +--- + +### ClassConstantToClassConstantRector + +Values: `{{OldClass}}`, `{{OLD_CONST}}`, `{{NewClass}}`, `{{NEW_CONST}}` + +```php +new ClassConstantToClassConstantConfiguration('{{OldClass}}', '{{OLD_CONST}}', '{{NewClass}}', '{{NEW_CONST}}'), +``` + +No BC wrapping. +Fixture "after": `{{NewClass}}::{{NEW_CONST}}` + +--- + +### ConstantToClassConstantRector + +Values: `{{GLOBAL_CONST}}`, `{{TargetClass}}`, `{{CONST_NAME}}` + +```php +new ConstantToClassConfiguration('{{GLOBAL_CONST}}', '{{TargetClass}}', '{{CONST_NAME}}'), +``` + +No BC wrapping. +Fixture "after": `\{{TargetClass}}::{{CONST_NAME}}` + +--- + +### RenameClassRector (Rector core) + +Values: `{{OldFqcn}}`, `{{NewFqcn}}` + +```php +$rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + '{{OldFqcn}}' => '{{NewFqcn}}', +]); +``` + +Add `use Rector\Renaming\Rector\Name\RenameClassRector;` at the top of the config file. +No BC wrapping. +Fixture "after": all references to `{{OldFqcn}}` replaced with `{{NewFqcn}}`. diff --git a/.claude/skills/prompts/recipes/func-to-class-service-bc-multi.md b/.claude/skills/prompts/recipes/func-to-class-service-bc-multi.md new file mode 100644 index 000000000..7efe6aff3 --- /dev/null +++ b/.claude/skills/prompts/recipes/func-to-class-service-bc-multi.md @@ -0,0 +1,186 @@ +# Recipe: Multiple FuncCalls → `::class` service methods (BC-wrapped) + +**Use when:** several deprecated global functions all map to methods on the same +`\Drupal::service(SomeClass::class)`, were introduced in Drupal >= 10.1.0, +**and** the replacement requires custom logic: arg-count dispatch to different methods, +chained calls, or method-on-first-arg mixed with service calls. + +For a group of plain 1-to-1 mappings with no custom logic, use multiple +`FunctionToServiceConfiguration(..., true)` config-only entries instead (see `config-only-template.md`). + +**Output:** one new custom rector class + test suite (4–5 files). + +This is the multi-function variant of `func-to-class-service-bc.md`. Use that recipe instead +when there is only one function to replace. + +--- + +## Step 1 — Extract these values from the digest + +**Per-class values:** + +| Placeholder | Where to find it | +|---|---| +| `{{ClassName}}` | PHP class name in the digest file | +| `{{serviceClass}}` | Shared service FQCN, e.g. `Drupal\node\NodeAccessRebuild` | +| `{{introducedVersion}}` | From issue markdown `## Impact`, e.g. `11.4.0` | +| `{{removedVersion}}` | From issue markdown `## Impact`, e.g. `13.0.0` | +| `{{issueNumber}}` | From filename or `@see` comment | + +**Per-function values** (repeat for each deprecated function): + +| Placeholder | Example | +|---|---| +| `{{funcN}}` | `node_access_rebuild` | +| `{{methodN}}` | `rebuild` | +| Arg handling | "forward all args" OR "check count first" (see variants below) | + +--- + +## Step 2 — Determine the target config file + +Same lookup as `func-to-class-service-bc.md` Step 2. + +--- + +## Step 3 — Write the rector class + +Path: `src/Drupal11/Rector/Deprecation/{{ClassName}}.php` + +```php +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + return match ($node->name->toString()) { + '{{func1}}' => $this->buildServiceCall('{{method1}}', $node->args), + '{{func2}}' => $this->buildServiceCall('{{method2}}', $node->args), + // add more cases here + default => null, + }; + } + + /** @param \PhpParser\Node\Arg[] $args */ + private function buildServiceCall(string $method, array $args): MethodCall + { + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch(new FullyQualified('{{serviceClass}}'), 'class'))] + ); + + return new MethodCall($serviceCall, $method, $args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated {{func1}}() and related functions with the {{serviceClass}} service.', + [ + new ConfiguredCodeSample( + '{{before1}}', + '{{after1}}', + [new DrupalIntroducedVersionConfiguration('{{introducedVersion}}')] + ), + // add more ConfiguredCodeSample entries for each function + ] + ); + } +} +``` + +### Variant: arg-count dispatch (getter/setter pattern) + +When one function behaves differently based on whether args are passed (e.g. +`needs_rebuild()` = getter, `needs_rebuild($value)` = setter), replace the match +arm with an if/else: + +```php +'{{funcN}}' => count($node->args) === 0 + ? $this->buildServiceCall('{{getterMethod}}', []) + : $this->buildServiceCall('{{setterMethod}}', $node->args), +``` + +--- + +## Steps 4–9 — Test class, test config, fixtures, registration, quality checks, commit + +Follow Steps 4–9 from `func-to-class-service-bc.md` exactly, substituting the +multi-function fixture below for Step 6. + +### Fixture — include one representative call per function + +`tests/src/Drupal11/Rector/Deprecation/{{ClassName}}/fixture/basic.php.inc` + +``` + +----- + {{after1_no_semi}}, fn() => {{before1_no_semi}}); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '{{introducedVersion}}', fn() => {{after2_no_semi}}, fn() => {{before2_no_semi}}); + +?> +``` + +The below-version fixture is identical before and after for all functions (no change). diff --git a/.claude/skills/prompts/recipes/func-to-class-service-bc.md b/.claude/skills/prompts/recipes/func-to-class-service-bc.md new file mode 100644 index 000000000..ad4123527 --- /dev/null +++ b/.claude/skills/prompts/recipes/func-to-class-service-bc.md @@ -0,0 +1,288 @@ +# Recipe: FuncCall → `::class` service method (BC-wrapped) + +**Use when:** a single deprecated global function is replaced by +`\Drupal::service(SomeClass::class)->method()`, was introduced in Drupal >= 10.1.0, +**and** the replacement requires custom logic: arg-count dispatch to different methods, +chained calls (e.g. `->getFormat()->id()`), or method-on-first-arg mixed with a service call. + +For a plain 1-to-1 mapping with no custom logic, use `FunctionToServiceConfiguration(..., true)` +as a config-only entry instead (see `config-only-template.md`). + +**Output:** one new custom rector class + test suite (4–5 files). + +--- + +## Step 1 — Extract these values from the digest + +| Placeholder | Where to find it | +|---|---| +| `{{ClassName}}` | PHP class name in the digest file | +| `{{functionName}}` | The deprecated function name, e.g. `node_access_grants` | +| `{{serviceClass}}` | Fully-qualified service class, e.g. `Drupal\node\NodeGrantsHelper` | +| `{{methodName}}` | Method to call on the service, e.g. `nodeAccessGrants` | +| `{{introducedVersion}}` | From issue markdown `## Impact` section, e.g. `11.4.0` | +| `{{removedVersion}}` | From issue markdown `## Impact` section, e.g. `13.0.0` | +| `{{issueNumber}}` | From filename or `@see` comment, e.g. `2473041` | +| `{{beforeCode}}` | CodeSample "before" snippet, e.g. `node_access_grants($operation, $account);` | +| `{{afterCode}}` | CodeSample "after" snippet (clean, no BC wrapper), e.g. `\Drupal::service(\Drupal\node\NodeGrantsHelper::class)->nodeAccessGrants($operation, $account);` | + +--- + +## Step 2 — Determine the target config file + +| introducedVersion | Config file | +|---|---| +| 11.4.x | `config/drupal-11/drupal-11.4-deprecations.php` | +| 11.3.x | `config/drupal-11/drupal-11.3-deprecations.php` | +| 11.2.x | `config/drupal-11/drupal-11.2-deprecations.php` | +| 11.1.x | `config/drupal-11/drupal-11.1-deprecations.php` | + +--- + +## Step 3 — Write the rector class + +Path: `src/Drupal11/Rector/Deprecation/{{ClassName}}.php` + +```php +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name || $node->name->toString() !== '{{functionName}}') { + return null; + } + + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch(new FullyQualified('{{serviceClass}}'), 'class'))] + ); + + return new MethodCall($serviceCall, '{{methodName}}', $node->args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated {{functionName}}() with \Drupal::service(\{{serviceClass}}::class)->{{methodName}}().', + [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +{{beforeCode}} +CODE_BEFORE, + <<<'CODE_AFTER' +{{afterCode}} +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('{{introducedVersion}}')] + ), + ] + ); + } +} +``` + +--- + +## Step 4 — Write the test class + +Path: `tests/src/Drupal11/Rector/Deprecation/{{ClassName}}/{{ClassName}}Test.php` + +```php +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} +``` + +--- + +## Step 5 — Write the test config + +Path: `tests/src/Drupal11/Rector/Deprecation/{{ClassName}}/config/configured_rule.php` + +```php + +----- + {{afterCode}}, fn() => {{beforeCode}}); +?> +``` + +> **Note:** In the "after" section, strip any trailing `;` from `{{beforeCode}}` and `{{afterCode}}` +> when they appear inside the `backwardsCompatibleCall` arguments — the `;` belongs on the +> outer statement only. If `{{beforeCode}}` is wrapped in a variable assignment +> (e.g. `$x = fn();`), the BC call wraps only the right-hand side expression. + +### Below-version fixture (no change) + +Path: `tests/src/Drupal11/Rector/Deprecation/{{ClassName}}/fixture-below-version/basic.php.inc` + +``` + +----- + +``` + +--- + +## Step 7 — Register in the deprecations config + +Add to the appropriate config file (`config/drupal-11/drupal-11.{{X}}-deprecations.php`): + +```php +use DrupalRector\Drupal11\Rector\Deprecation\{{ClassName}}; +// (add to the use block at the top) + +// https://www.drupal.org/node/{{issueNumber}} +// {{functionName}}() deprecated in drupal:{{introducedVersion}}, removed in drupal:{{removedVersion}}. +$rectorConfig->ruleWithConfiguration({{ClassName}}::class, [ + new DrupalIntroducedVersionConfiguration('{{introducedVersion}}'), +]); +``` + +--- + +## Step 8 — Run quality checks + +```bash +ddev composer fix-style +ddev composer phpstan +vendor/bin/phpunit tests/src/Drupal11/Rector/Deprecation/{{ClassName}}/ +``` + +All three must pass before committing. + +--- + +## Step 9 — Commit + +```bash +git add src/Drupal11/Rector/Deprecation/{{ClassName}}.php \ + tests/src/Drupal11/Rector/Deprecation/{{ClassName}}/ \ + config/drupal-11/drupal-11.{{X}}-deprecations.php +git commit -m "feat(Drupal11): Add {{ClassName}} for issue #{{issueNumber}}" +``` diff --git a/.claude/skills/rector-discover/SKILL.md b/.claude/skills/rector-discover/SKILL.md new file mode 100644 index 000000000..881dc95ed --- /dev/null +++ b/.claude/skills/rector-discover/SKILL.md @@ -0,0 +1,84 @@ +--- +name: rector-discover +description: Lists unimplemented drupal-digests rules classified by implementation phase. Regenerates docs/rector-index.yml if absent or stale (>24h). Use to find what to work on next, filter by phase, or get a summary count. +argument-hint: "[--phase 1a|1b|1c|2|3|4] [--limit N] [--pending-only]" +allowed-tools: Bash, Read +--- + +# Rector Discover + +Show which drupal-digests deprecation rules still need to be implemented in drupal-rector, grouped by phase. + +## Steps + +### 1. Ensure the digests repo is available + +The canonical path is `repos/drupal-digests` (inside ddev: `/var/www/html/repos/drupal-digests`). Always run the setup script first to clone or update the repositories: + +```bash +bash .claude/scripts/setup-repos.sh +``` + +### 2. Ensure the index is fresh + +Update the `docs/rector-index.yml`: + +```bash +INDEX="docs/rector-index.yml" +echo "Regenerating rector-index.yml…" +php .claude/scripts/generate-rector-index.php --digests-path=repos/drupal-digests +``` + +### 3. Read the index + +Read `docs/rector-index.yml` completely. + +### 4. Apply filters + +If `$ARGUMENTS` contains `--phase X`, show only entries with `phase: 'X'`. +If `$ARGUMENTS` contains `--limit N`, show only the first N entries. +If `$ARGUMENTS` contains `--pending-only`, show only `status: pending` entries (default unless --all is passed). + +### 5. Present results + +Print a summary header: +``` +Rector Index — + implemented: X config-only: Y pending: Z +``` + +Then list pending entries grouped by phase in order: 1a → 1b → 1c → 2 → 3 → 4 → unknown. Eg: `Phase 3 — Remove function call / node removal` + +For each pending entry show: +``` +[Phase 2] ReplaceSessionManagerDeleteRector — issue [#3577376](https://www.drupal.org/i/3577376) + Digest: replace-deprecated-sessionmanager-delete-with-3577376.php +``` + +If all rules are implemented, print: +``` +All rules are implemented or have config-only entries. Nothing pending. +``` + +### 6. Suggest next action + +At the end, suggest the highest-priority pending rule to work on next (Phase 1a first, then 1b, 1c, 2, 3, 4): +``` +Next suggested: /rector-implement repos/drupal-digests/rector/rules/ +``` + +## Phase Reference + +| Phase | Description | Generic rector | +|-------|---------------------------------------------------------------------------------|----------------| +| 1a | FuncCall → service call (`fn(...)` -> `\Drupal::service(...)` | `FunctionToServiceRector` | +| 1a | FuncCall → method on first arg (`fn($obj)` → `$obj->method()`) | `FunctionToFirstArgMethodRector` | +| 1a | Service ID rename (`\Drupal::service('old')` → `\Drupal::service('new')`) | `DrupalServiceRenameRector` | +| 1b | FuncCall → static call on class (`fn(...)` -> `Class::method(...)` | `FunctionToStaticRector` | +| 1c | Class constant → class constant (`OldClass::OLD` → `NewClass::NEW`) | `ClassConstantToClassConstantRector` | +| 1c | Bare global constant → class constant (`DEPRECATED_CONST` → `\Ns\Class::CONST`) | `ConstantToClassConstantRector` | +| 2 | MethodCall rename with type check (`$obj->old()` → `$obj->new()`) | `MethodToMethodWithCheckRector` | +| 2 | MethodCall custom transformation | custom `AbstractRector` or `AbstractDrupalCoreRector` | +| 3 | Remove a function call statement with no replacement | `FunctionCallRemovalRector` | +| 3 | Node removal (other patterns) | custom class returning `REMOVE_NODE` | +| 4 | Complex / multi-node | custom class | diff --git a/.claude/skills/rector-implement/SKILL.md b/.claude/skills/rector-implement/SKILL.md new file mode 100644 index 000000000..93f5a7ff5 --- /dev/null +++ b/.claude/skills/rector-implement/SKILL.md @@ -0,0 +1,218 @@ +--- +name: rector-implement +description: Converts a single drupal-digests rule to a drupal-rector-compliant implementation. Follows .claude/skills/prompts/digest-to-rector-prompt.md steps 1–14 and adds quality gates for type guards (QG-A) and version-gating tests (QG-B). Pass the path to the digests rule file as argument. +argument-hint: "repos/drupal-digests/rector/rules/.php" +allowed-tools: Read, Write, Edit, Bash, Glob +--- + +# Rector Implement + +Convert a single drupal-digests rule into a drupal-rector–compliant implementation with tests. + +## Input + +`$ARGUMENTS` must be the path to a drupal-digests rule file, e.g.: +``` +repos/drupal-digests/rector/rules/replace-deprecated-sessionmanager-delete-with-3577376.php +``` + +If `repos/drupal-digests` does not exist yet, run `bash .claude/scripts/setup-repos.sh` first. + +## Steps + +### Step 0 — Try a recipe first + +Before doing anything else: + +1. Read the digest file. +2. Read `.claude/skills/prompts/recipes/RECIPES.md` and answer the routing questions. +3. If a recipe matches, read that recipe file and follow it **instead of Steps 1–14 below**. + The recipe is self-contained and includes quality checks and commit instructions. +4. If no recipe matches, continue with Steps 1–14. + +Available recipes: +- `func-to-class-service-bc.md` — single FuncCall → `Fqcn::class` service method (BC-wrapped) +- `func-to-class-service-bc-multi.md` — multiple FuncCalls → same `Fqcn::class` service (BC-wrapped) +- `config-only-template.md` — all config-only patterns (FunctionToServiceRector, FunctionToStaticRector, etc.) + +--- + +### Steps 1–14: Follow the canonical conversion workflow (fallback) + +Read `.claude/skills/prompts/digest-to-rector-prompt.md` completely and execute steps 1–14 as written there. + +The canonical prompt covers: +- Step 1: Confirm input and extract class name, node types, refactor logic, code samples, issue number +- Step 2: Read the companion issue markdown +- Step 3: Fetch from Drupal.org if Step 2 was insufficient +- Step 4: Classify the rule (BC decision) — `AbstractRector` vs `AbstractDrupalCoreRector` +- Step 4b: Check for existing generic rectors BEFORE writing a custom class +- Step 5: Derive class name and file paths +- Step 6: Generate the rule class +- Step 7: Generate the fixture file +- Step 8: Generate the test class +- Step 9: Generate the test config +- Step 10: Write all files +- Step 11: Fix code style (`ddev composer fix-style`) +- Step 12: Run static analysis (`ddev composer phpstan`) +- Step 13: Run the test (`vendor/bin/phpunit tests/src/Drupal11/Rector/Deprecation/[ClassName]/`) +- Step 14: Done (no commit — leave that to the reviewer) + +**Do not skip or abbreviate any step.** The `.claude/skills/prompts/digest-to-rector-prompt.md` prompt is authoritative. + +--- + +### After Step 10: Quality Gate QG-A — Type Guard Audit + +For every `MethodCall`, `NullsafeMethodCall`, or `PropertyFetch` node the rector handles: + +**Choose the right guard for the node type:** +- Instance method/property on a variable → `isObjectType($node->var, new ObjectType('FQCN'))` (prefer the interface over the concrete class) +- Static call `ClassName::method()` → `isName($node->class, 'Fully\Qualified\ClassName')` using the FQCN directly — `isObjectType` is not needed here +- Global function `foo()` or class constant `ClassName::CONST` → no guard needed, SAFE + +1. Is the correct guard present? +2. If missing: + a. Find the owning interface/class in the Drupal core source (`repos/drupal-core`). If absent, run `bash .claude/scripts/setup-repos.sh` first. + ```bash + grep -rn "function \|property \$" repos/drupal-core/core --include="*.php" -l | head -5 + ``` + b. Check whether a stub exists: + ```bash + find stubs/ -name "*.php" | xargs grep -l "class \|interface " 2>/dev/null + ``` + c. If no stub: create a minimal stub at `stubs/Drupal/Some/Namespace/ClassName.php`: + ```php + getObjectClassNames()` + `str_ends_with()` fallback (see `ReplaceSessionManagerDeleteRector::isSessionManagerType()` for the reference pattern). Only add this when a real miss is confirmed; do not add it pre-emptively. + +**Global functions (FuncCall without a receiver) and class constants (ClassConstFetch) do NOT need `isObjectType()` guards — skip QG-A for these.** + +--- + +### After QG-A: Quality Gate QG-B — Version-Gating Tests (BC-wrapped rectors only) + +Apply only if the rector extends `AbstractDrupalCoreRector`. + +Replace the simple test class with the full `testAboveVersion` / `testBelowVersion` form that uses +`DrupalRectorSettings::setDrupalVersion()`: + +```php +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} +``` + +- `'99.99.99'` simulates a Drupal version well above any introduced version → BC wrapper fires. +- `'1.0.0'` simulates a version below every introduced version → rector skips, no change applied. + +Create `tests/src/Drupal11/Rector/Deprecation/[ClassName]/fixture-below-version/basic.php.inc`: +``` + +----- + +``` + +Only "transformable" fixtures (those that produce a change) need a `fixture-below-version/` +counterpart. No-change fixtures (`no_change_*.php.inc`) do not need one — they already show no +transformation. + +--- + +### After Step 14: Update the index (if it exists) + +```bash +if [ -f docs/rector-index.yml ]; then + php .claude/scripts/generate-rector-index.php +fi +``` + +This marks the newly implemented rule as `implemented` in the index. + +--- + +### After the index update: Run rector-qa + +Read `.claude/skills/rector-qa/SKILL.md` and execute all four passes for `[ClassName]`. + +Apply any fixes the QA reveals. Do not declare the implementation complete until rector-qa reports **Overall: PASS**. + +--- + +## Pre-flight Checklist + +Before declaring the implementation complete, verify all items from `.claude/skills/prompts/digest-to-rector-prompt.md`'s final checklist, plus: + +- [ ] QG-A: `isObjectType()` guard present for all MethodCall/PropertyFetch nodes (or explicitly not needed) +- [ ] QG-A: `no_change_unrelated.php.inc` fixture exists if a type guard was added +- [ ] QG-B: `testAboveVersion()` + `testBelowVersion()` (with `DrupalRectorSettings::setDrupalVersion`) and `fixture-below-version/basic.php.inc` present if BC-wrapped +- [ ] `vendor/bin/phpunit tests/src/Drupal11/Rector/Deprecation/[ClassName]/` passes +- [ ] `ddev composer phpstan` reports no new errors +- [ ] `ddev composer fix-style` produces no changes +- [ ] rector-qa reports **Overall: PASS** (all four passes green) + +## Quick Reference: Phase 1 (config-only) path + +If Step 4b determines a generic rector handles this rule, follow the "config-only" path in `.claude/skills/prompts/digest-to-rector-prompt.md` Step 4b instead of generating a custom class. No custom PHP class is written — only a config entry and fixture are added. diff --git a/.claude/skills/rector-live-test/SKILL.md b/.claude/skills/rector-live-test/SKILL.md new file mode 100644 index 000000000..fce351163 --- /dev/null +++ b/.claude/skills/rector-live-test/SKILL.md @@ -0,0 +1,225 @@ +--- +name: rector-live-test +description: Finds D11-compatible contrib modules that exercise a rector and runs it against them. Uses api.tresbien.tech JSON API as primary search tool, falls back to Drupal GitLab API. Pass rector class name or issue number as argument. +argument-hint: "" +allowed-tools: Read, Bash, Glob, WebFetch, WebSearch +--- + +# Rector Live Test + +Find real contrib modules that use the deprecated API a rector targets, then run the rector against them to verify it transforms real-world code correctly. + +## Input + +`$ARGUMENTS` — either: +- Rector class name: `ReplaceSessionManagerDeleteRector` +- Issue number: `3577376` + +## Steps + +### 1. Resolve the rector + +If given a class name, find the source file: +```bash +find src -name ".php" +``` + +If given an issue number, check `docs/rector-index.yml` (regenerate if needed) for the class name, then find the source file. + +Read the rector source to extract: +- The deprecated method/function/constant name (look in `isName()` calls, constants, or `FUNCTION_MAP`) +- The deprecated class/interface name (from `isObjectType()` guards) + +### 2. Search for contrib modules + +**Primary: `api.tresbien.tech` JSON API** + +Use `curl` + `jq` to query the JSON search API. The base URL is: + +``` +https://api.tresbien.tech/v1/search?q=&num= +``` + +**Always include `-r:drupal`** to exclude Drupal core from results (use `-r:drupal`, NOT `-r:core`). + +**Regex escaping:** The query is treated as a regex. Escape `(` as `\(` — an unescaped `(` causes a parse error and returns HTTP 418. + +Standard query construction: +- Method call: `-r:drupal ->methodName\(` +- Function call: `-r:drupal functionName\(` +- Class constant: `-r:drupal ClassName::CONSTANT_NAME` +- Property access: `-r:drupal ->propertyName` + +Additional filters to add as needed: +- `f:\.php$` — PHP files only (add `f:\.module$` if pattern may appear in `.module` files) +- `-f:test` — exclude test files +- `lang:php` — PHP language filter +- `case:yes` — force case-sensitive match + +Example — search for `_filter_autop(` in contrib PHP files: +```bash +curl -s "https://api.tresbien.tech/v1/search?q=-r%3Adrupal+_filter_autop%5C%28+-f%3Atest&num=20" \ + | jq -r '.Result.Files[] | "\(.Repository)\t\(.FileName)\t\(.Branches | join(","))"' +``` + +The response is JSON with `Result.Files[]` — each entry has: +- `.Repository` — module/project name (use this directly, no path parsing needed) +- `.FileName` — file path within the repo +- `.Branches[]` — which branch the match is on +- `.ChunkMatches[].Content` — base64-encoded matched line(s) + +To decode a matched line and see actual code context: +```bash +echo "" | base64 -d +``` + +**Never loop over individual repos.** If you need to search within a known set of repos, use regex alternation: `r:^(module1|module2|module3)$`. + +**Fallback: Drupal GitLab API blob search** + +If the API yields no results or is unavailable: + +```bash +QUERY="" +curl -s "https://git.drupalcode.org/search?group_id=2&scope=blobs&search=-path%3Acore+-path%3Avendor+-path%3Adocroot+-path%3Aweb+-path%3Aprofiles+-path%3Asites+$QUERY" \ + | grep -o 'data-project="[^"]*"' | sort -u | head -20 +``` + +### 3. Filter to D11-compatible modules + +Use the repo listing API to batch-check all found modules at once. The endpoint returns `RawConfig."drupal-core"` (branch-keyed compatibility strings) and `RawConfig."drupal-usage"` (install counts per branch): + +```bash +# Get D11-compatible repos and their install counts +MODULES='module1|module2|module3' # pipe-separated list from step 2 +curl -s "https://api.tresbien.tech/v1/search/repo" \ + | jq -r --arg mods "$MODULES" \ + '.List.Repos[] + | select(.Repository.Name | test($mods)) + | select(.Repository.RawConfig."drupal-core" // "" | test("\\^11")) + | [.Repository.Name, + .Repository.RawConfig."drupal-core", + .Repository.RawConfig."drupal-usage"] | @tsv' +``` + +The `drupal-core` field looks like `"1.x:^10 || ^11;2.x:^11"` — keep modules where any branch entry includes `^11`. + +The `drupal-usage` field looks like `"1.x:4521;2.x:312"` — **prefer modules with higher install counts** for better real-world test coverage. + +If no D11-compatible modules are found, report: +``` +No D11-compatible contrib modules found for . +Try manually: https://git.drupalcode.org/search?group_id=2&scope=blobs&search= +``` + +### 4. Run the rector + +**Check if the DDEV test project exists:** +```bash +ls ~/projects/drupal-rector-test/.ddev 2>/dev/null && echo "exists" || echo "not found" +``` + +**If not found**, run the one-time setup (takes ~10 minutes): +```bash +bash .claude/skills/rector-live-test/setup-rector-test.sh +``` +This creates a Drupal 11 site at `~/projects/drupal-rector-test` with a broad set of +contrib modules pre-installed. + +**If found**, check whether DDEV is running and start it only if needed: +```bash +cd ~/projects/drupal-rector-test +DDEV_STATUS=$(ddev status --json-output 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin).get('raw',{}).get('status','stopped'))" 2>/dev/null || echo "stopped") +[ "$DDEV_STATUS" = "running" ] || ddev start -y +``` + +**If a module found in step 2 is not pre-installed**, add it before running: +```bash +cd ~/projects/drupal-rector-test +ddev composer require drupal/ --no-interaction +``` + +**Resolve the FQCN** based on where the rector source lives: +- `src/Drupal11/…` → `DrupalRector\Drupal11\Rector\Deprecation\` +- `src/Drupal10/…` → `DrupalRector\Drupal10\Rector\Deprecation\` +- `src/Rector/…` → `DrupalRector\Rector\Deprecation\` + +**Write a minimal config file** — do NOT use `--only` (it loads the project's default rector.php +which may be broken) and do NOT omit `fileExtensions` (Rector only processes `.php` by default, +silently skipping `.module`, `.install`, etc.): + +```bash +cat > ~/projects/drupal-rector-test/rector-live-test.php << 'RECTOR_EOF' +; +use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; +use Rector\Config\RectorConfig; + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->fileExtensions(['php', 'module', 'theme', 'install', 'profile', 'inc']); + $rectorConfig->ruleWithConfiguration(::class, [ + new DrupalIntroducedVersionConfiguration(''), + ]); +}; +RECTOR_EOF +``` + +**Run rector** against the found modules: +```bash +cd ~/projects/drupal-rector-test +ddev exec -d /var/www/html \ + vendor/bin/rector process \ + web/modules/contrib/ web/modules/contrib/ \ + --config rector-live-test.php \ + --clear-cache 2>&1 +``` + +**Inspect the diff**, then reset and clean up: +```bash +git -C ~/projects/drupal-rector-test diff web/modules/contrib/ +git -C ~/projects/drupal-rector-test checkout -- web/modules/contrib/ +rm ~/projects/drupal-rector-test/rector-live-test.php +``` + +### 5. Report results + +For each tested module, report: +``` + file(s) changed + Transformations: +``` + +For every module with **zero changes**, do not just say "no match" — always show the actual +code and explain why. See step 6. + +### 6. Diagnose zero-match results + +For **every** module that produced no changes, you must: + +1. **Find the exact call site:** + ```bash + grep -n "" ~/projects/drupal-rector-test/web/modules/contrib// + ``` + +2. **Show the surrounding code** (±8 lines): + ```bash + sed -n ',p' ~/projects/drupal-rector-test/web/modules/contrib// + ``` + +3. **Diagnose** by reading the code and identifying the cause from the table below. + +4. **Report** the code snippet and diagnosis inline — do not skip this even if the cause seems obvious. + +**Common causes:** + +| Cause | How to spot it | Verdict | +|-------|---------------|---------| +| Untyped receiver | No `@var` annotation and no type-hinted parameter for the variable | Rector is correct to skip — would cause false positives on unrelated classes | +| Chained call, return type unresolvable | `$foo->something()->getOriginalClass()` where `something()` has no known return type | Rector correctly skips — add phpstan-drupal or a stub to fix | +| Broken `use` import | File imports a class from a module that isn't installed | PHPStan can't resolve the import, degrades type inference for the whole file | +| `.module` file silently skipped | File extension is `.module`, `.install`, etc. | Config is missing `fileExtensions()` — this should not happen if step 4 was followed | +| Module already updated | The call site no longer uses the deprecated API | Expected — the module has already migrated | +| Wrong rector class | The rector targets a different method/function | Verify the rector's `isName()` matches the actual call in the module | + diff --git a/.claude/skills/rector-live-test/setup-rector-test.sh b/.claude/skills/rector-live-test/setup-rector-test.sh new file mode 100755 index 000000000..5a2f5cd7d --- /dev/null +++ b/.claude/skills/rector-live-test/setup-rector-test.sh @@ -0,0 +1,478 @@ +#!/usr/bin/env bash +# Sets up a Drupal 11 DDEV project with contrib modules that exercise all new +# rectors, wires in the local drupal-rector branch, and runs rector so you can +# review the resulting diff. +# +# Usage: bash .claude/skills/rector-live-test/setup-rector-test.sh [project-name] +# Default project name: drupal-rector-test +# Default location: / (sibling of the rector repo) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RECTOR_REPO="$(cd "$SCRIPT_DIR/../../.." && pwd)" +RECTOR_BRANCH="$(git -C "$RECTOR_REPO" rev-parse --abbrev-ref HEAD)" + +TARGET_DIR="${RECTOR_TEST_DIR:-$HOME/projects/drupal-rector-test}" +PROJECT_NAME="$(basename "$TARGET_DIR")" + +echo "==> Project directory : $TARGET_DIR" +echo "==> drupal-rector repo : $RECTOR_REPO ($RECTOR_BRANCH)" +echo "" + +# Idempotency: skip full setup if the DDEV project already exists. +if [ -d "$TARGET_DIR/.ddev" ]; then + echo "==> Project already exists — checking DDEV status." + cd "$TARGET_DIR" + DDEV_STATUS=$(ddev status --json-output 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin).get('raw',{}).get('status','stopped'))" 2>/dev/null || echo "stopped") + if [ "$DDEV_STATUS" = "running" ]; then + echo "==> DDEV already running." + else + ddev start -y + fi + echo "" + echo " To run a single rector:" + echo " ddev exec -d /var/www/html vendor/bin/rector process web/modules/contrib/ \\" + echo " --only=\"DrupalRector\\\\Drupal11\\\\Rector\\\\Deprecation\\\\\" --no-cache" + exit 0 +fi + +# --------------------------------------------------------------------------- +# 1. Create project directory and configure DDEV +# --------------------------------------------------------------------------- +mkdir -p "$TARGET_DIR" +cd "$TARGET_DIR" + +echo "==> Configuring DDEV…" +ddev config \ + --project-type=drupal11 \ + --docroot=web \ + --project-name="$PROJECT_NAME" + +# Mount the local rector clone inside the DDEV container so the PATH +# composer repository works without pushing to GitHub first. +cat > .ddev/docker-compose.mounts.yaml << DOCKERCOMPOSE +services: + web: + volumes: + - "$RECTOR_REPO:/mnt/drupal-rector" +DOCKERCOMPOSE + +ddev start -y + +# --------------------------------------------------------------------------- +# 2. Scaffold Drupal 11 +# --------------------------------------------------------------------------- +echo "" +echo "==> Scaffolding Drupal 11 via composer create-project…" +ddev composer create-project "drupal/recommended-project:^11" . \ + --no-interaction \ + --stability dev + +# Pre-approve all composer plugins upfront so no interactive prompts appear +# during any subsequent require/update calls. +ddev composer config allow-plugins.tbachert/spi true + +ddev composer require drush/drush --no-interaction + +echo "" +echo "==> Installing Drupal site…" +ddev drush site:install --account-name=admin --account-pass=admin -y + +# --------------------------------------------------------------------------- +# 3. Wire in drupal-rector from the local clone +# --------------------------------------------------------------------------- +echo "" +echo "==> Wiring in drupal-rector (local clone via DDEV mount)…" + +# PATH repository — resolves to the mounted rector clone inside the container. +# Listed as the only source; no VCS fallback needed (and SSH keys aren't +# available inside DDEV containers anyway). +ddev composer config repositories.drupal-rector \ + '{"type":"path","url":"/mnt/drupal-rector","options":{"symlink":true}}' + +ddev composer require \ + "palantirnet/drupal-rector:dev-$RECTOR_BRANCH as 1.x-dev" \ + --no-interaction + +# --------------------------------------------------------------------------- +# 4. Require contrib modules (≥2 per rector where possible) +# --------------------------------------------------------------------------- +echo "" +echo "==> Requiring contrib modules…" + +# Batch 1 — multi-rector modules (high-value) +ddev composer require --no-update \ + "drupal/acquia_contenthub:*" \ + "drupal/searchstax:*" \ + "drupal/ai_agents:*" \ + "drupal/commerce_invoice:*" \ + "drupal/custom_field:*" \ + "drupal/role_expire:*" \ + "drupal/views_dependent_filters:*" \ + "drupal/search_api:*" \ + "drupal/schemadotorg:*" \ + "drupal/smart_migrate_cli:*" \ + "drupal/metatag:*" \ + "drupal/external_entity:*" \ + "drupal/ckeditor5_premium_features:1.3.*" \ + "drupal/reassign_user_content:*" + +# Batch 2 — single-rector gap-fillers +ddev composer require --no-update \ + "drupal/tara:*" \ + "drupal/vani:*" \ + "drupal/association:*" \ + "drupal/tome:*" \ + "drupal/cmrf_form_processor:*" \ + "drupal/intl_date:*" \ + "drupal/responsive_preview:*" \ + "drupal/tmgmt:*" \ + "drupal/config_track:*" \ + "drupal/site_guardian:*" \ + "drupal/smart_date:*" \ + "drupal/vcp4dates:*" \ + "drupal/gdpr:*" \ + "drupal/ai_eca:*" \ + "drupal/migmag:*" \ + "drupal/sparql_entity_storage:*" \ + "drupal/views_advanced_cache:*" \ + "drupal/smart_sql_idmap:*" \ + "drupal/forum:*" \ + "drupal/history:*" \ + "drupal/addanother:*" \ + "drupal/quicktabs:*" \ + "drupal/entity_usage:*" \ + "drupal/media_auto_publication:*" \ + "drupal/migrate_tools:6.1.*" \ + "drupal/stage_file_proxy:^3.1" \ + "drupal/workflow_buttons:^1" \ + "drupal/optional_end_date:*" \ + "drupal/scheduler_field:*" \ + "drupal/mailsystem:*" \ + "drupal/webform:*" \ + "drupal/recipe_installer_kit:*" + +echo "" +echo "==> Running composer update to resolve all requirements…" +ddev composer update --no-interaction --with-all-dependencies + +# Upgrade to 11.x-dev so the Drupal 11.4 version gate in AbstractDrupalCoreRector +# is satisfied, enabling rectors like RemoveTrustDataCallRector and +# ReplaceSessionManagerDeleteRector to produce diffs. +echo "" +echo "==> Upgrading Drupal core to 11.x-dev (satisfies 11.4 version gates)…" +ddev composer require \ + 'drupal/core-recommended:11.x-dev' \ + 'drupal/core-composer-scaffold:11.x-dev' \ + --with-all-dependencies --no-interaction + +# --------------------------------------------------------------------------- +# 5. Initialise git and commit the installed baseline +# (vendor/ and web/core/ excluded; web/modules/contrib/ tracked so +# rector changes show up in git diff) +# --------------------------------------------------------------------------- +echo "" +echo "==> Initialising git repository…" +if [ ! -d ".git" ]; then + git init +fi + +cat > .gitignore << 'GITIGNORE' +/vendor/ +/web/core/ +/web/sites/default/settings.php +/web/sites/default/services.yml +/web/sites/default/files/ +*.orig +GITIGNORE + +git add . +git commit -m "Install Drupal 11 + contrib modules for rector testing" + +# --------------------------------------------------------------------------- +# 6. Write rector.php +# --------------------------------------------------------------------------- +echo "" +echo "==> Writing rector.php…" +cat > rector.php << 'RECTOR' +withPaths([ + __DIR__ . '/web/modules/contrib', + ]) + ->withFileExtensions(['php', 'module', 'theme', 'install', 'profile', 'inc', 'engine']) + ->withSets([ + Drupal10SetList::DRUPAL_10, + Drupal11SetList::DRUPAL_11, + ]); +RECTOR + +git add rector.php + +# --------------------------------------------------------------------------- +# 7. Generate per-rector test script (run this inside ddev ssh) +# --------------------------------------------------------------------------- +mkdir -p scripts +cat > scripts/test-rectors.sh << 'TESTSCRIPT' +#!/usr/bin/env bash +# Per-rector test runner. Run this INSIDE the DDEV container: +# ddev ssh +# bash /var/www/html/scripts/test-rectors.sh [RectorName] +# +# With no argument: runs all rectors in sequence. +# With a rector class name: runs only that one rector. +# Output is tee'd to /var/www/html/rector-test.log + +set -euo pipefail +cd /var/www/html + +LOG=/var/www/html/rector-test.log +CONTRIB=web/modules/contrib +FILTER="${1:-}" + +run_test() { + local rector="$1" + shift + local mods=("$@") + + # Skip if a filter is set and doesn't match + if [ -n "$FILTER" ] && [ "$FILTER" != "$rector" ]; then + return + fi + + # Derive FQCN by checking which namespace the rector lives in + local fqcn + if [ -f "vendor/palantirnet/drupal-rector/src/Drupal11/Rector/Deprecation/${rector}.php" ]; then + fqcn="DrupalRector\\Drupal11\\Rector\\Deprecation\\${rector}" + else + fqcn="DrupalRector\\Drupal10\\Rector\\Deprecation\\${rector}" + fi + + # Resolve installed module directories + local paths=() + for mod in "${mods[@]}"; do + [ -d "$CONTRIB/$mod" ] && paths+=("$CONTRIB/$mod") + done + + echo "" | tee -a "$LOG" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | tee -a "$LOG" + printf " %s\n" "$rector" | tee -a "$LOG" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | tee -a "$LOG" + + if [ ${#paths[@]} -eq 0 ]; then + echo " SKIP — no modules installed for this rector" | tee -a "$LOG" + return + fi + + printf " Rector: %s\n" "$fqcn" | tee -a "$LOG" + printf " Modules: %s\n" "${paths[*]}" | tee -a "$LOG" + echo "" | tee -a "$LOG" + + rm -rf /tmp/rector_cached_files + timeout 60 vendor/bin/rector process "${paths[@]}" --only="$fqcn" --dry-run 2>&1 | tee -a "$LOG" || true + + echo "" | tee -a "$LOG" + git diff "${paths[@]}" | tee -a "$LOG" || true + + # Reset files so the next rector starts clean + git checkout -- "${paths[@]}" 2>/dev/null || true + +} + +echo "Rector test run — $(date)" | tee "$LOG" +echo "Log: $LOG" | tee -a "$LOG" + +# ── Drupal 11 rectors ────────────────────────────────────────────────────── +run_test ErrorCurrentErrorHandlerRector + # No contrib usage: Error::currentErrorHandler() deprecated D11.3.0; devel 5.x already cleaned up, no other hits + +run_test FileSystemBasenameToNativeRector \ + stage_file_proxy + +run_test LoadAllIncludesRector \ + config_track schemadotorg + +run_test MigrateSqlGetMigrationPluginManagerRector \ + feeds_migrate migmag smart_sql_idmap + +run_test NodeStorageDeprecatedMethodsRector \ + workflow_buttons + +run_test PluginBaseIsConfigurableRector \ + metatag search_api + +run_test RemoveAutomatedCronSubmitHandlerRector + # No contrib usage: automated_cron_settings_submit deprecated D11.4.0, no contrib calls found + +run_test RemoveCacheExpireOverrideRector \ + cmrf_form_processor vcp4dates + +run_test RemoveHandlerBaseDefineExtraOptionsRector \ + views_dependent_filters + +run_test RemoveLinkWidgetValidateTitleElementRector + # No contrib usage: LinkWidget::validateTitleElement() deprecated D11.4.0, no contrib calls found + +run_test RemoveModuleHandlerAddModuleCallsRector \ + config_track + +run_test RemoveModuleHandlerDeprecatedMethodsRector + # No contrib usage: writeCache()/getHookInfo() on ModuleHandlerInterface not called in any D11 contrib module found + +run_test RemoveRootFromConvertDbUrlRector \ + smart_migrate_cli sparql_entity_storage + +run_test RemoveSetUriCallbackRector + # No contrib usage: $entityType->setUriCallback() not called in any D11 contrib module found + +run_test RemoveStateCacheSettingRector + # No contrib usage: $settings['state_cache'] pattern not found in any D11 contrib module; likely already gone from codebase + +run_test RemoveTrustDataCallRector \ + views_dependent_filters + +run_test RemoveTwigNodeTransTagArgumentRector + # No contrib usage: TwigNodeTrans 6-arg constructor removed from core before D11 contrib caught up (version drift) + +run_test RemoveUpdaterPostInstallMethodsRector + # No contrib usage: targets classes extending Drupal\Core\Updater\Updater/Module/Theme + # that override postInstall() or postInstallTasks(). No D11 contrib module uses this pattern. + +run_test RemoveViewsRowCacheKeysRector \ + metatag views_advanced_cache + +run_test RenameStopProceduralHookScanRector + # No contrib usage: StopProceduralHookScan attribute deprecated D11.2.0, niche — no contrib usage found + +run_test ReplaceAlphadecimalToIntNullRector + # No contrib usage: alphadecimalToInt(null/'') — both values always returned 0, + # so this was only ever passed as a literal in custom code or tests. No D11 contrib + # module calls the function with a literal null or empty string. + +run_test ReplaceCommentManagerGetCountNewCommentsRector \ + forum + +run_test ReplaceCommentUriRector + # No contrib usage: comment_uri() deprecated D11.3.0; Social 13.x already cleaned up (issue #3432522), no other D11 hits + +run_test ReplaceDateTimeRangeConstantsRector \ + optional_end_date scheduler_field + +run_test ReplaceEditorLoadRector \ + ckeditor5_premium_features + # No contrib usage: editor_load() deprecated D11; not called in any D11 contrib module found + +run_test ReplaceEntityOriginalPropertyRector \ + entity_usage media_auto_publication + +run_test ReplaceEntityReferenceRecursiveLimitRector + # No contrib usage: RECURSIVE_RENDER_LIMIT removed from core before D11; + # all D11-compatible modules either define their own constant or hardcode 20. + # custom_field/external_entity define the constant, they don't reference core's. + +run_test ReplaceFieldgroupToFieldsetRector \ + webform + +run_test ReplaceFileGetContentHeadersRector \ + commerce_invoice tmgmt + +run_test ReplaceLocaleConfigBatchFunctionsRector + # No contrib usage: locale_config_batch_* deprecated D11.1.0; all GitLab hits were bundled core copies, not contrib code + +run_test ReplaceNodeAccessViewAllNodesRector + # No contrib usage: node_access_view_all_nodes() deprecated D11.3.0; no D11 contrib caller found (newly deprecated) + +run_test ReplaceNodeAddBodyFieldRector \ + tome ai_eca + +run_test ReplaceNodeModuleProceduralFunctionsRector \ + reassign_user_content addanother + +run_test ReplaceNodeSetPreviewModeRector \ + ai_agents responsive_preview + +run_test ReplacePdoFetchConstantsRector \ + acquia_contenthub gdpr + +run_test ReplaceRecipeRunnerInstallModuleRector \ + recipe_installer_kit + +run_test ReplaceSessionManagerDeleteRector \ + role_expire + +run_test ReplaceSessionWritesWithRequestSessionRector + # No contrib usage: direct $_SESSION writes not present in any D11-compatible contrib module found + +run_test ReplaceSystemPerformanceGzipKeyRector + # No contrib usage: advagg (only known caller) declares "core_version_requirement: ^9.3 || ^10" — not D11-compatible + +run_test ReplaceThemeGetSettingRector \ + tara vani + +run_test ReplaceUserSessionNamePropertyRector + # No contrib usage: $userSession->name property access not present in any D11 contrib module found + +run_test ReplaceViewsProceduralFunctionsRector \ + custom_field quicktabs + +run_test StatementPrefetchIteratorFetchColumnRector + # No contrib usage: fetchColumn() removed at D10.0; all D11-compatible modules already resolved this + +run_test StripMigrationDependenciesExpandArgRector \ + migrate_tools + +run_test UseEntityTypeHasIntegerIdRector \ + commerce_invoice association + +run_test ViewsPluginHandlerManagerRector \ + searchstax search_api metatag + +# ── Drupal 10 rectors ────────────────────────────────────────────────────── +run_test ReplaceModuleHandlerGetNameRector \ + mailsystem + +run_test ReplaceRebuildThemeDataRector \ + site_guardian + +run_test ReplaceRequestTimeConstantRector + # No contrib usage: REQUEST_TIME constant usage already migrated in all D11-compatible modules found + +run_test SystemTimeZonesRector \ + intl_date smart_date + +echo "" | tee -a "$LOG" +echo "Done. Full log: $LOG" | tee -a "$LOG" +TESTSCRIPT + +chmod +x scripts/test-rectors.sh +git add rector.php scripts/test-rectors.sh +git commit -m "Add rector.php and per-rector test script" + +# --------------------------------------------------------------------------- +# 8. Print manual run instructions +# --------------------------------------------------------------------------- +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Setup complete." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " cd $TARGET_DIR && ddev ssh" +echo "" +echo " # Run all rectors, each against its own modules only:" +echo " bash /var/www/html/scripts/test-rectors.sh" +echo "" +echo " # Run a single rector:" +echo " bash /var/www/html/scripts/test-rectors.sh LoadAllIncludesRector" +echo "" +echo " # Log is written to: $TARGET_DIR/rector-test.log" +echo "" +echo " Site: run 'ddev launch' | Admin: admin / admin" + diff --git a/.claude/skills/rector-qa/SKILL.md b/.claude/skills/rector-qa/SKILL.md new file mode 100644 index 000000000..fe09d4c90 --- /dev/null +++ b/.claude/skills/rector-qa/SKILL.md @@ -0,0 +1,322 @@ +--- +name: rector-qa +description: Comprehensive quality review of an existing Drupal rector. Runs four audit passes — type guards, fixture coverage, BC decision correctness, and @see URL accuracy — and produces a PASS/FAIL/WARN checklist. Use before merging a rector or when reviewing existing ones for regressions. Pass 'all' to walk the full branch type-guard checklist. +argument-hint: "" +allowed-tools: Read, Bash, Edit, Write, Glob +--- + +# Rector QA + +Comprehensive four-pass quality review for a drupal-rector implementation. + +## Input + +`$ARGUMENTS` — one of: +- Rector class name, e.g. `ReplaceSessionManagerDeleteRector` — runs all four passes on that rector. +- `all` — walks every Drupal-rector source file under `src/` and runs Pass 1 (type-guard audit) on each, fixing as it goes. + +## Finding the files + +```bash +find src -name ".php" +find tests/src -type d -name "" +``` + +Read the rector class, test class, all fixture files, and the test config. + +--- + +## Pass 1 — Type Guard Audit + +**Goal:** Every `MethodCall`, `NullsafeMethodCall`, or `PropertyFetch` node the rector handles must be guarded by an appropriate type check. + +| Pattern | What to look for | Risk if missing | +|---------|-----------------|-----------------| +| `->method()` on a variable | `isObjectType($node->var, new ObjectType('FQCN'))` | Any class with this method is transformed | +| `->property` on a variable | Same `isObjectType` on `$node->var` | Any class with this property is transformed | +| `$this->method()` inside a class body | `isObjectType($node->var, ...)` or `extends`-check on enclosing `Class_` | Any class with this method is transformed | +| `ClassName::method()` static call | `isName($node->class, 'Fully\Qualified\ClassName')` — use the FQCN directly; `isObjectType` is not needed | Low risk but use FQCN, not short name | +| Global function call `foo()` | None needed | SAFE — function names are global | +| Class declaration (`class Foo extends Bar`) | Check `extends` on the `Class_` node | EXEMPT — different pattern | + +**When `isObjectType` is not enough:** contrib code sometimes writes `@var Drupal\Core\Session\SessionManager` (no leading `\`). PHPStan resolves this relative to the current namespace and produces a mangled class name like `Vendor\Module\Drupal\Core\Session\SessionManager` that `isObjectType` won't match. Add a fallback using `getType($node)->getObjectClassNames()` with `str_ends_with()`: + +```php +private function isSessionManagerType(Node\Expr $node): bool +{ + if ($this->isObjectType($node, new ObjectType('Drupal\Core\Session\SessionManagerInterface'))) { + return true; + } + // Fallback for @var without leading \ (PHPStan mangles the class name) + foreach ($this->getType($node)->getObjectClassNames() as $className) { + if (str_ends_with($className, '\\Drupal\\Core\\Session\\SessionManagerInterface')) { + return true; + } + } + return false; +} +``` + +Only add this fallback when real-world testing shows `isObjectType` silently misses a valid case. See `ReplaceSessionManagerDeleteRector` for the reference implementation. + +**Steps:** + +1. Read the rector source. +2. Identify the node types from `getNodeTypes()`. +3. For each MethodCall/NullsafeMethodCall/PropertyFetch handler in `refactor()`: + - Is there an `isObjectType($node->var, new ObjectType('FQCN'))` guard? +4. Classify: + - **SAFE** — correct type guard present, or targets global functions/constants only + - **AT-RISK** — matches name without type guard + - **EXEMPT** — operates on class declaration, checks parent class + +**Output:** `Pass 1: [SAFE|AT-RISK|EXEMPT] — ` + +**If AT-RISK:** Apply the fix (see patterns below). + +### Finding the right class/interface + +Look up the FQCN in `repos/drupal-core` (run `bash .claude/scripts/setup-repos.sh` if absent): + +```bash +grep -rn "function \|property \$" repos/drupal-core/core --include="*.php" -l | head -5 +``` + +Prefer the *interface* over the concrete class — it catches all implementations. + +### Fix pattern + +```php +// Before — matches any ->save() call: +if (!$this->isName($node->name, 'save')) { + return null; +} + +// After — only matches Config::save(): +if (!$this->isName($node->name, 'save')) { + return null; +} +if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Config\Config'))) { + return null; +} +``` + +Always add the `isObjectType` check *after* the name check so the heavier type resolution only runs when the name already matches. + +### Stub pattern + +If the class is not already in `stubs/`, create a minimal stub: + +```php +/fixture -name "*.php.inc" + find tests/src/Drupal11/Rector/Deprecation//fixture-below-version -name "*.php.inc" 2>/dev/null + ``` + +2. Check for required coverage: + +| Fixture | Required when | Status | +|---------|--------------|--------| +| `fixture/basic.php.inc` | Always | ✅/❌ | +| `fixture/no_change_unrelated.php.inc` | Always | ✅/❌ | +| `fixture-below-version/basic.php.inc` | Rector extends `AbstractDrupalCoreRector` | ✅/❌ | +| Edge-case fixtures | Rector has conditional branches in `refactor()` | ✅/❌/N/A | + +3. For `no_change_unrelated.php.inc`: verify the before and after sections are **identical** (the rector must NOT change untyped code). + +4. For `fixture-below-version/basic.php.inc`: verify the before and after sections are **identical** (BC mode suppresses the transformation). + +**Output:** `Pass 2: [PASS|WARN] — ` + +**If WARN:** Propose missing fixture content and add it. + +--- + +## Pass 3 — BC Decision Audit + +**Goal:** The base class (`AbstractRector` vs `AbstractDrupalCoreRector`) must match the Step 4 classification from `.claude/skills/prompts/digest-to-rector-prompt.md`. + +**Steps:** + +1. Read the rector class docblock and header to find `extends AbstractRector` or `extends AbstractDrupalCoreRector`. + +2. Re-run the Step 4 decision: + - **Q1:** What node types does `getNodeTypes()` return? + - **Q2:** Is the transformation Expr → Expr? + - Old node is `Node\Expr` if: `FuncCall`, `MethodCall`, `StaticCall`, `NullsafeMethodCall`, `New_`, `Array_`, `ClassConstFetch`, `String_`, etc. + - New node (what `refactor()` or `refactorWithConfiguration()` returns) must also be `Node\Expr`. + - **Q3:** Was the deprecation introduced in Drupal >= 10.1.0? + - Check `introduced_version` in the test config or `DrupalIntroducedVersionConfiguration` usage. + - If unclear, read `repos/drupal-digests/issues/drupal-core/.md`. + - **Q4:** Does the replacement code depend on a new Drupal API? + - Read `refactor()`/`refactorWithConfiguration()` and identify what the returned node calls or + references (function name, class name, method name, constant). + - Ask: could this replacement code run unchanged on a Drupal version that predates the deprecation? + - **New Drupal API** (function/method/class introduced alongside the deprecation) → BC needed. + - **Pure PHP or version-agnostic** (native functions, inline closures, no new Drupal symbols) → BC NOT needed. + +3. Expected base class: + - Q2 = Expr → Expr AND Q3 = version >= 10.1.0 AND **Q4 = new Drupal API** → **`AbstractDrupalCoreRector`** + - Q4 = version-agnostic replacement (or Q2/Q3 not met) → **`AbstractRector`** + +4. Compare expected vs actual. + +**Output:** `Pass 3: [PASS|FAIL] — expected , found ` + +**If FAIL:** The base class is wrong. Propose the corrected class and note that `configure()`, `refactorWithConfiguration()`, and the test class will need updating. + +--- + +## Pass 4 — @see URL Audit + +**Goal:** The rector docblock must contain `@see` lines for **both** the Drupal.org issue node +and the change record node, so the class is findable regardless of which reference appears in +a given digest file or Drupal core deprecation notice. + +**Steps:** + +1. Extract all `@see` lines from the rector class: + ```bash + grep '@see' src/Drupal11/Rector/Deprecation/.php + ``` + +2. Determine the issue number and change record number: + + a. **Issue number** — last numeric group in the digest filename, e.g. + `remove-deprecated-foo-3505370.php` → issue `3505370`. + + b. **Change record number** — work through these sources in order, stopping when found: + + **Source 1 — Issue markdown (fastest):** + ```bash + cat repos/drupal-digests/issues/drupal-core/.md + ``` + Scan for a `drupal.org/node/` link in the "Upgrade path", "Change record", or "Technical + details" sections. A link like `[#3567879](https://www.drupal.org/node/3567879)` or + `https://www.drupal.org/node/3567879` in those sections is the change record number. + Also check the frontmatter for `change_record_url` or similar fields. + + **Source 2 — Drupal.org issue page (reliable):** + Fetch `https://www.drupal.org/node/` and look for a + "Change records for this issue" section or a "Related change records" block. + Those links point directly to the change record node. + + **Source 3 — Drupal core deprecation annotation (fallback):** + Run `bash .claude/scripts/setup-repos.sh` if `repos/drupal-core` is absent, then: + ```bash + grep -rn "@deprecated\|trigger_error" repos/drupal-core/core --include="*.php" \ + | grep "\|" | head -10 + ``` + The `@see` URL inside the `@deprecated` docblock or `trigger_error` message usually + points to the **change record**. Verify the node number differs from the issue number + before treating it as a CR. + + c. If the issue number and change record number are the **same** (rare), one `@see` suffices. + +3. Verify the rector has **both** `@see` lines: + - `@see https://www.drupal.org/node/` + - `@see https://www.drupal.org/node/` + +4. Flag: + - **PASS** — both `@see` lines present (or issue == CR, so one is correct) + - **WARN** — one `@see` present but the other is missing; add the missing line + - **FAIL** — `@see` points to an entirely unrelated node + +**If WARN/FAIL:** Add or correct the `@see` line(s) in the rector docblock. Both should appear +consecutively, issue first: + +```php + * @see https://www.drupal.org/node/ + * @see https://www.drupal.org/node/ +``` + +**Output:** `Pass 4: [PASS|WARN|FAIL] — issue: CR:` + +--- + +## Pass 5 — Registration Audit + +**Goal:** The rector must be wired into a `config/drupal-11/drupal-11.N-deprecations.php` file so it actually runs when users invoke drupal-rector. + +**Steps:** + +1. Check if the class is referenced in any config file: + ```bash + grep -rq "" config/ && echo "REGISTERED" || echo "FAIL — not registered" + ``` + +2. If unregistered, identify the correct config file from the deprecation version in the rector docblock (e.g. `drupal:11.2.0` → `config/drupal-11/drupal-11.2-deprecations.php`). + +3. Determine the entry type: + - Extends `AbstractDrupalCoreRector` → `$rectorConfig->ruleWithConfiguration(::class, [new DrupalIntroducedVersionConfiguration('11.N.0')])` + - Extends `AbstractRector` → `$rectorConfig->rule(::class)` + +**Output:** `Pass 5: [PASS|FAIL] — ` + +**If FAIL:** Add the `use` statement and `rule`/`ruleWithConfiguration` entry to the correct config file. + +--- + +## Final Summary + +After all five passes, produce a summary checklist: + +``` +=== QA Summary: === + +Pass 1 — Type Guard: [SAFE|AT-RISK|EXEMPT] +Pass 2 — Fixtures: [PASS|WARN] — +Pass 3 — BC Decision: [PASS|FAIL] — +Pass 4 — @see URL: [PASS|WARN|FAIL] — issue: CR: +Pass 5 — Registration: [PASS|FAIL] — + +Overall: [PASS — ready to merge | NEEDS FIXES — see above] +``` + +If any pass shows AT-RISK or FAIL, do not declare the rector ready to merge. Apply the proposed fixes and re-run the affected passes. + +--- + +## Running on a "known good" rector + +To verify the skill works correctly, run it on `ReplaceSessionManagerDeleteRector` — all five passes should be PASS/SAFE. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6ed4397e6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,16 @@ +/.claude export-ignore +/.github export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.dist.php export-ignore +/behat.yml export-ignore +/phpstan.neon export-ignore +/phpstan-baseline.neon export-ignore +/phpunit.xml export-ignore +/tests export-ignore +/fixtures export-ignore +/scripts export-ignore +/docs export-ignore +/rector.php export-ignore +/README-automated-testing.md export-ignore diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index 08d499ac1..0c5d340e1 100644 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.3 coverage: none # disable xdebug, pcov tools: composer:v2 extensions: zip diff --git a/.github/workflows/functional_test__rector_examples.yml b/.github/workflows/functional_test__rector_examples.yml deleted file mode 100644 index 83cdf522a..000000000 --- a/.github/workflows/functional_test__rector_examples.yml +++ /dev/null @@ -1,73 +0,0 @@ - -name: functional_test__rector_examples - -# This test will run on every pull request, and on every commit on any branch -on: - push: - branches: - - main - schedule: - # Run tests every week (to check for rector changes) - - cron: '0 0 * * 0' - pull_request: - types: [opened, synchronize, reopened, closed] - -jobs: - run_functional_test: - name: Functional | PHP ${{ matrix.php-version }} | Drupal ${{ matrix.drupal }}" - strategy: - fail-fast: false - matrix: - include: - - php-version: "8.2" - drupal: "^10.0" - fixture: "d10" - - php-version: "8.4" - drupal: "^10.0" - fixture: "d10" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: shivammathur/setup-php@v2 - with: - php-version: "${{ matrix.php-version }}" - coverage: none - tools: composer:v2 - extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql, gd - - name: Setup Drupal - uses: bluehorndigital/setup-drupal@v1.2.0 - with: - version: '${{ matrix.drupal }}' - path: ~/drupal - - name: Install Drupal Rector - run: | - cd ~/drupal - composer require palantirnet/drupal-rector:@dev --no-progress - - name: Install Drupal Rector Config - run: | - cd ~/drupal - cp vendor/palantirnet/drupal-rector/rector.php . - - name: Prepare rector_examples folder in the drupal modules directory - run: | - cd ~/drupal - mkdir -p web/modules/custom - cp -R vendor/palantirnet/drupal-rector/fixtures/${{ matrix.fixture }}/rector_examples web/modules/custom - # dry-run is expected to return exit code 2 if there are changes, which we are expecting to happen, here. - # an error code of 1 represents other errors. - # @see \Rector\Core\Console\ExitCode::CHANGED_CODE - - name: Run rector against Drupal (dry-run) - run: | - cd ~/drupal - vendor/bin/rector process web/modules/custom/rector_examples --dry-run --debug || if (($? == 2)); then true; else false; fi - - name: Run rector against Drupal - run: | - cd ~/drupal - vendor/bin/rector process web/modules/custom/rector_examples --debug - # diff options: - # -r: recursive - # -u: show the joined context, like git diff - # -b: ignore whitespace - # -B: ignore lines that are only whitespace - - name: Check that the updated examples match expectations - run: | - diff --color -rubB fixtures/${{ matrix.fixture }}/rector_examples_updated ~/drupal/web/modules/custom/rector_examples diff --git a/.github/workflows/functional_test__single_rectors.yml b/.github/workflows/functional_test__single_rectors.yml index 549316913..f3d8d9dec 100644 --- a/.github/workflows/functional_test__single_rectors.yml +++ b/.github/workflows/functional_test__single_rectors.yml @@ -36,7 +36,7 @@ jobs: run: | cd ~/drupal mkdir -p web/modules/custom - cp -R vendor/palantirnet/drupal-rector/tests/functional/hookconvertrector/fixture/hookconvertrector web/modules/custom/hookconvertrector + cp -R ${GITHUB_WORKSPACE}/tests/functional/hookconvertrector/fixture/hookconvertrector web/modules/custom/hookconvertrector # dry-run is expected to return exit code 2 if there are changes, which we are expecting to happen, here. # an error code of 1 represents other errors. # @see \Rector\Core\Console\ExitCode::CHANGED_CODE @@ -46,7 +46,7 @@ jobs: for d in web/modules/custom/*; do if [ -d "$d" ]; then echo "Processing $d" - cp vendor/palantirnet/drupal-rector/tests/functional/$(basename ${d})/rector.php . + cp ${GITHUB_WORKSPACE}/tests/functional/$(basename ${d})/rector.php . vendor/bin/rector process $d --dry-run --debug || if (($? == 2)); then true; else exit 1; fi fi done @@ -56,7 +56,7 @@ jobs: for d in web/modules/custom/*; do if [ -d "$d" ]; then echo "Processing $d" - cp vendor/palantirnet/drupal-rector/tests/functional/$(basename ${d})/rector.php . + cp ${GITHUB_WORKSPACE}/tests/functional/$(basename ${d})/rector.php . vendor/bin/rector process $d --debug fi done @@ -71,6 +71,6 @@ jobs: cd ~/drupal for d in web/modules/custom/*; do if [ -d "$d" ]; then - diff --color -rubB "$d" "vendor/palantirnet/drupal-rector/tests/functional/$(basename ${d})/fixture/$(basename ${d})_updated" + diff --color -rubB "$d" "${GITHUB_WORKSPACE}/tests/functional/$(basename ${d})/fixture/$(basename ${d})_updated" fi done diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 7621b1aee..f4b1a7074 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -19,24 +19,17 @@ jobs: strategy: matrix: include: - - php-version: "8.2" - rector: "^1" - phpstan-config: phpstan-1.neon - - php-version: "8.2" + - php-version: "8.3" rector: "^2" phpstan-config: phpstan.neon steps: - uses: actions/checkout@v3 - uses: shivammathur/setup-php@v2 with: - php-version: 8.2 + php-version: 8.3 coverage: none # disable xdebug, pcov tools: composer:v2 extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql, bcmath, gd, exif, iconv - # Uncomment to enable SSH access to Github Actions - https://github.com/marketplace/actions/debugging-with-tmate#getting-started - # - name: Debugging with tmate - # uses: mxschmitt/action-tmate@v2 - # END: SHARED SETUP - run: composer require rector/rector:${{ matrix.rector }} --dev - run: vendor/bin/phpstan analyse -c ${{ matrix.phpstan-config }} diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 4246e0ecc..5c63a15d9 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -18,9 +18,7 @@ jobs: strategy: matrix: include: - - php-version: "8.2" - rector: "^1" - - php-version: "8.2" + - php-version: "8.3" rector: "^2" steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index d78e97a5d..d2680e282 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ !/.gitattributes !/.editorconfig !.gitkeep +!/.claude +!/.claude/skills +!/.claude/skills/** # Other ignored paths and files. /vendor @@ -15,3 +18,8 @@ !/features/tmp/.placeholder /.phpunit.cache +.worktrees/ +rector-decision.log +docs/rector-index.yml +docs/plans/ +/repos/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 64f7498a4..60a4f6577 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -5,6 +5,7 @@ __DIR__.'/src', __DIR__.'/tests', __DIR__.'/config/drupal-*', + __DIR__.'/scripts', ]) ->exclude([ 'functional/hookconvertrector/fixture', diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..954b508e2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1062 @@ +# Changelog + +All notable changes to **drupal-rector** are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Historical entries (≤ 0.21.2) are reproduced from the +[GitHub Releases page](https://github.com/palantirnet/drupal-rector/releases) as +they were originally published; their format and level of detail varies +release-by-release. + +## [Unreleased] + +### Changed + +This will be the first beta of the 1.0 line. Adds full Drupal 11 deprecation coverage +(versions 11.0 through 11.4), a new container-managed settings service that gives users +explicit control over backward-compatibility wrapping, a documented set of Claude Code +skills for building further rectors, and drops support for Rector 1. + +Real-world validated end-to-end: + +- Applied to [Drupal Canvas](https://www.drupal.org/project/canvas) on the + `canvas-3589155` branch — the full Canvas test suite passed + ([pipeline 825601](https://git.drupalcode.org/issue/canvas-3589155/-/pipelines/825601)). +- Run across **the whole of Drupal contrib** via + [project_analysis](https://git.drupalcode.org/project/project_analysis) — code + branch [`master-next`](https://git.drupalcode.org/project/project_analysis/-/tree/master-next?ref_type=heads), + generated patches under + [`results-next`](https://git.drupalcode.org/project/project_analysis/-/tree/results-next?ref_type=heads). + Spot-checking those patches surfaced and drove the late-PR fixes (PDO receiver + guard, BC-wrapper mutation, `ConstantToClassConstantRector` generalisation, + 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\Expr` → `Node\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](https://github.com/dbuytaert/drupal-digests) + rules into shipped rector classes with tests. Includes the 700-line canonical + conversion prompt and three reusable recipes. + +### Added + +#### Infrastructure +- **`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](https://www.drupal.org/node/3217904) | +| `RemoveStateCacheSettingRector` | Remove `$settings['state_cache']` — state caching is permanently enabled | [node/2575105](https://www.drupal.org/node/2575105) | +| `ReplaceRequestTimeConstantRector` | `REQUEST_TIME` → `\Drupal::time()->getRequestTime()` | [node/3395986](https://www.drupal.org/node/3395986) | +| `StripMigrationDependenciesExpandArgRector` | `getMigrationDependencies($expand)` → drop the boolean arg | [node/3442785](https://www.drupal.org/node/3442785) | +| `MigrateSqlGetMigrationPluginManagerRector` | `Sql::getMigrationPluginManager()` → `$this->migrationPluginManager` property | [node/3282894](https://www.drupal.org/node/3282894) | + +##### Drupal 11.1 + +| Rule | What it does | Change record | +|---|---|---| +| `PluginBaseIsConfigurableRector` | `PluginBase::isConfigurable()` → `instanceof ConfigurableInterface` check | [node/2946122](https://www.drupal.org/node/2946122) | +| `RenameClassRector` | `AliasWhitelist[Interface]` → `AliasPrefixList[Interface]`; `MatchingRouteNotFoundException` → `ResourceNotFoundException` | [node/3467559](https://www.drupal.org/node/3467559) | +| `MethodToMethodWithCheckRector` | `AliasManager::pathAliasWhitelistRebuild()` → `pathAliasPrefixListRebuild()` | [node/3467559](https://www.drupal.org/node/3467559) | +| `RemoveModuleHandlerDeprecatedMethodsRector` | `ModuleHandler::writeCache()` removed; `getHookInfo()` returns `[]` | [node/3368812](https://www.drupal.org/node/3368812) | +| `ReplaceLocaleConfigBatchFunctionsRector` | Rename `locale_config_batch_set_config_langcodes()` and `locale_config_batch_refresh_name()` | [node/3575254](https://www.drupal.org/node/3575254) | +| `RemoveUpdaterPostInstallMethodsRector` | Remove `Updater::postInstall*()` methods (no replacement) | [node/3461934](https://www.drupal.org/node/3461934) | +| `BlockContentTestBaseStringToArrayRector` | `BlockContentTestBase::createBlockContentType($string)` → array arg | [node/3473739](https://www.drupal.org/node/3473739) | +| `MovePointerToMouseOverRector` | `movePointerTo($css)` → `getSession()->getDriver()->mouseOver($xpath)` | [node/3460567](https://www.drupal.org/node/3460567) | +| `ReplaceAddCachedDiscoveryMethodCallRector` | `addMethodCall('addCachedDiscovery', …)` on `plugin.cache_clearer` removed | [node/3442229](https://www.drupal.org/node/3442229) | +| `FunctionToStaticRector` | `drupal_common_theme()` → `ThemeCommonElements::commonElements()` | [node/3488176](https://www.drupal.org/node/3488176) | + +##### Drupal 11.2 + +| Rule | What it does | Change record | +|---|---|---| +| `StatementPrefetchIteratorFetchColumnRector` | `StatementPrefetchIterator::fetchColumn()` → `fetchField()` | [node/3490312](https://www.drupal.org/node/3490312) | +| `MethodToMethodWithCheckRector` | `CacheBackendInterface::invalidateAll()` → `deleteAll()` | [node/3500622](https://www.drupal.org/node/3500622) | +| `FunctionToServiceRector` | `template_preprocess_{html,page,container,links,time,datetime_form,datetime_wrapper}()` → `ThemePreprocess` / `DatePreprocess` service methods | [node/3504125](https://www.drupal.org/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](https://www.drupal.org/node/3501136) | +| `RemoveModuleHandlerAddModuleCallsRector` | Remove `ModuleHandler::addModule()` / `addProfile()` (no-ops) | [node/3550193](https://www.drupal.org/node/3550193) | +| `RemoveHandlerBaseDefineExtraOptionsRector` | Remove `HandlerBase::defineExtraOptions()` overrides (dead code) | [node/3486781](https://www.drupal.org/node/3486781) | +| `FunctionToStaticRector` | `drupal_requirements_severity()` → `RequirementSeverity::maxSeverityFromRequirements()` | [node/3497049](https://www.drupal.org/node/3497049) | +| `FunctionToServiceRector` | `views_field_default_views_data()` and `_views_field_get_entity_type_storage()` → services | [node/3489415](https://www.drupal.org/node/3489415) | +| `ConstantToClassConstantRector` | Global `REQUIREMENT_INFO/OK/WARNING/ERROR` → `RequirementSeverity::*` | [node/3575841](https://www.drupal.org/node/3575841) | +| `RemoveTwigNodeTransTagArgumentRector` | Drop the 6th `$tag` argument from `TwigNodeTrans` constructor calls | [node/3474692](https://www.drupal.org/node/3474692) | +| `ReplaceAlphadecimalToIntNullRector` | `Number::alphadecimalToInt(null\|'')` → `0` | [node/3494472](https://www.drupal.org/node/3494472) | +| `ReplaceFieldgroupToFieldsetRector` | Render-array `'#type' => 'fieldgroup'` → `'fieldset'` | [node/3515272](https://www.drupal.org/node/3515272) | +| `ReplacePdoFetchConstantsRector` | `\PDO::FETCH_*` → `\Drupal\Core\Database\Statement\FetchAs::*` (StatementInterface receiver only) | [node/3488338](https://www.drupal.org/node/3488338) | +| `ReplaceDateTimeRangeConstantsRector` | `DateTimeRangeConstantsInterface::{BOTH,START_DATE,END_DATE}` → enum `->value`; `datetime_type_field_views_data_helper()` → service | [node/3574901](https://www.drupal.org/node/3574901) | +| `FunctionToFirstArgMethodRector` | `file_get_content_headers($file)` → `$file->getDownloadHeaders()` | [node/3494172](https://www.drupal.org/node/3494172) | +| `ReplaceSessionWritesWithRequestSessionRector` | `$_SESSION['key'] = $value` → `\Drupal::request()->getSession()->set(...)` | [node/3518914](https://www.drupal.org/node/3518914) | +| `ReplaceEditorLoadRector` | `editor_load($format_id)` → `entityTypeManager()->getStorage('editor')->load($format_id)` | [node/3509245](https://www.drupal.org/node/3509245) | +| `ReplaceEntityOriginalPropertyRector` | `$entity->original` magic property → `$entity->getOriginal()` / `setOriginal()`; nullsafe-aware | [node/3571065](https://www.drupal.org/node/3571065) | +| `RenameStopProceduralHookScanRector` | `#[StopProceduralHookScan]` → `#[ProceduralHookScanStop]` | [node/3495943](https://www.drupal.org/node/3495943) | +| `RenameClassRector` | `Drupal\Core\Entity\Query\Sql\pgsql\*` → `Drupal\pgsql\Entity\Query\*`; `jsonapi` ResourceResponseValidator move | [node/3488572](https://www.drupal.org/node/3488572) | +| `RemoveCacheTagChecksumAssertionsRector` | Remove `CacheTagChecksumCount` / `CacheTagIsValidCount` performance-trait assertions | [node/3511149](https://www.drupal.org/node/3511149) | +| `RemoveRootFromCreateConnectionOptionsFromUrlRector` | `Connection::createConnectionOptionsFromUrl()` — drop `$root` parameter | [node/3511287](https://www.drupal.org/node/3511287) | +| `ClassConstantToClassConstantRector` | `SystemManager::REQUIREMENT_*` → `RequirementSeverity::*` | [node/3410939](https://www.drupal.org/node/3410939) | + +##### Drupal 11.3 + +| Rule | What it does | Change record | +|---|---|---| +| `ReplaceCommentManagerGetCountNewCommentsRector` | `CommentManager::getCountNewComments()` → `HistoryManager::getCountNewComments()` | [node/3551729](https://www.drupal.org/node/3551729) | +| `LoadAllIncludesRector` | `ModuleHandler::loadAllIncludes()` → explicit `foreach (getModuleList() …) loadInclude()` | [node/3536432](https://www.drupal.org/node/3536432) | +| `NodeStorageDeprecatedMethodsRector` | `NodeStorage::revisionIds()` / `userRevisionIds()` → entity query chains; `countDefaultLanguageRevisions()` removed | [node/3519187](https://www.drupal.org/node/3519187) | +| `FunctionToServiceRector` | `node_mass_update()` → `NodeBulkUpdate::process()` | [node/3533083](https://www.drupal.org/node/3533083) | +| `FunctionToServiceRector` | `template_preprocess_layout()` → `LayoutDiscoveryThemeHooks::preprocessLayout()` | [node/3504125](https://www.drupal.org/node/3504125) | +| `ReplaceTwigExtensionRector` | `twig_extension()` → `'.html.twig'` string literal | [node/1685492](https://www.drupal.org/node/1685492) | +| `ReplaceNodeModuleProceduralFunctionsRector` | `node_type_get_names()` → `entity_type.bundle.info` service; `node_get_type_label($node)` → `$node->getBundleEntity()->label()` | [node/3516778](https://www.drupal.org/node/3516778) | +| `FunctionCallRemovalRector` | Remove `block_content_add_body_field()` calls (replaced by config) | [node/3535528](https://www.drupal.org/node/3535528) | +| `FunctionToFirstArgMethodRector` | `comment_uri($comment)` → `$comment->permalink()` | [node/2010202](https://www.drupal.org/node/2010202) | +| `ReplaceNodeAccessViewAllNodesRector` | `node_access_view_all_nodes()` → entity-type-manager access-control chain | [node/3038909](https://www.drupal.org/node/3038909) | +| `FunctionToServiceRector` | `responsive_image_*()` helpers → `responsive_image.builder` service | [node/3548329](https://www.drupal.org/node/3548329) | +| `ReplaceNodeAddBodyFieldRector` | `node_add_body_field()` → `$this->createBodyField()` (BodyFieldCreationTrait) | [node/3516778](https://www.drupal.org/node/3516778) | +| `ReplaceUserSessionNamePropertyRector` | `$userSession->name` read → `getAccountName()` | [node/3513877](https://www.drupal.org/node/3513877) | +| `FunctionToStaticRector` | `file_system_settings_submit()` → `FileSystemSettingsForm::submit()` | [node/3534091](https://www.drupal.org/node/3534091) | +| `FileManagedFileSubmitRector` | `'file_managed_file_submit'` string callback → `[ManagedFile::class, 'submit']` array callable | [node/3534091](https://www.drupal.org/node/3534091) | +| `ConstantToClassConstantRector` | Global `JSONAPI_FILTER_AMONG_*` → `\Drupal\jsonapi\JsonApiFilter::AMONG_*` | [node/3495601](https://www.drupal.org/node/3495601) | +| `ReplaceNodeSetPreviewModeRector` | `DRUPAL_DISABLED/OPTIONAL/REQUIRED` (and integers) in `setPreviewMode()` → `NodePreviewMode` enum | [node/3538666](https://www.drupal.org/node/3538666) | +| `FileSystemBasenameToNativeRector` | `FileSystem::basename()` → PHP native `basename()` | [node/3530869](https://www.drupal.org/node/3530869) | +| `ErrorCurrentErrorHandlerRector` | `Error::currentErrorHandler()` → PHP `get_error_handler()` | [node/3529500](https://www.drupal.org/node/3529500) | +| `ReplaceThemeGetSettingRector` | `theme_get_setting()` → `ThemeSettingsProvider` service; `_system_default_theme_features()` → `ThemeSettingsProvider::DEFAULT_THEME_FEATURES` | [node/3573896](https://www.drupal.org/node/3573896) | +| `RemoveRootFromConvertDbUrlRector` | `Database::convertDbUrlToConnectionInfo($url, $root, …)` — drop the `$root` arg | [node/3511287](https://www.drupal.org/node/3511287) | +| `RenameClassRector` | `workspaces.association` service / `WorkspaceAssociation*` → `workspaces.tracker` / `WorkspaceTracker*` | [node/3551450](https://www.drupal.org/node/3551450) | + +##### Drupal 11.4 + +| Rule | What it does | Change record | +|---|---|---| +| `ViewsPluginHandlerManagerRector` | `Views::pluginManager()` / `handlerManager()` → `plugin.manager.views.*` services | [node/3566982](https://www.drupal.org/node/3566982) | +| `FunctionToServiceRector` | `node_access_grants()` → `NodeGrantsHelper::nodeAccessGrants()` | [node/3578055](https://www.drupal.org/node/3578055) | +| `NodeAccessRebuildFunctionsRector` | `node_access_rebuild()` / `node_access_needs_rebuild()` → `NodeAccessRebuild` service | [node/3534610](https://www.drupal.org/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](https://www.drupal.org/node/3035368) | +| `MediaFilterFormatEditFormValidateRector` | `media_filter_format_edit_form_validate()` → `MediaHooks::formatEditFormValidate()` | [node/3566774](https://www.drupal.org/node/3566774) | +| `DeprecatedFilterFunctionsRector` | `_filter_autop()` / `_filter_html_escape()` / `_filter_html_image_secure_process()` → `plugin.manager.filter` createInstance chain | [node/3566774](https://www.drupal.org/node/3566774) | +| `ReplaceSessionManagerDeleteRector` | `SessionManager::delete($uid)` → `UserSessionRepositoryInterface::deleteAll($uid)` | [node/3570851](https://www.drupal.org/node/3570851) | +| `ClassConstantToClassConstantRector` | `CommentItemInterface::{FORM_BELOW,FORM_SEPARATE_PAGE,HIDDEN,CLOSED,OPEN}` and `CommentInterface::ANONYMOUS_*` → `FormLocation` / `CommentingStatus` / `AnonymousContact` enums | [node/3550054](https://www.drupal.org/node/3550054) | +| `FunctionToServiceRector` | `language_configuration_element_submit()` → `LanguageConfiguration::submit()` | [node/3574727](https://www.drupal.org/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](https://www.drupal.org/node/3566774) | +| `RemoveSetUriCallbackRector` | Remove `EntityTypeInterface::setUriCallback()` calls — use `link_templates` instead | [node/3575062](https://www.drupal.org/node/3575062) | +| `ReplaceRecipeRunnerInstallModuleRector` | `RecipeRunner::installModule($module)` → `installModules([$module])` | [node/3579527](https://www.drupal.org/node/3579527) | +| `ReplaceSystemPerformanceGzipKeyRector` | `system.performance` `css.gzip` / `js.gzip` config keys → `css.compress` / `js.compress` | [node/3526344](https://www.drupal.org/node/3526344) | +| `RemoveViewsRowCacheKeysRector` | Remove `CachePluginBase::getRowCacheKeys()` / `getRowId()` overrides | [node/3564958](https://www.drupal.org/node/3564958) | +| `RemoveCacheExpireOverrideRector` | Remove `CachePluginBase::cacheExpire()` subclass overrides | [node/3576855](https://www.drupal.org/node/3576855) | +| `RemoveTrustDataCallRector` | Strip `->trustData()` from `Config` fluent chains | [node/3348180](https://www.drupal.org/node/3348180) | +| `RemoveConfigSaveTrustedDataArgRector` | `Config::save($has_trusted_data)` — drop the boolean arg | [node/3348180](https://www.drupal.org/node/3348180) | +| `RemoveLinkWidgetValidateTitleElementRector` | Remove `LinkWidget::validateTitleElement()` — now handled by `LinkTitleRequiredConstraint` | [node/3554139](https://www.drupal.org/node/3554139) | +| `RemoveAutomatedCronSubmitHandlerRector` | Drop `'automated_cron_settings_submit'` from `$form['#submit']` — `#config_target` now handles config save | [node/3566774](https://www.drupal.org/node/3566774) | +| `ReplaceViewsProceduralFunctionsRector` | `views_view_is_{enabled,disabled}()`, `views_{enable,disable}_view()`, `views_get_view*()` → entity-storage / `Views::*` static calls | [node/3572594](https://www.drupal.org/node/3572594) | +| `GetOriginalClassToGetDecoratedClassesRector` | `EntityTypeInterface::getOriginalClass()` → `getDecoratedClasses()` array access | [node/3557464](https://www.drupal.org/node/3557464) | +| `UseEntityTypeHasIntegerIdRector` | `getEntityTypeIdKeyType() === 'integer'`, `entityTypeSupportsComments()`, `hasIntegerId($entityType)` → `EntityTypeInterface::hasIntegerId()` | [node/3566814](https://www.drupal.org/node/3566814) | +| `FunctionToServiceRector` | `editor_filter_xss()` → `EditorXssFilterInterface` service | [node/3568144](https://www.drupal.org/node/3568144) | +| `FunctionToStaticRector` | `_media_library_configure_form_display()` / `_media_library_configure_view_display()` → static helpers | [node/3566774](https://www.drupal.org/node/3566774) | +| `FunctionToStaticRector` | `language_configuration_element_submit()` static-call form → `LanguageConfiguration::submit()` | [node/3574727](https://www.drupal.org/node/3574727) | +| `FunctionToServiceRector` | `contextual_links_to_id()` / `contextual_id_to_links()` → `ContextualLinksSerializer` | [node/3568087](https://www.drupal.org/node/3568087) | +| `FunctionToServiceRector` | `views_add_contextual_links()` → `views.contextual_links` service | [node/3382344](https://www.drupal.org/node/3382344) | +| `ConstantToClassConstantRector` | `IMAGE_DERIVATIVE_TOKEN` → `ImageStyleInterface::TOKEN` | [node/3567619](https://www.drupal.org/node/3567619) | +| `ReplaceEntityReferenceRecursiveLimitRector` | `EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT` → literal `20` | [node/2940605](https://www.drupal.org/node/2940605) | +| `SystemRegionFunctionsRector` | `system_region_list()` / `system_default_region()` → `theme.region.manager` service | [node/3015812](https://www.drupal.org/node/3015812) | +| `CheckMarkupToProcessedTextRector` | `check_markup()` → processed-text render array | [node/3588040](https://www.drupal.org/node/3588040) | +| `RemoveFilterTipsLongParamRector` | `FilterInterface::tips()` — drop the `$long` parameter | [node/3567879](https://www.drupal.org/node/3567879) | +| `SystemSortThemesRector` | `'system_sort_themes'` string callback → `Closure` (closure callable) | [node/3566774](https://www.drupal.org/node/3566774) | +| `LocaleCompareIncToServiceRector` | `locale_translation_flush_projects()` / `locale_translation_build_projects()` / `locale_translation_check_projects*()` etc. → `locale.project` and `LocaleSource` services | [node/3037031](https://www.drupal.org/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](https://www.drupal.org/node/3407994) | +| `FunctionToStaticRector` | `file_icon_class()` → `IconMimeTypes::getIconClass()`; `file_icon_map()` → `IconMimeTypes::getGenericMimeType()` | [node/3411269](https://www.drupal.org/node/3411269) | +| `ReplaceRebuildThemeDataRector` | `ThemeHandlerInterface::rebuildThemeData()` → `extension.list.theme` service | [node/3413196](https://www.drupal.org/node/3413196) | +| `ReplaceModuleHandlerGetNameRector` | `ModuleHandlerInterface::getName()` → `extension.list.module` service | [node/3310017](https://www.drupal.org/node/3310017) | +| `ClassConstantToClassConstantRector` | `FileSystemInterface::EXISTS_*` → `\Drupal\Core\File\FileExists` enum | [node/3426517](https://www.drupal.org/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](https://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: + +```sh +cp vendor/palantirnet/drupal-rector/rector.php . +``` + +If you have local customisations, merge them by hand. The notable new block is: + +```php +$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: + +```php +$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`): + +```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. + +[1.0.0-beta1]: https://github.com/palantirnet/drupal-rector/releases/tag/1.0.0-beta1 +## [0.21.2] — 2026-05-08 + +### What's Changed +* build: fix codestyle by @bbrala in https://github.com/palantirnet/drupal-rector/pull/322 +* fix: Fix phpstan issues and update pipeline actions to current versions by @bbrala in https://github.com/palantirnet/drupal-rector/pull/324 +* Allow rector ^2.4.1 and replace file with getFile() if method exists by @samsonasik in https://github.com/palantirnet/drupal-rector/pull/326 +* Add theme extension by @nlighteneddesign in https://github.com/palantirnet/drupal-rector/pull/325 +* fix: fix theme hook test by @bbrala in https://github.com/palantirnet/drupal-rector/pull/328 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.21.1...0.21.2 + +[0.21.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.21.2 + +## [0.21.1] — 2025-11-07 + +### What's Changed +* Fix method not found on GetDeclaringSourceTrait::getDeclaringSource() by @samsonasik in https://github.com/palantirnet/drupal-rector/pull/317 +* Update Deny list to include to missing hooks and remove preprocess by @nlighteneddesign in https://github.com/palantirnet/drupal-rector/pull/318 +* Better replacement for dynamic uppercase parts in Implements hook_xy by @Berdir in https://github.com/palantirnet/drupal-rector/pull/320 + +### New Contributors +* @Berdir made their first contribution in https://github.com/palantirnet/drupal-rector/pull/320 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.21.0...0.21.1 + +[0.21.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.21.1 + +## [0.21.0] — 2025-05-23 + +This release adds Rector 2 support. FOr now we support both 1 and 2, for now this seems like a reasonable goal. If we start running into to many issues we will split the repo into a 1 and 2 release. + +### What's Changed +* feat: Drop Drupal 8 support by @bbrala in https://github.com/palantirnet/drupal-rector/pull/311 +* feat: Add support for PHPstan 2 and Rector 2 by @ptmkenny in https://github.com/palantirnet/drupal-rector/pull/312 +* feat: Add new HookConvert rector to convert legacy hooks to new OOP hooks. by @nlighteneddesign in https://github.com/palantirnet/drupal-rector/pull/308 +* chore: Remove use of protected property NodeNameResolver on HookConvertRector by @samsonasik in https://github.com/palantirnet/drupal-rector/pull/316 + +### New Contributors +* @ptmkenny made their first contribution in https://github.com/palantirnet/drupal-rector/pull/312 +* @nlighteneddesign made their first contribution in https://github.com/palantirnet/drupal-rector/pull/308 +* @samsonasik made their first contribution in https://github.com/palantirnet/drupal-rector/pull/316 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.20.3...0.21.0 + +[0.21.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.21.0 + +## [0.20.3] — 2024-06-10 + +### New rectors +* New rector (10.3): Add rule for file_icon_class() and file_icon_map() by @timohuisman in https://github.com/palantirnet/drupal-rector/pull/304 +* Add common PHPUnit 10 deprecations by @bbrala in https://github.com/palantirnet/drupal-rector/pull/307 + +### Bugfixes +* symplify/rule-doc-generator doesnt like cs-fixer a lot of times. Adju… by @bbrala in https://github.com/palantirnet/drupal-rector/pull/306 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.20.2...0.20.3 + +[0.20.3]: https://github.com/palantirnet/drupal-rector/releases/tag/0.20.3 + +## [0.20.2] — 2024-05-31 + +### New rectors +* New rector (10.1): drupal_theme_rebuild is deprecated by @bbrala in https://github.com/palantirnet/drupal-rector/pull/297 +* New rector (10.2): Rector for _drupal_flush_css_js through new VersionedFunctionToServiceRector by @bbrala in https://github.com/palantirnet/drupal-rector/pull/302 + +### Bugfixes +* bugfix: remove extra \ from Drupal\Core\StringTranslation\ByteSizeMarkup rector by @bbrala in https://github.com/palantirnet/drupal-rector/pull/301 + +### What's Changed +* Update core_plugin_conversion.md by @bbrala in https://github.com/palantirnet/drupal-rector/pull/298 +* doc: Fix doc for AnnotationToAttributeRector and generate new docs. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/299 +* Upgrade project tooling to PHP 8.2 by @agentrickard in https://github.com/palantirnet/drupal-rector/pull/300 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.20.1...0.20.2 + +[0.20.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.20.2 + +## [0.20.1] — 2024-03-09 + +This release adds the code to move from annotations to attributes which currently is being done in core. For now this code is not added by default until things settle down. If you want to use the rector check out [AnnotationToAttributeRector](https://github.com/palantirnet/drupal-rector/blob/main/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php). + +If you want to contribute to core in the migration, [check out these docs](https://github.com/palantirnet/drupal-rector/blob/main/docs/core_plugin_conversion.md) or go help out in [#3396165](https://www.drupal.org/project/drupal/issues/3396165). + +### New rectors +* New rector: Rule to convert action to attributes by @andypost in https://github.com/palantirnet/drupal-rector/pull/257 +* New rector (9.1): ClassConstantToClassConstantRector to rename class constants to a new class. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/282 +* feat: Optimize AnnotationToAttributeRector by @bbrala in https://github.com/palantirnet/drupal-rector/pull/296 + +### What's Changed +* Add bbrala to authors by @bbrala in https://github.com/palantirnet/drupal-rector/pull/294 +* Add agentrickard to authors. by @agentrickard in https://github.com/palantirnet/drupal-rector/pull/295 + +### New Contributors +* @andypost made their first contribution in https://github.com/palantirnet/drupal-rector/pull/257 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.20.0...0.20.1 + +[0.20.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.20.1 + +## [0.20.0] — 2024-03-05 + +### New Rector + +* feat: add rector rule for deprecated GDToolkit by @timohuisman in https://github.com/palantirnet/drupal-rector/pull/289 + +### What's Changed +* refactor: Replace deprecated LevelSetLists for version specific PHPUnit, Symfony and Twig sets by @timohuisman in https://github.com/palantirnet/drupal-rector/pull/290 +* feat: upgrade to rector 1.0 by @bbrala in https://github.com/palantirnet/drupal-rector/pull/292 +* fix: Add proper levels for all symfony versions deprecated in the different versions of Drupal by @bbrala in https://github.com/palantirnet/drupal-rector/pull/293 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.19.2...0.20.0 + +[0.20.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.20.0 + +## [0.19.2] — 2024-01-17 + +### Fixed + +* Fix: Double deprecated calls when already refactored by @bbrala in https://github.com/palantirnet/drupal-rector/pull/288 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.19.1...0.19.2 + +[0.19.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.19.2 + +## [0.19.1] — 2024-01-12 + +### What's Changed +* bugfix: switch arguments of AbstractDrupalCoreRector by @timohuisman in https://github.com/palantirnet/drupal-rector/pull/287 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.19.0...0.19.1 + +[0.19.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.19.1 + +## [0.19.0] — 2024-01-11 + +### New rector +* feat: Add rector rule for format_size in 10.2 by @timohuisman in https://github.com/palantirnet/drupal-rector/pull/286 + +### What's Changed +* feat: Upgrade rector to 0.19 and fix phpstan incompatiblities by @bbrala in https://github.com/palantirnet/drupal-rector/pull/284 + +### New Contributors +* @timohuisman made their first contribution in https://github.com/palantirnet/drupal-rector/pull/286 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.6...0.19. + +[0.19.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.19.0 + +## [0.18.6] — 2023-12-28 + +### What's Changed +* Hotfix: New rector FILE_STATUS_PERMANENT was not added to deprecation list. … by @bbrala in https://github.com/palantirnet/drupal-rector/pull/280 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.5...0.18.6 + +[0.18.6]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.6 + +## [0.18.5] — 2023-12-28 + +### New rector +* New rector (9.3): Support FILE_STATUS_PERMANENT deprecation by @bbrala in https://github.com/palantirnet/drupal-rector/pull/278 +* New rector (9.1): \Drupal\Component\Utility\Bytes::toInt() is deprecated by @bbrala in https://github.com/palantirnet/drupal-rector/pull/279 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.4...0.18.5 + +[0.18.5]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.5 + +## [0.18.4] — 2023-12-18 + +### New rector + +* New rector: Support for module_load_include deprecation (Drupal 9) by @bbrala in https://github.com/palantirnet/drupal-rector/pull/277 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.3...0.18.4 + +[0.18.4]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.4 + +## [0.18.3] — 2023-12-06 + +### New rector +* PHPUnit: Rector to fix missing parent::setUp and parent::tearDown methods by @bbrala in https://github.com/palantirnet/drupal-rector/pull/273 + +### Bugfix +* Fix EntityManagerRector based on project_analysis results by @bbrala in https://github.com/palantirnet/drupal-rector/pull/274 + +### Other changes +* Remove latest release from readme, unneeded maintainance, its already… by @bbrala in https://github.com/palantirnet/drupal-rector/pull/270 +* Better workflows by @bbrala in https://github.com/palantirnet/drupal-rector/pull/275 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.2...0.18.3 + +[0.18.3]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.3 + +## [0.18.2] — 2023-11-24 + +### New rector +* Add rector for system_time_zones by @bbrala in https://github.com/palantirnet/drupal-rector/pull/271 + +### What's Changed +* Link packagist version shield to package on packagist.org by @kasperg in https://github.com/palantirnet/drupal-rector/pull/269 + +### New Contributors +* @kasperg made their first contribution in https://github.com/palantirnet/drupal-rector/pull/269 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.1...0.18.2 + +[0.18.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.2 + +## [0.18.1] — 2023-11-21 + +### New rector +* Add $defaultTheme property if missing on BrowserTestBase by @mglaman in https://github.com/palantirnet/drupal-rector/pull/211 + +### What's Changed +* Update README.md by @bbrala in https://github.com/palantirnet/drupal-rector/pull/264 +* Restructure rules by major and generate rule list (docs) by @bbrala in https://github.com/palantirnet/drupal-rector/pull/267 +* Fix db_query.php expected test. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/268 +* Update README-automated-testing.md by @bbrala in https://github.com/palantirnet/drupal-rector/pull/266 +* Add php-cs-fixer by @bbrala in https://github.com/palantirnet/drupal-rector/pull/265 +* Add $defaultTheme property if missing on BrowserTestBase by @mglaman in https://github.com/palantirnet/drupal-rector/pull/211 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.18.0...0.18.1 + +[0.18.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.1 + +## [0.18.0] — 2023-11-10 + +### Release highlights +This release of Drupal Rector introduces backwards compatible fixes for new rectors that target deprecations in Drupal 10. This is made possible by using the new `DeprecationHelper` class introduced in Drupal 10.1. + +We also worked on making the code more sustainable by employing `ConfigurableRectorInterface`. This interface allows passing configuration to a rector to set certain variables. This means it is easier to reuse rectors that for example rewrite a function to a static call without introducing new code. + +Rector has also been upgraded from 0.15 to 0.18, bringing a lot of improvement, but also making it harder to add comments to code generated by Rector. This there are instances where the previous version added comments, that the current does not. + +* Support for Drupal 10 deprecations and testing by @bbrala in https://github.com/palantirnet/drupal-rector/pull/252 +* Support for backwards compatible rector rules and version scoping of rules by @bbrala in https://github.com/palantirnet/drupal-rector/pull/250 + +### Upgrade notes + +Because rector moved to Laravel dependency injection a new version of `rector.php` must be copies/configured in your project root. + +### New rectors +* Issue #3354343: Add TwigSetList::TWIG_240 to D9 deprecations. by @m4olivei in https://github.com/palantirnet/drupal-rector/pull/223 +* Add new Rector for system_sort_modules_by_info_name() by @bbrala in https://github.com/palantirnet/drupal-rector/pull/253 +* module_load_install() is deprecated in 9.4 and removed in 10. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/239 +* Implement watchdog_exception rector by @bbrala in https://github.com/palantirnet/drupal-rector/pull/262 +* 9.3 Multiple taxonomy rectors by @bbrala in https://github.com/palantirnet/drupal-rector/pull/254 + +### What's Changed +* Remove NodesToAddCollector by @bbrala in https://github.com/palantirnet/drupal-rector/pull/225 +* Remove dependency on rector-src by @bbrala in https://github.com/palantirnet/drupal-rector/pull/236 +* Update PHPUnit configuration by @agentrickard in https://github.com/palantirnet/drupal-rector/pull/237 +* Phase 1 - Refactor to support Rector 0.17 by @bbrala in https://github.com/palantirnet/drupal-rector/pull/238 +* Simplify codebase: replace EntityLoadBase with configurable rule by @bbrala in https://github.com/palantirnet/drupal-rector/pull/228 +* Refactor AssertLegacyTraitBase and ConstantToClassConstantBase to configurable rule by @bbrala in https://github.com/palantirnet/drupal-rector/pull/229 +* Improve AssertNoFieldByIdRector by @mglaman in https://github.com/palantirnet/drupal-rector/pull/213 +* Create FunctionToServiceRector to replace all function to service call deprecations by @bbrala in https://github.com/palantirnet/drupal-rector/pull/242 +* New StaticToFunctionRector to replace StaticToFunctionBase by @bbrala in https://github.com/palantirnet/drupal-rector/pull/243 +* Remove single use FunctionToImmutableConfigBase by @bbrala in https://github.com/palantirnet/drupal-rector/pull/245 +* StaticArgumentRenameBase and DrupalServiceRenameBase are now covered with StaticArgumentRenameRector by @bbrala in https://github.com/palantirnet/drupal-rector/pull/241 +* New ExtensionPathRector to replace ExtensionPathBase by @bbrala in https://github.com/palantirnet/drupal-rector/pull/244 +* New DBRector to replace DbBase with configurable rector. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/246 +* GetMockRector as configurable rule by @bbrala in https://github.com/palantirnet/drupal-rector/pull/248 +* Upgrade to PHPStan level 6 by @bbrala in https://github.com/palantirnet/drupal-rector/pull/249 +* MethodToMethodWithCheckRector to replace methods with a certainty check by @bbrala in https://github.com/palantirnet/drupal-rector/pull/247 +* Refactor Base\FunctionToStatic to configurable rule. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/251 +* Upgrade to Rector 0.18.x by @bbrala in https://github.com/palantirnet/drupal-rector/pull/240 +* Refactored BC rules with configuration by @bbrala in https://github.com/palantirnet/drupal-rector/pull/255 +* Add better labels to readme by @bbrala in https://github.com/palantirnet/drupal-rector/pull/256 +* Add failing unit test for ExtensionPathRector when assining by @bbrala in https://github.com/palantirnet/drupal-rector/pull/258 +* Add WatchdogExceptionRector the 10.1 setlist by @bbrala in https://github.com/palantirnet/drupal-rector/pull/263 +* PHPUnit deprecations should be checked in Drupal 9 to 10 by @bbrala in https://github.com/palantirnet/drupal-rector/pull/261 +* Fix MethodToMethodWithCheckRector by also matching on MethodCall. by @bbrala in https://github.com/palantirnet/drupal-rector/pull/260 +* ExtensionPathRector does not handle Assignments by @bbrala in https://github.com/palantirnet/drupal-rector/pull/259 + +### New Contributors +* @m4olivei made their first contribution in https://github.com/palantirnet/drupal-rector/pull/223 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.15.1...0.18.0 + +[0.18.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.18.0 + +## [0.15.1] — 2023-03-23 + +### What's Changed +* Update rector config to resolve bootstrap issues with recent rector releases by @mglaman in https://github.com/palantirnet/drupal-rector/pull/220 +* Update rector config to resolve bootstrap issues with recent rector releases by @goba in https://github.com/palantirnet/drupal-rector/pull/219 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.15.0...0.15.1 + +[0.15.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.15.1 + +## [0.15.0] — 2023-01-11 + +### What's Changed +* Update to rector 15 and github setup v3. by @agentrickard in https://github.com/palantirnet/drupal-rector/pull/218 and @FlorentTorregrosa in https://github.com/palantirnet/drupal-rector/pull/217 +* Fix function signature in tests by @chrfritsch in https://github.com/palantirnet/drupal-rector/pull/216 + +### New Contributors +* @chrfritsch made their first contribution in https://github.com/palantirnet/drupal-rector/pull/216 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.13.1...0.15.0 + +[0.15.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.15.0 + +## [0.13.1] — 2022-08-17 + +### What's Changed +* ProtectedStaticModulesProperty rule by @mglaman in https://github.com/palantirnet/drupal-rector/pull/210 +* Update rector.php so we do not use FQCN's in method arguments by @bbrala in https://github.com/palantirnet/drupal-rector/pull/209 + +### New Contributors +* @bbrala made their first contribution in https://github.com/palantirnet/drupal-rector/pull/209 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.13.0...0.13.1 + +[0.13.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.13.1 + +## [0.13.0] — 2022-07-15 + +### What's Changed +* #3295386 Bump to rector/rector:0.13.8 by @mglaman in https://github.com/palantirnet/drupal-rector/pull/204 +* Remove unneeded assert for $pathValue by @mglaman in https://github.com/palantirnet/drupal-rector/pull/206 +* Scope may not be available when detecting delcaring source by @mglaman in https://github.com/palantirnet/drupal-rector/pull/207 +* Prevent @doesNotPerformAssertions from being added to tests by @mglaman in https://github.com/palantirnet/drupal-rector/pull/208 + + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.12.4...0.13.0 + +[0.13.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.13.0 + +## [0.12.4] — 2022-06-01 + +### Release notes + +* The 0.12.4 is a stable release pinned to Rector 0.12.21. Developers should be aware that Rector 0.12.22 introduces breaking changes to how we handle Drupal configuration. + +* The 0.12.5 release of drupal-rector will include Rector 0.12.22. The upgrade path should be as simple as re-copying the configuration file. `cp vendor/palantirnet/drupal-rector/rector.php` + +### What's Changed +* Add integration test for user_password() deprecation by @claudiu-cristea in https://github.com/palantirnet/drupal-rector/pull/201 +* Issue #3282217: file_build_uri() is deprecated by @claudiu-cristea in https://github.com/palantirnet/drupal-rector/pull/202 +* Remove remaining Rector conflicts up to 0.12.21 by @mglaman in https://github.com/palantirnet/drupal-rector/pull/203 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.12.3...0.12.4 + +[0.12.4]: https://github.com/palantirnet/drupal-rector/releases/tag/0.12.4 + +## [0.12.3] — 2022-05-24 + +The 0.12.3 release bypasses a known conflict with Rector 0.12.18. The current preferred version is Rector 0.12.19. + +### What's Changed +* Fix PHP 8 warnings on `null` to `file_exists` by @mglaman in https://github.com/palantirnet/drupal-rector/pull/196 +* Issue #3277704: Remove PHPUNIT_75 constant in Rector by @FlorentTorregrosa in https://github.com/palantirnet/drupal-rector/pull/197 +* Issue #3280205: drupalPostForm() with 1st param NULL by @claudiu-cristea in https://github.com/palantirnet/drupal-rector/pull/198 +* user_password() deprecation by @claudiu-cristea in https://github.com/palantirnet/drupal-rector/pull/199 +* Remove conflicts on rector/rector by @mglaman in https://github.com/palantirnet/drupal-rector/pull/200 + +### New Contributors +* @claudiu-cristea made their first contribution in https://github.com/palantirnet/drupal-rector/pull/198 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.12.2...0.12.3 + +[0.12.3]: https://github.com/palantirnet/drupal-rector/releases/tag/0.12.3 + +## [0.12.2] — 2022-05-04 + +### What's Changed +* Mark conflict on rector >=0.12.18 by @mglaman in https://github.com/palantirnet/drupal-rector/pull/195 +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.12.1...0.12.2 + +[0.12.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.12.2 + +## [0.12.1] — 2022-02-21 + +Updates Drupal Rector for the most common Drupal 9.3 and 9.4 deprecations. + +* https://www.drupal.org/project/rector/issues/3261614 + +### What's Changed + +* Update composer.json for allow-plugins by @mglaman in https://github.com/palantirnet/drupal-rector/pull/186 +* Rectors for drupal_get_path & drupal_get_filename by @mglaman in https://github.com/palantirnet/drupal-rector/pull/187 +* Handle custom message types by @mglaman in https://github.com/palantirnet/drupal-rector/pull/190 +* Add Rector for deprecated `render` by @mglaman in https://github.com/palantirnet/drupal-rector/pull/188 +* Use dev-main for rector-src by @mglaman in https://github.com/palantirnet/drupal-rector/pull/193 +* Rectors for file_move, file_copy, file_save_data by @mglaman in https://github.com/palantirnet/drupal-rector/pull/189 +* file_url_generator service Rector rules by @mglaman in https://github.com/palantirnet/drupal-rector/pull/191 +* MetadataBag::clearCsrfTokenSeed replaced by stampNew by @mglaman in https://github.com/palantirnet/drupal-rector/pull/192 + +**Full Changelog**: https://github.com/palantirnet/drupal-rector/compare/0.12.0...0.12.1 + +[0.12.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.12.1 + +## [0.12.0] — 2021-11-19 + +This release updates the codebase to support: + +- PHPStan 1.0 +- Rector 0.12.x + +[0.12.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.12.0 + +## [0.11.4] — 2021-10-13 + +This is a maintenance release that includes updates to stay compatible with Rector. + +[0.11.4]: https://github.com/palantirnet/drupal-rector/releases/tag/0.11.4 + +## [0.11.3] — 2021-09-03 + +This is a maintenance release that fixes a number of issues and updates the `deprecations-index` for Drupal 9 changes. + +- Cleans up internal function doc mismatches. +- Fix entity_view(), entity_delete_multiple() and EntityTypeInterface::getLowercaseLabel deprecation message +- Issue #3228110: Improve AssertNoUniqueTextRector documentation and scope +- Adds AssertLegacy and other new items to the index. +- Issue #3229896: Fix broken params on assert cache tag Rector rules +- Ensure proper rector install on github ci. +- Issue #3221584: Use "*" over sha for sandbox test +- Issue #3228113: Make PassRector more specific and fix documentation +- Prevents stubs used for testing from breaking end user's PHPUnit tests for Drupal +- Fixes BuildXPathQuery docs. +- Fix REQUEST_TIME deprecation message. Noted by @mglaman +- Issue #3222671: Rector for assertNoFieldByName() +- Issue #3222671: Rector for assertUniqueText() and assertNoUniqueText() +- Issue #3222671: Rector for pass() + +[0.11.3]: https://github.com/palantirnet/drupal-rector/releases/tag/0.11.3 + +## [0.11.2] — 2021-07-29 + +This release corrects an error in Rector 0.11.38 that was fixed in later versions (https://github.com/rectorphp/rector-src/pull/484). + +See https://www.drupal.org/project/rector/issues/3225019 and a hat-tip to `Grimreaper` for reporting and testing this issue. + +[0.11.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.11.2 + +## [0.11.1] — 2021-07-06 + +The release prepares drupal-rector to handle Drupal 9 deprecations, using PHP 8 and rector v 0.11. + +This release also changes the file structure of the underlying code and introduced PHPUnit testing. + +Note that this version can be run in PHP 7 without PHPUnit, which is not necessary for running rector upgrades on your code. + +This release supports 8.x and 9.x code conversions and can be run with either Drupal 8 or Drupal 9. + +[0.11.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.11.1 + +## [0.11.0] — 2021-06-30 + +This release brings us up-to-date with Rector 11 and adds support for PHPUnit testing. + +PHPUnit testing requires PHP 8 and is used for development. + +Creating new Rector rules and running the Rector update requires PHP 7 or higher. + +[0.11.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.11.0 + +## [0.10.0] — 2021-06-23 + +This release updates to Rector version 0.10.0 and prepares for more substantial changes coming to prepare for Drupal 10. + +### Major changes + +- We now use `rector.php` instead of `rector.yml` for configuration. +- Adds PHPStan for static code analysis. +- We are deprecating the Behat tests in favor of PHPUnit (See #152) + +### New rules + +- entity_view + +[0.10.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.10.0 + +## [0.5.6] — 2020-06-05 + +Summary of updates in this release: + +* 3 new Rector rules: + * DatetimeDateStorageFormatRector + * DatetimeDatetimeStorageFormatRector + * DatetimeStorageTimezoneRector +* Upgrade to rector-prefixed 0.7.27 +* Fix internal Github tests + +[0.5.6]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.6 + +## [0.5.5] — 2020-05-30 + +Summary of updates in this release: +* Bug fixes for Rector rules +* Running the latest rector-prefixed +* Commented out PHPUnit8 code upgrade option in `rector.yml` +* [Drupal Rector rules documentation](https://github.com/palantirnet/drupal-rector/blob/master/docs/drupal_rector_rules.md) +* Add comments to call out edge cases in Drupal Rector code replacements (this option can be disabled through `rector.yml`) + +[0.5.5]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.5 + +## [0.5.4] — 2020-05-22 + +Using latest rector-prefixed, which would stop creating diffs with unnecessary indentation fixes. + +Added new Rector rules for the following deprecations: +* FILE_EXISTS_RENAME +* LinkGeneratorTrait::l() +* entity_create() +* SafeMarkup::format() + +[0.5.4]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.4 + +## [0.5.3] — 2020-05-17 + +Added new Rector rules for the following deprecations: + +* Unicode::strlen +* Unicode::substr +* EntityInterface:link() +* entity_load() +* node_load() +* file_load() +* file_directory_temp +* file_directory_os_temp +* drupal_realpath() +* file_uri_target() + +[0.5.3]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.3 + +## [0.5.2] — 2020-05-09 + +Added new Rector rules for the following deprecations: + +* db_update() +* file_scan_directory() +* REQUEST_TIME +* entity_get_display +* entity_get_form_display +* file_default_scheme() +* EntityInterface:urlInfo() + +[0.5.2]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.2 + +## [0.5.1] — 2020-05-04 + +Added new Rector rules for the following deprecations: + +* FILE_MODIFY_PERMISSIONS +* db_delete() + +[0.5.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.1 + +## [0.5.0] — 2020-04-27 + +Added new Rector rules for the following deprecations: + +* file_unmanaged_save_data() + +[0.5.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.5.0 + +## [0.4.1] — 2020-04-25 + +Rector-prefix 0.7.19 is broken - +Issue: +rectorphp/rector#3256 + +PR to prevent it from happening in the future: +rectorphp/rector#3255 (comment) + +[0.4.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.4.1 + +## [0.4.0] — 2020-04-17 + +Upgraded to latest Rector (version 0.7.x) + +Added CI test using Github Actions + +Added new Rector rules for the following deprecations: +* format_date +* Unicode::strtolower +* FILE_CREATE_DIRECTORY +* FILE_EXISTS_REPLACE +* Drupal::l() +* drupal_render() +* drupal_render_root() + +[0.4.0]: https://github.com/palantirnet/drupal-rector/releases/tag/0.4.0 + +## [0.3.3] — 2020-04-11 + +Deprecations covered: +``` +drupal_set_message() +entityManager() + Drupal::entityManager() + ControllerBase::entityManager() +db_insert +db_select +db_query +file_prepare_directory() +getMock() + BrowserTestBase::getMock() + KernelTestBase::getMock() + UnitTestCase::getMock() +Drupal::url() +``` + +[0.3.3]: https://github.com/palantirnet/drupal-rector/releases/tag/0.3.3 + +## [0.3.1] — 2020-02-17 + +Renamed package to Drupal-Rector + +[0.3.1]: https://github.com/palantirnet/drupal-rector/releases/tag/0.3.1 + diff --git a/README.md b/README.md index 62c0cf891..bd359778c 100644 --- a/README.md +++ b/README.md @@ -4,36 +4,30 @@ Automate fixing deprecated Drupal code. ## Status -[![Packagist Version](https://img.shields.io/packagist/v/palantirnet/drupal-rector)](https://packagist.org/packages/palantirnet/drupal-rector) ![Functional test: Rector examples](https://img.shields.io/github/actions/workflow/status/palantirnet/drupal-rector/functional_test__rector_examples.yml?logo=github&label=Functional%20tests) ![Unit tests](https://img.shields.io/github/actions/workflow/status/palantirnet/drupal-rector/phpunit.yml?logo=github&label=Unit%20tests) ![PHPStan](https://img.shields.io/github/actions/workflow/status/palantirnet/drupal-rector/phpstan.yml?logo=github&label=PHPStan) +[![Packagist Version](https://img.shields.io/packagist/v/palantirnet/drupal-rector)](https://packagist.org/packages/palantirnet/drupal-rector) ![Functional tests](https://img.shields.io/github/actions/workflow/status/palantirnet/drupal-rector/functional_test__single_rectors.yml?logo=github&label=Functional%20tests) ![Unit tests](https://img.shields.io/github/actions/workflow/status/palantirnet/drupal-rector/phpunit.yml?logo=github&label=Unit%20tests) ![PHPStan](https://img.shields.io/github/actions/workflow/status/palantirnet/drupal-rector/phpstan.yml?logo=github&label=PHPStan) -### Release notes - -* The 0.18.0 and higher releases of drupal-rector will include Rector 0.18+. The upgrade path should be as simple as re-copying the configuration file. `cp vendor/palantirnet/drupal-rector/rector.php`. - -* The 0.13.0 and higher releases of drupal-rector will include Rector 0.13.8+. The upgrade path should be as simple as re-copying the configuration file. `cp vendor/palantirnet/drupal-rector/rector.php` - -*Note that GitHub does not let us have different default homepage and merge branches. If you checked out the project using packagist/composer, read the docs for your version.* +If upgrading from an older version, refresh `rector.php` by copying from the vendor copy: `cp vendor/palantirnet/drupal-rector/rector.php .` ## Introduction -You can read more details in the following blog post: +Originally created to automate Drupal 9 upgrades; Drupal 8 and 9 rules are still included for legacy projects. You can read more details in the following blog post: https://www.palantir.net/blog/jumpstart-your-drupal-9-upgrade-drupal-rector ## Documentation -Development guides, individual deprecation overviews, and other resources can be found here: +Development guides and other resources: https://www.palantir.net/rector -List of all rules with examples: +Changelog and release history: -[Rule overview in docs/rules_overview.md](docs%2Frules_overview.md) +https://github.com/palantirnet/drupal-rector/releases ## Scope and limitations -The development of this tool is prioritized by the perceived impact of the deprecations and updates. There are many deprecations that often involve several components and for each of these there are several ways to address the deprecation. +Drupal 10 and 11 are the primary targets (Drupal 8/9 rules are included for legacy projects). The development of this tool is prioritized by the perceived impact of the deprecations and updates. There are many deprecations that often involve several components and for each of these there are several ways to address the deprecation. We've tried to determine impact based on: - The use of the deprecated functionality in the contributed modules on Drupal.org @@ -61,7 +55,7 @@ For contribution suggestions, please see the later section of this document. ## Installation -**NOTE**: To have the best experience with Drupal Rector, your Drupal site should be running version 8.9 or higher. +**NOTE**: To have the best experience with Drupal Rector, your Drupal site should be running Drupal 10 or higher. ### Install Drupal Rector inside a Drupal project. @@ -82,86 +76,88 @@ cp vendor/palantirnet/drupal-rector/rector.php . ``` By default, Drupal Rector will fix deprecated code for all versions of Drupal. If you want to change this behavior, modify -the sets used in the `rector.php` config. For example, if your site is still on Drupal 9.3, and you cannot fix deprecations -made in Drupal 9.4, use the following configuration: +the sets used in the `rector.php` config. For example, if your site is still on Drupal 10.3, and you do not want to fix deprecations +made in Drupal 10.4, use the following configuration: ```php $rectorConfig->sets([ - Drupal9SetList::DRUPAL_90, - Drupal9SetList::DRUPAL_91, - Drupal9SetList::DRUPAL_92, - Drupal9SetList::DRUPAL_93, + Drupal10SetList::DRUPAL_100, + Drupal10SetList::DRUPAL_101, + Drupal10SetList::DRUPAL_102, + Drupal10SetList::DRUPAL_103, ]); ``` -This is more granular than the `Drupal9SetList::DRUPAL_9` set. +This is more granular than the `Drupal10SetList::DRUPAL_10` set. Since Drupal 10.1 there is not real reason not to include later versions. It will detect the installed Drupal version and supply BC wrappers as needed if you enable it in the config. -## Suggested workflow +### DrupalRectorSettings -1. Analyze your code with Rector and review suggested changes: +The copied `rector.php` includes a `DrupalRectorSettings` block that controls two behaviours: -```sh -$ vendor/bin/rector process web/modules/contrib/[YOUR_MODULE] --dry-run -``` - -2. Apply suggested changes: +**Backward-compatibility wrapping** — when enabled, rule results are wrapped in `DeprecationHelper::backwardsCompatibleCall()` so the code works on both the old and new Drupal API simultaneously. The default in `rector.php` is **disabled** (recommended for most projects). Enable it when you need the output to run on multiple Drupal versions at the same time: -```sh -$ vendor/bin/rector process web/modules/contrib/[YOUR_MODULE] +```php +$rectorConfig->singleton(DrupalRectorSettings::class, fn () => + (new DrupalRectorSettings()) + ->enableBackwardCompatibility() +); ``` -You can find more information about Rector [here](https://github.com/rectorphp/rector). - -## Troubleshooting - -### PhpStan composer issues - -You may need to upgrade `phpstan/phpstan` with Composer before installing this package. +**Minimum supported Drupal version** (contrib modules) — if you are running Rector against a contrib module that must stay compatible with an older Drupal release, set `minimumCoreVersionSupported` so BC wrappers are emitted correctly even when your development environment runs a newer Drupal: -Rector itself has conflicts with older versions of PhpStan. - -### Unable to find Rector rule classes - -If you are getting errors like +```php +$rectorConfig->singleton(DrupalRectorSettings::class, fn () => + (new DrupalRectorSettings()) + ->enableBackwardCompatibility() + ->setMinimumCoreVersionSupported('10.5.0') +); +``` -`[ERROR] Class "DrupalRector\Drupal8\Rector\Deprecation\EntityManagerRector" was not found while loading` +### Cleaning up BC wrappers (contrib modules) -You may need to rebuild your autoload file. +If you previously used backward-compatibility wrapping and have since raised your module's minimum supported Drupal version, use `DeprecationHelperRemoveRector` to strip the now-redundant wrappers. It replaces each `DeprecationHelper::backwardsCompatibleCall()` with the new API call directly, for any deprecation introduced before your configured minimum version. -`composer dump-autoload` +```php +use DrupalRector\Rector\Deprecation\DeprecationHelperRemoveRector; +use DrupalRector\Rector\ValueObject\DeprecationHelperRemoveConfiguration; -### FileLocator::locate() must be compatible with FileLocatorInterface::locate() +$rectorConfig->ruleWithConfiguration(DeprecationHelperRemoveRector::class, [ + new DeprecationHelperRemoveConfiguration('10.3.0'), +]); +``` -If you are getting errors like +With the above, a wrapper like: -``` -PHP Fatal error: Declaration of _HumbugBox3630ef99eac4\Symfony\Component\HttpKernel\Config\FileLocator::locate($file, $currentPath = NULL, $first = true) must be compatible with _HumbugBox3630ef99eac4\Symfony\Component\Config\FileLocatorInterface::locate(string $name, ?string $currentPath = NULL, bool $first = true) in phar:///var/www/html/vendor/rector/rector-prefixed/rector/vendor/symfony/http-kernel/Config/FileLocator.php on line 20 -Fatal error: Declaration of _HumbugBox3630ef99eac4\Symfony\Component\HttpKernel\Config\FileLocator::locate($file, $currentPath = NULL, $first = true) must be compatible with _HumbugBox3630ef99eac4\Symfony\Component\Config\FileLocatorInterface::locate(string $name, ?string $currentPath = NULL, bool $first = true) in phar:///var/www/html/vendor/rector/rector-prefixed/rector/vendor/symfony/http-kernel/Config/FileLocator.php on line 20 +```php +DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '9.1.0', + fn() => \Drupal::service('password_generator')->generate(), + fn() => user_password() +); ``` -You may need to check that you are -- Running `composer install` from an environment that supports Php 7.2 or greater -- Running Drupal Rector from an environment that supports Php 7.2 or greater +becomes: -Sometimes people install composer dependencies from one machine (host machine) and run Drupal Rector from another (such as a Lando VM). +```php +\Drupal::service('password_generator')->generate(); +``` -If you are having these issues try running Rector from the environment that has Php 7.2 or greater. Drupal Rector does not need a fully functional web server, it only (more or less) needs Php and access to a standard Drupal set of files. +Wrappers for deprecations introduced at or after your minimum version are left untouched. The rule is commented out in `rector.php` — uncomment and set the version when you are ready to clean up. -### Iconv error when running Rector in Alpine Docker +## Suggested workflow -If you are getting errors like +1. Analyze your code with Rector and review suggested changes: -`iconv(): Wrong charset, conversion from UTF-8 to ASCII//TRANSLIT//IGNORE is not allowed` +```sh +$ vendor/bin/rector process web/modules/contrib/[YOUR_MODULE] --dry-run +``` -You can fix it in Dockerfile with +2. Apply suggested changes: -``` -# fix work iconv library with alphine -RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ --allow-untrusted gnu-libiconv -ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so php +```sh +$ vendor/bin/rector process web/modules/contrib/[YOUR_MODULE] ``` -Credits to @zolotov88 in https://github.com/nunomaduro/phpinsights/issues/43#issuecomment-498108857 +You can find more information about Rector [here](https://github.com/rectorphp/rector). ## Development and contribution suggestions @@ -169,30 +165,22 @@ Thanks for your interest in contributing! Our goal is to make contributing to this project easy for people. While we've made certain architectural decisions here to hopefully achieve that goal, it's a work in progress and feedback is appreciated. -### Development environment - -See the instructions in [README](https://github.com/palantirnet/drupal-rector-sandbox/blob/master/README.md#developing-with-drupal-rector) - ### Adding a Rector rule If you would like to submit a Rector rule, we are looking for the following: -- A Rector rule class, see `/src/Rector/Deprecation` for existing rules -- An example file or files that show(s) the before and after, see `/rector_examples` and `/rector_examples_updated` -- An updated configuration file that registers the Rector rule, see `/config/drupal-8` -- A listing in the index file, see `/deprecation-index.yml` +- A Rector rule class, see `src/Rector/Deprecation` for existing rules. Copy an existing class as a starting point. +- A test class in `tests/src/Drupal{8,9,10,11}/Rector/` and fixture files in `tests/src/Drupal*/Rector/**/fixture/` +- An updated configuration file that registers the Rector rule, see `config/drupal-{8,9,10,11}/` #### Guides A few guides are currently available and we encourage people to create additional guides to provide their perspective and help us better understand this tool together. -##### Video guide on creating a rector rule -[https://www.palantir.net/rector/creating-drupal-rector-rule](https://www.palantir.net/rector/creating-drupal-rector-rule) - ##### Additional documentation and links [https://www.palantir.net/rector](https://www.palantir.net/rector) -#### Quick(?) overview +#### Quick overview ##### Create a Rector rule class @@ -203,40 +191,24 @@ Rector rules should be named after the deprecation, including the class name. We would like one Rector rule per deprecation. Some deprecations include updating multiple things and those would be separate rules. -To avoid duplication, we have created base classes for simple repeated patterns where possible. These end in `Base.php` and are located in `/src/Rector/Deprecation/Base`. In many of these rules, you will extend the base class, define class properties, add a class comment, and define the definition. - -Rector supports passing parameters to rules and you can also define your rules in a variety of ways. To avoid confusion for new developers, we're trying to avoid these advanced features so that someone with limited familiarity with the tool can easily determine where things are located and what they are doing. If the copy & paste challenge isn't worth this trade-off, we can re-evaluate it as we go. Suggestions appreciated. - -##### Create examples - -We are creating pairs of example files. - -These should be named the same thing as the deprecation. So, `DrupalUrlRector` has a `rector_examples/drupal_url.php` example. An example `rector_examples_updated/drupal_url.php` should also be created to show the updated code. You can run Drupal Rector on this file to show the update. +All drupal-rector rules extend `AbstractDrupalCoreRector` (found in `src/Rector/AbstractDrupalCoreRector.php`) rather than Rector's own `AbstractRector`. This base class provides three things automatically: -Example +- **Version gating** — skips the rule if the installed Drupal version predates the deprecation via `rectorShouldApplyToDrupalVersion()` +- **BC wrapping** — when backward-compatibility mode is enabled, wraps `Expr`→`Expr` results in `DeprecationHelper::backwardsCompatibleCall()` automatically +- **Configuration pattern** — you implement `refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration)` instead of `refactor()` -`DrupalUrlRector` -> `rector_examples/drupal_url.php` and `rector_examples_updated/drupal_url.php` +To avoid duplication, we have created base classes for simple repeated patterns where possible. These end in `Base.php` and are located in `src/Rector/Deprecation/Base`. In many of these rules, you will extend the base class, define class properties, add a class comment, and define the definition. -If you would like to show how the code is used in a class, you can add the class to the appropriate place in the `/rector_examples/src` or `/rector_examples/test` directories. Most of the examples in the example module are `services` in that they are stand alone classes. - -Since these classes can use static calls, dependency injection, or traits to get access to services, constants, etc, we have added more details to some class names. For example, `*Static` to indicate that the class is not using dependency injection. - -Example - -`DrupalUrlRector` -> `rector_examples/src/DrupalUrlStatic.php` and `rector_examples_updated/src/DrupalUrlStatic.php` +Rector supports passing parameters to rules and you can also define your rules in a variety of ways. To avoid confusion for new developers, we're trying to avoid these advanced features so that someone with limited familiarity with the tool can easily determine where things are located and what they are doing. If the copy & paste challenge isn't worth this trade-off, we can re-evaluate it as we go. Suggestions appreciated. ##### Create / Update a configuration file -The configuration files in `/config/drupal-8` are broken down by Drupal minor versions. +The configuration files in `config/drupal-{8,9,10,11}/` are broken down by Drupal minor versions. -Add your Rector rule to the relevant file. +Add your Rector rule to the relevant file. Always add a comment with a link to the issue and change record. The key is the fully qualified class name of the Rector rule. The key is the yaml null value `~`. -##### Update the index file - -The index file is used in part to provide automated updates to https://dev.acquia.com/drupal9/deprecation_status/errors which is a helpful way to track coverage. The `PHPStan` messages are listed there as well as in the change record comments throughout the Drupal codebase. - ## Pinning dev dependencies If there are conflicts with Rector, the package version can be conflicted with `conflict` on `rector/rector` and `phpstan/phpstan`. @@ -246,5 +218,6 @@ If there are conflicts with Rector, the package version can be conflicted with ` ## Credits +Current development is sponsored by [SWIS.nl](https://www.swis.nl).
Current development is sponsored by [Palantir.net](https://www.palantir.net).
Initial development is sponsored by [Pronovix](https://pronovix.com). diff --git a/composer.json b/composer.json index 996ac1599..1de2aec64 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "ast" ], "require": { - "rector/rector": "^1 || ^2", + "rector/rector": "^2", "webflo/drupal-finder": "^1.2" }, "license": "MIT", @@ -50,6 +50,11 @@ }, "classmap": [ "stubs" + ], + "exclude-from-classmap": [ + "**/fixture/**", + "**/fixture-*/**", + "**/Fixture/**" ] }, "config": { @@ -74,17 +79,16 @@ }, "require-dev": { "php": "^8.2", - "cweagans/composer-patches": "^1.7.2", - "friendsofphp/php-cs-fixer": "^3.58", + "cweagans/composer-patches": "^2.0", + "friendsofphp/php-cs-fixer": "^3.95.1", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12 || ^2.0", - "phpstan/phpstan-deprecation-rules": "^1.2 || ^2.0", - "phpunit/phpunit": "^10.0", - "symfony/yaml": "^5 || ^6 || ^7", - "symplify/vendor-patches": "^11.0" + "phpstan/phpstan": "^2.1.54", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpunit/phpunit": "^12.5.24", + "symfony/yaml": "^5 || ^6 || ^7.4.8", + "symplify/vendor-patches": "^12.0.6" }, "scripts": { - "docs": "composer remove friendsofphp/php-cs-fixer --dev && composer require symplify/rule-doc-generator --dev && vendor/bin/rule-doc-generator generate src/ --categorize=3 && composer remove symplify/rule-doc-generator --dev && composer require friendsofphp/php-cs-fixer --dev", "test": "vendor/bin/phpunit", "phpstan": "vendor/bin/phpstan analyse --memory-limit=2G", "check-style": "vendor/bin/php-cs-fixer check", diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index ec6539ac0..56fc97257 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -2,10 +2,10 @@ declare(strict_types=1); -use DrupalRector\Drupal10\Rector\Deprecation\VersionedFunctionToServiceRector; -use DrupalRector\Drupal10\Rector\ValueObject\VersionedFunctionToServiceConfiguration; +use DrupalRector\Rector\Deprecation\FunctionToServiceRector; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; +use DrupalRector\Rector\ValueObject\FunctionToServiceConfiguration; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; use DrupalRector\Rector\ValueObject\MethodToMethodWithCheckConfiguration; use Rector\Config\RectorConfig; @@ -23,12 +23,12 @@ // https://www.drupal.org/node/3265963 $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ - new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage'), - new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage'), + new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage', '10.2.0'), + new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage', '10.2.0'), ]); // https://www.drupal.org/node/3358337 - $rectorConfig->ruleWithConfiguration(VersionedFunctionToServiceRector::class, [ - new VersionedFunctionToServiceConfiguration('10.2.0', '_drupal_flush_css_js', 'asset.query_string', 'reset'), + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('10.2.0', '_drupal_flush_css_js', 'asset.query_string', 'reset'), ]); }; diff --git a/config/drupal-10/drupal-10.3-deprecations.php b/config/drupal-10/drupal-10.3-deprecations.php index ad76fea66..0e56f826e 100644 --- a/config/drupal-10/drupal-10.3-deprecations.php +++ b/config/drupal-10/drupal-10.3-deprecations.php @@ -2,14 +2,68 @@ declare(strict_types=1); +use DrupalRector\Drupal10\Rector\Deprecation\ReplaceModuleHandlerGetNameRector; +use DrupalRector\Drupal10\Rector\Deprecation\ReplaceRebuildThemeDataRector; +use DrupalRector\Rector\Deprecation\ClassConstantToClassConstantRector; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; +use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; +use DrupalRector\Rector\ValueObject\ClassConstantToClassConstantConfiguration; +use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; +use DrupalRector\Rector\ValueObject\MethodToMethodWithCheckConfiguration; use Rector\Config\RectorConfig; return static function (RectorConfig $rectorConfig): void { + // https://www.drupal.org/node/3407994 + // RendererInterface::renderPlain() deprecated in drupal:10.3.0, removed in drupal:12.0.0. + // Replaced by RendererInterface::renderInIsolation(). + $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ + new MethodToMethodWithCheckConfiguration('Drupal\Core\Render\RendererInterface', 'renderPlain', 'renderInIsolation', '10.3.0'), + ]); + // https://www.drupal.org/node/3411269 file_icon_class, file_icon_map $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ new FunctionToStaticConfiguration('10.3.0', 'file_icon_class', 'Drupal\file\IconMimeTypes', 'getIconClass'), new FunctionToStaticConfiguration('10.3.0', 'file_icon_map', 'Drupal\file\IconMimeTypes', 'getGenericMimeType'), ]); + + // https://www.drupal.org/node/3413196 + // ThemeHandlerInterface::rebuildThemeData() deprecated in drupal:10.3.0, removed in drupal:12.0.0. + // Replaced by \Drupal::service('extension.list.theme')->reset()->getList(). + $rectorConfig->ruleWithConfiguration(ReplaceRebuildThemeDataRector::class, [ + new DrupalIntroducedVersionConfiguration('10.3.0'), + ]); + + // https://www.drupal.org/node/3310017 + // ModuleHandlerInterface::getName() deprecated in drupal:10.3.0, removed in drupal:12.0.0. + $rectorConfig->ruleWithConfiguration(ReplaceModuleHandlerGetNameRector::class, [ + new DrupalIntroducedVersionConfiguration('10.3.0'), + ]); + + // https://www.drupal.org/node/3426517 + // FileSystemInterface::EXISTS_* deprecated in drupal:10.3.0, removed in drupal:12.0.0. + // Replaced by \Drupal\Core\File\FileExists enum cases. + $rectorConfig->ruleWithConfiguration(ClassConstantToClassConstantRector::class, [ + new ClassConstantToClassConstantConfiguration( + 'Drupal\Core\File\FileSystemInterface', + 'EXISTS_RENAME', + 'Drupal\Core\File\FileExists', + 'Rename', + '10.3.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\Core\File\FileSystemInterface', + 'EXISTS_REPLACE', + 'Drupal\Core\File\FileExists', + 'Replace', + '10.3.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\Core\File\FileSystemInterface', + 'EXISTS_ERROR', + 'Drupal\Core\File\FileExists', + 'Error', + '10.3.0', + ), + ]); }; diff --git a/config/drupal-11/drupal-11-all-deprecations.php b/config/drupal-11/drupal-11-all-deprecations.php new file mode 100644 index 000000000..fde49c10e --- /dev/null +++ b/config/drupal-11/drupal-11-all-deprecations.php @@ -0,0 +1,20 @@ +sets([ + Drupal11SetList::DRUPAL_110, + Drupal11SetList::DRUPAL_111, + Drupal11SetList::DRUPAL_112, + Drupal11SetList::DRUPAL_113, + Drupal11SetList::DRUPAL_114, + ]); + + $rectorConfig->bootstrapFiles([ + __DIR__.'/../drupal-phpunit-bootstrap-file.php', + ]); +}; diff --git a/config/drupal-11/drupal-11.0-deprecations.php b/config/drupal-11/drupal-11.0-deprecations.php new file mode 100644 index 000000000..1bfda948d --- /dev/null +++ b/config/drupal-11/drupal-11.0-deprecations.php @@ -0,0 +1,47 @@ +rule(GetNameToNameRector::class); + + // https://www.drupal.org/node/3436954 + // https://www.drupal.org/node/2575105 (change record) + // $settings['state_cache'] deprecated in drupal:11.0.0. + // State caching is now permanently enabled and the setting has no effect. + $rectorConfig->rule(RemoveStateCacheSettingRector::class); + + // https://www.drupal.org/node/3395986 + // REQUEST_TIME constant deprecated in drupal:8.3.0, removed in drupal:11.0.0. + // Replaced by \Drupal::time()->getRequestTime(). + $rectorConfig->ruleWithConfiguration(ReplaceRequestTimeConstantRector::class, [ + new DrupalIntroducedVersionConfiguration('11.0.0'), + ]); + + // https://www.drupal.org/node/3574717 + // https://www.drupal.org/node/3442785 (change record) + // getMigrationDependencies($expand) deprecated in drupal:11.0.0, removed in drupal:12.0.0. + // The $expand boolean argument is removed; call without arguments. + $rectorConfig->ruleWithConfiguration(StripMigrationDependenciesExpandArgRector::class, [ + new DrupalIntroducedVersionConfiguration('11.0.0'), + ]); + + // https://www.drupal.org/node/3439369 + // https://www.drupal.org/node/3282894 (change record) + // Sql::getMigrationPluginManager() deprecated in drupal:9.5.0, removed in drupal:11.0.0. + // Replaced by $this->migrationPluginManager property access. + $rectorConfig->ruleWithConfiguration(MigrateSqlGetMigrationPluginManagerRector::class, [ + new DrupalIntroducedVersionConfiguration('11.0.0'), + ]); +}; diff --git a/config/drupal-11/drupal-11.1-deprecations.php b/config/drupal-11/drupal-11.1-deprecations.php new file mode 100644 index 000000000..7b8ecb489 --- /dev/null +++ b/config/drupal-11/drupal-11.1-deprecations.php @@ -0,0 +1,93 @@ +ruleWithConfiguration(PluginBaseIsConfigurableRector::class, [ + new DrupalIntroducedVersionConfiguration('11.1.0'), + ]); + + // https://www.drupal.org/node/3467559 + // AliasWhitelist and AliasWhitelistInterface deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // Replaced by AliasPrefixList and AliasPrefixListInterface. + // AliasManager::pathAliasWhitelistRebuild() deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // Replaced by pathAliasPrefixListRebuild(). + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'Drupal\path_alias\AliasWhitelist' => 'Drupal\path_alias\AliasPrefixList', + 'Drupal\path_alias\AliasWhitelistInterface' => 'Drupal\path_alias\AliasPrefixListInterface', + 'Drupal\Core\Routing\MatchingRouteNotFoundException' => 'Symfony\Component\Routing\Exception\ResourceNotFoundException', + ]); + $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ + new MethodToMethodWithCheckConfiguration('Drupal\path_alias\AliasManager', 'pathAliasWhitelistRebuild', 'pathAliasPrefixListRebuild', '11.1.0'), + ]); + + // https://www.drupal.org/node/3442009 + // https://www.drupal.org/node/3368812 (change record) + // ModuleHandlerInterface::writeCache() deprecated in drupal:11.1.0, removed in drupal:12.0.0. No replacement needed. + // ModuleHandlerInterface::getHookInfo() deprecated in drupal:11.1.0, removed in drupal:12.0.0. Replaced by []. + $rectorConfig->rule(RemoveModuleHandlerDeprecatedMethodsRector::class); + + // https://www.drupal.org/node/3575254 + // locale_config_batch_set_config_langcodes() and locale_config_batch_refresh_name() deprecated + // in drupal:11.1.0, removed in drupal:12.0.0. Renamed to update_default_config_langcodes + // and update_config_translations respectively. + $rectorConfig->ruleWithConfiguration(ReplaceLocaleConfigBatchFunctionsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.1.0'), + ]); + + // https://www.drupal.org/node/3417136 + // https://www.drupal.org/node/3461934 (change record) + // Updater::postInstall() and postInstallTasks() deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // The entire install-via-URL flow was eliminated; overrides are dead code. + $rectorConfig->rule(RemoveUpdaterPostInstallMethodsRector::class); + + // https://www.drupal.org/node/3196937 + // https://www.drupal.org/node/3473739 (change record) + // BlockContentTestBase::createBlockContentType() $values deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // Callers must pass an explicit array such as ['id' => 'basic'] instead of a plain string. + $rectorConfig->rule(BlockContentTestBaseStringToArrayRector::class); + + // https://www.drupal.org/node/3421202 + // https://www.drupal.org/node/3460567 (change record) + // movePointerTo() deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // Replaced by getSession()->getDriver()->mouseOver() with an XPath selector. + $rectorConfig->rule(MovePointerToMouseOverRector::class); + + // https://www.drupal.org/node/3432827 + // https://www.drupal.org/node/3442229 (change record) + // addMethodCall('addCachedDiscovery', ...) on plugin.cache_clearer deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // Replaced by the plugin_manager_cache_clear tag approach. + $rectorConfig->ruleWithConfiguration(ReplaceAddCachedDiscoveryMethodCallRector::class, [ + new DrupalIntroducedVersionConfiguration('11.1.0'), + ]); + + // https://www.drupal.org/node/3488176 + // drupal_common_theme() removed in drupal:11.1.0. + // Replaced by \Drupal\Core\Theme\ThemeCommonElements::commonElements(). + // https://www.drupal.org/node/3268441 + // image_filter_keyword() deprecated in drupal:11.1.0, removed in drupal:12.0.0. + // Replaced by \Drupal\Component\Utility\Image::getKeywordOffset(). + $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ + new FunctionToStaticConfiguration('11.1.0', 'drupal_common_theme', 'Drupal\Core\Theme\ThemeCommonElements', 'commonElements'), + new FunctionToStaticConfiguration('11.1.0', 'image_filter_keyword', 'Drupal\Component\Utility\Image', 'getKeywordOffset'), + ]); +}; diff --git a/config/drupal-11/drupal-11.2-deprecations.php b/config/drupal-11/drupal-11.2-deprecations.php new file mode 100644 index 000000000..13657d2e1 --- /dev/null +++ b/config/drupal-11/drupal-11.2-deprecations.php @@ -0,0 +1,260 @@ +ruleWithConfiguration(StatementPrefetchIteratorFetchColumnRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3500622 + // CacheBackendInterface::invalidateAll() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by deleteAll(). + $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ + new MethodToMethodWithCheckConfiguration('Drupal\Core\Cache\CacheBackendInterface', 'invalidateAll', 'deleteAll', '11.2.0'), + ]); + + // https://www.drupal.org/node/3504125 + // template_preprocess_*() functions deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by ThemePreprocess and DatePreprocess service methods. + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_time', 'Drupal\Core\Datetime\DatePreprocess', 'preprocessTime'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_datetime_form', 'Drupal\Core\Datetime\DatePreprocess', 'preprocessDatetimeForm'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_datetime_wrapper', 'Drupal\Core\Datetime\DatePreprocess', 'preprocessDatetimeWrapper'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_links', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessLinks'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_container', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessContainer'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_html', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessHtml'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_page', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessPage'), + ]); + + // https://www.drupal.org/node/3501136 + // template_preprocess() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // https://www.drupal.org/node/3522119 + // update_clear_update_disk_cache(), update_delete_file_if_stale(), + // _update_manager_cache_directory(), _update_manager_extract_directory(), + // and _update_manager_unique_identifier() deprecated in drupal:11.2.0, removed in drupal:13.0.0. + $rectorConfig->ruleWithConfiguration(FunctionCallRemovalRector::class, [ + new FunctionCallRemovalConfiguration('template_preprocess'), + new FunctionCallRemovalConfiguration('update_clear_update_disk_cache'), + new FunctionCallRemovalConfiguration('update_delete_file_if_stale'), + new FunctionCallRemovalConfiguration('_update_manager_cache_directory'), + new FunctionCallRemovalConfiguration('_update_manager_extract_directory'), + new FunctionCallRemovalConfiguration('_update_manager_unique_identifier'), + ]); + + // https://www.drupal.org/node/3528899 + // https://www.drupal.org/node/3550193 (change record) + // ModuleHandlerInterface::addModule() and addProfile() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // These methods are no-ops and can be removed. + $rectorConfig->rule(RemoveModuleHandlerAddModuleCallsRector::class); + + // https://www.drupal.org/node/3485084 + // https://www.drupal.org/node/3486781 (change record) + // HandlerBase::defineExtraOptions() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // No replacement — Drupal core never called it; any override is dead code. + $rectorConfig->rule(RemoveHandlerBaseDefineExtraOptionsRector::class); + + // https://www.drupal.org/node/3410938 + // drupal_requirements_severity() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by RequirementSeverity::maxSeverityFromRequirements(). + // https://www.drupal.org/node/3495966 + // https://www.drupal.org/node/3497049 (change record) + // entity_test_create_bundle() and entity_test_delete_bundle() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by EntityTestHelper::createBundle() and EntityTestHelper::deleteBundle(). + $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ + new FunctionToStaticConfiguration('11.2.0', 'drupal_requirements_severity', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'maxSeverityFromRequirements'), + new FunctionToStaticConfiguration('11.2.0', 'entity_test_create_bundle', 'Drupal\entity_test\EntityTestHelper', 'createBundle'), + new FunctionToStaticConfiguration('11.2.0', 'entity_test_delete_bundle', 'Drupal\entity_test\EntityTestHelper', 'deleteBundle'), + ]); + + // https://www.drupal.org/node/3489415 + // views_field_default_views_data() and _views_field_get_entity_type_storage() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by views.field_data_provider service methods. + // https://www.drupal.org/node/3489411 + // views_entity_field_label() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by entity_field.manager::getFieldLabels(). + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.2.0', 'views_field_default_views_data', 'views.field_data_provider', 'defaultFieldImplementation'), + new FunctionToServiceConfiguration('11.2.0', '_views_field_get_entity_type_storage', 'views.field_data_provider', 'getSqlStorageForField'), + new FunctionToServiceConfiguration('11.2.0', 'views_entity_field_label', 'entity_field.manager', 'getFieldLabels'), + ]); + + // https://www.drupal.org/node/3575841 + // REQUIREMENT_INFO/OK/WARNING/ERROR global constants deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by RequirementSeverity enum cases. + // https://www.drupal.org/node/3488133 + // LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by \Drupal::TRANSLATION_DEFAULT_SERVER_PATTERN. + $rectorConfig->ruleWithConfiguration(ConstantToClassConstantRector::class, [ + new ConstantToClassConfiguration('REQUIREMENT_INFO', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'Info', '11.2.0'), + new ConstantToClassConfiguration('REQUIREMENT_OK', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'OK', '11.2.0'), + new ConstantToClassConfiguration('REQUIREMENT_WARNING', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'Warning', '11.2.0'), + new ConstantToClassConfiguration('REQUIREMENT_ERROR', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'Error', '11.2.0'), + new ConstantToClassConfiguration('LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN', 'Drupal', 'TRANSLATION_DEFAULT_SERVER_PATTERN', '11.2.0'), + ]); + + // https://www.drupal.org/node/3473440 + // https://www.drupal.org/node/3474692 (change record) + // TwigNodeTrans 6th $tag constructor argument deprecated in twig/twig 3.12, removed in drupal:11.2.0. + // Drop the argument. + $rectorConfig->ruleWithConfiguration(RemoveTwigNodeTransTagArgumentRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3442810 + // https://www.drupal.org/node/3494472 (change record) + // Number::alphadecimalToInt(null/'') deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Both arguments always produced 0; replaced with literal 0. + $rectorConfig->ruleWithConfiguration(ReplaceAlphadecimalToIntNullRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3512254 + // https://www.drupal.org/node/3515272 (change record) + // #type 'fieldgroup' deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by 'fieldset'. + $rectorConfig->ruleWithConfiguration(ReplaceFieldgroupToFieldsetRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3525077 + // https://www.drupal.org/node/3488338 (change record) + // PDO::FETCH_* constants deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by \Drupal\Core\Database\Statement\FetchAs enum cases. + $rectorConfig->ruleWithConfiguration(ReplacePdoFetchConstantsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3574901 + // DateTimeRangeConstantsInterface::BOTH/START_DATE/END_DATE deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by DateTimeRangeDisplayOptions enum cases (->value). + // datetime_type_field_views_data_helper() deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by \Drupal::service('datetime.views_helper')->buildViewsData(). + $rectorConfig->ruleWithConfiguration(ReplaceDateTimeRangeConstantsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3494172 + // file_get_content_headers($file) deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by $file->getDownloadHeaders(). + $rectorConfig->ruleWithConfiguration(FunctionToFirstArgMethodRector::class, [ + new FunctionToFirstArgMethodConfiguration('11.2.0', 'file_get_content_headers', 'getDownloadHeaders'), + ]); + + // https://www.drupal.org/node/3518527 + // https://www.drupal.org/node/3518914 (change record) + // $_SESSION['key'] = $value deprecated in drupal:11.2.0. + // Replaced by \Drupal::request()->getSession()->set('key', $value). + $rectorConfig->ruleWithConfiguration(ReplaceSessionWritesWithRequestSessionRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3447794 + // https://www.drupal.org/node/3509245 (change record) + // editor_load($format_id) deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by entityTypeManager()->getStorage('editor')->load($format_id). + $rectorConfig->ruleWithConfiguration(ReplaceEditorLoadRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3571065 + // $entity->original magic property deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Read access replaced by getOriginal(); write access replaced by setOriginal($value). + $rectorConfig->ruleWithConfiguration(ReplaceEntityOriginalPropertyRector::class, [ + new DrupalIntroducedVersionConfiguration('11.2.0'), + ]); + + // https://www.drupal.org/node/3495943 + // #[StopProceduralHookScan] attribute renamed to #[ProceduralHookScanStop] in drupal:11.2.0. + $rectorConfig->rule(RenameStopProceduralHookScanRector::class); + + // https://www.drupal.org/node/3488572 + // Drupal\Core\Entity\Query\Sql\pgsql\* deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Moved to Drupal\pgsql\EntityQuery\*. + // https://www.drupal.org/node/3472008 + // Drupal\jsonapi\EventSubscriber\ResourceResponseValidator moved to jsonapi_response_validator submodule. + // https://www.drupal.org/node/3498916 + // Drupal\migrate_drupal\Plugin\migrate\source\ContentEntity/ContentEntityDeriver deprecated in drupal:11.2.0, + // removed in drupal:12.0.0. Moved to Drupal\migrate namespace. + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'Drupal\Core\Entity\Query\Sql\pgsql\QueryFactory' => 'Drupal\pgsql\EntityQuery\QueryFactory', + 'Drupal\Core\Entity\Query\Sql\pgsql\Condition' => 'Drupal\pgsql\EntityQuery\Condition', + 'Drupal\jsonapi\EventSubscriber\ResourceResponseValidator' => 'Drupal\jsonapi_response_validator\EventSubscriber\ResourceResponseValidator', + 'Drupal\migrate_drupal\Plugin\migrate\source\ContentEntity' => 'Drupal\migrate\Plugin\migrate\source\ContentEntity', + 'Drupal\migrate_drupal\Plugin\migrate\source\ContentEntityDeriver' => 'Drupal\migrate\Plugin\migrate\source\ContentEntityDeriver', + ]); + + // https://www.drupal.org/node/3511123 + // https://www.drupal.org/node/3511149 (change record) + // CacheTagChecksumCount and CacheTagIsValidCount deprecated in drupal:11.2.0, removed in drupal:12.0.0. No replacement. + $rectorConfig->rule(RemoveCacheTagChecksumAssertionsRector::class); + + // https://www.drupal.org/node/3506931 + // https://www.drupal.org/node/3511287 (change record) + // Connection::createConnectionOptionsFromUrl() $root parameter deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Pass NULL explicitly instead of the root path argument. + $rectorConfig->rule(RemoveRootFromCreateConnectionOptionsFromUrlRector::class); + + // https://www.drupal.org/node/3410939 + // SystemManager::REQUIREMENT_* deprecated in drupal:11.2.0, removed in drupal:12.0.0. + // Replaced by \Drupal\Core\Extension\Requirement\RequirementSeverity enum cases. + $rectorConfig->ruleWithConfiguration(ClassConstantToClassConstantRector::class, [ + new ClassConstantToClassConstantConfiguration( + 'Drupal\system\SystemManager', + 'REQUIREMENT_OK', + 'Drupal\Core\Extension\Requirement\RequirementSeverity', + 'OK', + '11.2.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\system\SystemManager', + 'REQUIREMENT_WARNING', + 'Drupal\Core\Extension\Requirement\RequirementSeverity', + 'Warning', + '11.2.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\system\SystemManager', + 'REQUIREMENT_ERROR', + 'Drupal\Core\Extension\Requirement\RequirementSeverity', + 'Error', + '11.2.0', + ), + ]); +}; diff --git a/config/drupal-11/drupal-11.3-deprecations.php b/config/drupal-11/drupal-11.3-deprecations.php new file mode 100644 index 000000000..2401edcc1 --- /dev/null +++ b/config/drupal-11/drupal-11.3-deprecations.php @@ -0,0 +1,213 @@ +ruleWithConfiguration(ReplaceCommentManagerGetCountNewCommentsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3536431 + // https://www.drupal.org/node/3536432 (change record) + // ModuleHandler::loadAllIncludes() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by an explicit foreach over getModuleList() + loadInclude(). + $rectorConfig->rule(LoadAllIncludesRector::class); + + // https://www.drupal.org/node/3396062 + // https://www.drupal.org/node/3519187 (change record) + // NodeStorage::revisionIds() and userRevisionIds() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by equivalent entity queries. + $rectorConfig->rule(NodeStorageDeprecatedMethodsRector::class); + + // https://www.drupal.org/node/3533083 + // node_mass_update() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by \Drupal\node\NodeBulkUpdate::process(). + // https://www.drupal.org/node/3547356 + // twig_render_template() deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by \Drupal::service(TwigThemeEngine::class)->renderTemplate(). + // twig_extension() is handled by ReplaceTwigExtensionRector below. + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.3.0', 'node_mass_update', 'Drupal\node\NodeBulkUpdate', 'process', true), + new FunctionToServiceConfiguration('11.3.0', 'twig_render_template', 'Drupal\Core\Template\TwigThemeEngine', 'renderTemplate'), + ]); + + // https://www.drupal.org/node/3504125 + // template_preprocess_layout() deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by \Drupal\layout_discovery\Hook\LayoutDiscoveryThemeHooks::preprocessLayout(). + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.3.0', 'template_preprocess_layout', 'Drupal\layout_discovery\Hook\LayoutDiscoveryThemeHooks', 'preprocessLayout', true), + ]); + + // https://www.drupal.org/node/1685492 + // twig_extension() deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by the '.html.twig' string literal. + $rectorConfig->ruleWithConfiguration(ReplaceTwigExtensionRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + $rectorConfig->ruleWithConfiguration(ReplaceNodeModuleProceduralFunctionsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3535528 + // block_content_add_body_field() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // The body field is now added via config. + $rectorConfig->ruleWithConfiguration(FunctionCallRemovalRector::class, [ + new FunctionCallRemovalConfiguration('block_content_add_body_field'), + ]); + + // https://www.drupal.org/node/2010202 + // comment_uri($comment) deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by $comment->permalink(). + // https://www.drupal.org/node/3531945 + // node_type_get_description($node_type) deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by $node_type->getDescription(). + $rectorConfig->ruleWithConfiguration(FunctionToFirstArgMethodRector::class, [ + new FunctionToFirstArgMethodConfiguration('11.3.0', 'comment_uri', 'permalink'), + new FunctionToFirstArgMethodConfiguration('11.3.0', 'node_type_get_description', 'getDescription'), + ]); + + // https://www.drupal.org/node/3038908 + // https://www.drupal.org/node/3038909 (change record) + // node_access_view_all_nodes() deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by entityTypeManager()->getAccessControlHandler('node')->checkAllGrants(). + // drupal_static_reset('node_access_view_all_nodes') replaced by node.view_all_nodes_memory_cache->deleteAll(). + $rectorConfig->ruleWithConfiguration(ReplaceNodeAccessViewAllNodesRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3548329 + // responsive_image_* functions deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by \Drupal::service(ResponsiveImageBuilder::class)->method() calls. + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.3.0', '_responsive_image_build_source_attributes', 'Drupal\responsive_image\ResponsiveImageBuilder', 'buildSourceAttributes'), + new FunctionToServiceConfiguration('11.3.0', 'responsive_image_get_image_dimensions', 'Drupal\responsive_image\ResponsiveImageBuilder', 'getImageDimensions'), + new FunctionToServiceConfiguration('11.3.0', 'responsive_image_get_mime_type', 'Drupal\responsive_image\ResponsiveImageBuilder', 'getMimeType'), + new FunctionToServiceConfiguration('11.3.0', '_responsive_image_image_style_url', 'Drupal\responsive_image\ResponsiveImageBuilder', 'getImageStyleUrl'), + ]); + + // https://www.drupal.org/node/3489266 + // https://www.drupal.org/node/3516778 (change record) + // node_add_body_field() deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by $this->createBodyField() from BodyFieldCreationTrait. + $rectorConfig->ruleWithConfiguration(ReplaceNodeAddBodyFieldRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3513856 + // https://www.drupal.org/node/3513877 (change record) + // UserSession::$name property read deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by getAccountName(). + $rectorConfig->ruleWithConfiguration(ReplaceUserSessionNamePropertyRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3534092 + // file_system_settings_submit() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by \Drupal\file\Hook\FileHooks::settingsSubmit(). + // https://www.drupal.org/node/3534089 + // https://www.drupal.org/node/3534091 (change record) + // file_managed_file_submit() deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by \Drupal\file\Element\ManagedFile::submit(). + $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ + new FunctionToStaticConfiguration('11.3.0', 'file_system_settings_submit', 'Drupal\file\Hook\FileHooks', 'settingsSubmit'), + new FunctionToStaticConfiguration('11.3.0', 'file_managed_file_submit', 'Drupal\file\Element\ManagedFile', 'submit'), + ]); + + // https://www.drupal.org/node/3534089 + // https://www.drupal.org/node/3534091 (change record) + // 'file_managed_file_submit' string callback deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // Replaced by [\Drupal\file\Element\ManagedFile::class, 'submit'] array callable. + $rectorConfig->ruleWithConfiguration(FileManagedFileSubmitRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3495601 + // JSONAPI_FILTER_AMONG_* global constants deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by \Drupal\jsonapi\JsonApiFilter::AMONG_* class constants. + $rectorConfig->ruleWithConfiguration(ConstantToClassConstantRector::class, [ + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_ALL', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_ALL', '11.3.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_PUBLISHED', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_PUBLISHED', '11.3.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_ENABLED', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_ENABLED', '11.3.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_OWN', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_OWN', '11.3.0'), + ]); + + // https://www.drupal.org/node/3538277 + // https://www.drupal.org/node/3538666 (change record) + // DRUPAL_DISABLED/OPTIONAL/REQUIRED constants (and integers 0/1/2) in setPreviewMode() + // deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by NodePreviewMode enum cases. + $rectorConfig->ruleWithConfiguration(ReplaceNodeSetPreviewModeRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3530461 + // https://www.drupal.org/node/3530869 (change record) + // FileSystemInterface::basename() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by PHP native basename(). + $rectorConfig->ruleWithConfiguration(FileSystemBasenameToNativeRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3526515 + // https://www.drupal.org/node/3529500 (change record) + // Error::currentErrorHandler() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by PHP built-in get_error_handler(). + $rectorConfig->ruleWithConfiguration(ErrorCurrentErrorHandlerRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3573896 + // theme_get_setting() and _system_default_theme_features() deprecated in drupal:11.3.0, removed in drupal:13.0.0. + // Replaced by ThemeSettingsProvider service. + $rectorConfig->ruleWithConfiguration(ReplaceThemeGetSettingRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3522513 + // https://www.drupal.org/node/3511287 (change record) + // Database::convertDbUrlToConnectionInfo($url, $root, ...) deprecated in drupal:11.3.0, removed in drupal:12.0.0. + // The $root parameter is obsolete; remove it (shift any $include_test_drivers arg left). + $rectorConfig->ruleWithConfiguration(RemoveRootFromConvertDbUrlRector::class, [ + new DrupalIntroducedVersionConfiguration('11.3.0'), + ]); + + // https://www.drupal.org/node/3551450 + // workspaces.association service and WorkspaceAssociationInterface renamed in drupal:11.3.0. + // Replaced by workspaces.tracker and WorkspaceTrackerInterface. + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'Drupal\workspaces\WorkspaceAssociationInterface' => 'Drupal\workspaces\WorkspaceTrackerInterface', + 'Drupal\workspaces\WorkspaceAssociation' => 'Drupal\workspaces\WorkspaceTracker', + ]); +}; diff --git a/config/drupal-11/drupal-11.4-deprecations.php b/config/drupal-11/drupal-11.4-deprecations.php new file mode 100644 index 000000000..c020f0c22 --- /dev/null +++ b/config/drupal-11/drupal-11.4-deprecations.php @@ -0,0 +1,449 @@ +ruleWithConfiguration(ViewsPluginHandlerManagerRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3578055 + // node_access_grants() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal\node\NodeGrantsHelper::nodeAccessGrants(). + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.4.0', 'node_access_grants', 'Drupal\node\NodeGrantsHelper', 'nodeAccessGrants', true), + ]); + + // https://www.drupal.org/node/3533299 + // https://www.drupal.org/node/3534610 (change record) + // node_access_rebuild() and node_access_needs_rebuild() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal\node\NodeAccessRebuild service. + $rectorConfig->ruleWithConfiguration(NodeAccessRebuildFunctionsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/2536594 + // https://www.drupal.org/node/3035368 (change record) + // filter_formats(), filter_get_roles_by_format(), filter_get_formats_by_role(), + // filter_default_format(), and filter_fallback_format() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal\filter\FilterFormatRepositoryInterface service. + $rectorConfig->ruleWithConfiguration(FilterFormatFunctionsToServiceRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3568124 + // https://www.drupal.org/node/3566774 (change record) + // media_filter_format_edit_form_validate() deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by \Drupal\media\Hook\MediaHooks::formatEditFormValidate(). + $rectorConfig->ruleWithConfiguration(MediaFilterFormatEditFormValidateRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3226806 + // https://www.drupal.org/node/3566774 (change record) + // _filter_autop(), _filter_html_escape(), and _filter_html_image_secure_process() + // deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by plugin.manager.filter createInstance() chain. + $rectorConfig->ruleWithConfiguration(DeprecatedFilterFunctionsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3570851 + // SessionManager::delete() deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by \Drupal\Core\Session\UserSessionRepositoryInterface::deleteAll(). + $rectorConfig->ruleWithConfiguration(ReplaceSessionManagerDeleteRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3550054 + // CommentItemInterface::FORM_BELOW and FORM_SEPARATE_PAGE deprecated in 11.4.0, + // removed in 13.0.0. Replaced by FormLocation enum cases. + // https://www.drupal.org/node/3547352 + // CommentItemInterface::HIDDEN/CLOSED/OPEN and CommentInterface::ANONYMOUS_* + // deprecated in 11.4.0, removed in 13.0.0. Replaced by CommentingStatus and + // AnonymousContact enum cases. + $rectorConfig->ruleWithConfiguration(ClassConstantToClassConstantRector::class, [ + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'FORM_BELOW', + 'Drupal\comment\FormLocation', + 'Below', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'FORM_SEPARATE_PAGE', + 'Drupal\comment\FormLocation', + 'SeparatePage', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'HIDDEN', + 'Drupal\comment\CommentingStatus', + 'Hidden', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'CLOSED', + 'Drupal\comment\CommentingStatus', + 'Closed', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'OPEN', + 'Drupal\comment\CommentingStatus', + 'Open', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\CommentInterface', + 'ANONYMOUS_MAYNOT_CONTACT', + 'Drupal\comment\AnonymousContact', + 'Forbidden', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\CommentInterface', + 'ANONYMOUS_MAY_CONTACT', + 'Drupal\comment\AnonymousContact', + 'Allowed', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\CommentInterface', + 'ANONYMOUS_MUST_CONTACT', + 'Drupal\comment\AnonymousContact', + 'Required', + '11.4.0', + ), + ]); + + // https://www.drupal.org/node/3574727 + // language_configuration_element_submit() deprecated in 11.4.0, removed in 13.0.0. + // Replaced by LanguageConfiguration::submit(). + // language_process_language_select() deprecated in 11.4.0, removed in 12.0.0. + // Replaced by LanguageHooks::processLanguageSelect() via the service container. + // https://www.drupal.org/node/3566792 + // ckeditor5_filter_format_edit_form_submit() and _update_ckeditor5_html_filter() + // deprecated in 11.4.0, removed in 12.0.0. Replaced by Ckeditor5Hooks service. + // https://www.drupal.org/node/3560398 + // _dblog_get_message_types() and dblog_filters() deprecated in 11.4.0, + // removed in 13.0.0. Replaced by DbLogFilters service. + // https://www.drupal.org/node/3566888 + // contact_user_profile_form_submit() and contact_form_user_admin_settings_submit() + // deprecated in 11.4.0, removed in 12.0.0. Replaced by ContactFormHooks service. + // https://www.drupal.org/node/3548571 + // content_translation_* functions deprecated in 11.4.0, removed in 12.0.0/13.0.0. + // https://www.drupal.org/node/3572339 + // locale_translation_batch_update_build() and locale_translation_batch_fetch_build() + // deprecated in 11.4.0, removed in 13.0.0. Replaced by LocaleFetch service. + // https://www.drupal.org/node/3569328 + // locale.translation.inc functions deprecated in 11.4.0, removed in 13.0.0. + // https://www.drupal.org/node/3571400 + // menu_ui.module procedural functions deprecated in 11.4.0, removed in 12.0.0/13.0.0. + // https://www.drupal.org/node/3568387 + // text_summary() deprecated in 11.4.0, removed in 13.0.0. Replaced by TextSummary service. + // https://www.drupal.org/node/3582107 + // user_form_process_password_confirm() deprecated in 11.4.0, removed in 13.0.0. + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.4.0', 'language_process_language_select', 'Drupal\language\Hook\LanguageHooks', 'processLanguageSelect'), + new FunctionToServiceConfiguration('11.4.0', 'ckeditor5_filter_format_edit_form_submit', 'Drupal\ckeditor5\Hook\Ckeditor5Hooks', 'filterFormatEditFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', '_update_ckeditor5_html_filter', 'Drupal\ckeditor5\Hook\Ckeditor5Hooks', 'updateCkeditor5HtmlFilter'), + new FunctionToServiceConfiguration('11.4.0', '_dblog_get_message_types', 'Drupal\dblog\DbLogFilters', 'getMessageTypes'), + new FunctionToServiceConfiguration('11.4.0', 'dblog_filters', 'Drupal\dblog\DbLogFilters', 'filters'), + new FunctionToServiceConfiguration('11.4.0', 'contact_user_profile_form_submit', 'Drupal\contact\Hook\ContactFormHooks', 'profileFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', 'contact_form_user_admin_settings_submit', 'Drupal\contact\Hook\ContactFormHooks', 'userAdminSettingsSubmit'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_translate_access', 'content_translation.manager', 'access'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_enable_widget', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'getWidget'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_language_configuration_element_process', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'configElementProcess'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_language_configuration_element_validate', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'configElementValidate'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_language_configuration_element_submit', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'configElementSubmit'), + new FunctionToServiceConfiguration('11.4.0', '_content_translation_install_field_storage_definitions', 'Drupal\content_translation\Hook\ContentTranslationHooks', 'installFieldStorageDefinitions'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_batch_update_build', 'Drupal\locale\LocaleFetch', 'batchUpdateBuild'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_batch_fetch_build', 'Drupal\locale\LocaleFetch', 'batchFetchBuild'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_get_projects', 'locale.project', 'getProjects'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_clear_cache_projects', 'locale.project', 'resetCache'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_load_sources', 'Drupal\locale\LocaleSource', 'loadSources'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_build_sources', 'Drupal\locale\LocaleSource', 'buildSources'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_source_check_file', 'Drupal\locale\LocaleSource', 'sourceCheckFile'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_source_build', 'Drupal\locale\LocaleSource', 'sourceBuild'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_build_server_pattern', 'Drupal\locale\LocaleSource', 'buildServerPattern'), + new FunctionToServiceConfiguration('11.4.0', '_menu_ui_node_save', 'Drupal\menu_ui\MenuUiUtility', 'menuUiNodeSave'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_get_menu_link_defaults', 'Drupal\menu_ui\MenuUiUtility', 'getMenuLinkDefaults'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_node_builder', 'Drupal\menu_ui\Hook\MenuUiHooks', 'nodeBuilder'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_form_node_form_submit', 'Drupal\menu_ui\Hook\MenuUiHooks', 'formNodeFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_form_node_type_form_validate', 'Drupal\menu_ui\Hook\MenuUiHooks', 'formNodeTypeFormValidate'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_form_node_type_form_builder', 'Drupal\menu_ui\Hook\MenuUiHooks', 'formNodeTypeFormBuilder'), + new FunctionToServiceConfiguration('11.4.0', 'text_summary', 'Drupal\text\TextSummary', 'generate'), + new FunctionToServiceConfiguration('11.4.0', 'user_form_process_password_confirm', 'Drupal\user\Hook\UserThemeHooks', 'processPasswordConfirm'), + // https://www.drupal.org/node/3567163 + // https://www.drupal.org/node/3566774 (change record) + // field_ui_form_manage_field_form_submit() deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by \Drupal\field_ui\Hook\FieldUiHooks::manageFieldFormSubmit(). + new FunctionToServiceConfiguration('11.4.0', 'field_ui_form_manage_field_form_submit', 'Drupal\field_ui\Hook\FieldUiHooks', 'manageFieldFormSubmit', true), + ]); + + // https://www.drupal.org/node/3035340 + // views_ui_contextual_links_suppress*() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // These are no-ops and can be removed. + // https://www.drupal.org/node/3566768 + // https://www.drupal.org/node/3566774 (change record) + // automated_cron_settings_submit() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Config saving is now handled automatically via #config_target on the interval element. + // https://www.drupal.org/node/3566783 + // block_theme_initialize() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Logic moved to protected BlockHooks::themeInitialize(); external callers must drop the call. + $rectorConfig->ruleWithConfiguration(FunctionCallRemovalRector::class, [ + new FunctionCallRemovalConfiguration('views_ui_contextual_links_suppress'), + new FunctionCallRemovalConfiguration('views_ui_contextual_links_suppress_push'), + new FunctionCallRemovalConfiguration('views_ui_contextual_links_suppress_pop'), + new FunctionCallRemovalConfiguration('automated_cron_settings_submit'), + new FunctionCallRemovalConfiguration('block_theme_initialize'), + new FunctionCallRemovalConfiguration('syslog_facility_list'), + new FunctionCallRemovalConfiguration('syslog_logging_settings_submit'), + new FunctionCallRemovalConfiguration('taxonomy_build_node_index'), + new FunctionCallRemovalConfiguration('taxonomy_delete_node_index'), + ]); + + // https://www.drupal.org/node/2667040 + // https://www.drupal.org/node/3575062 (change record) + // EntityTypeInterface::setUriCallback() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Use link templates or a route provider instead. + $rectorConfig->rule(RemoveSetUriCallbackRector::class); + + // https://www.drupal.org/node/3498026 + // https://www.drupal.org/node/3579527 (change record) + // RecipeRunner::installModule() deprecated in drupal:11.4.0. Use installModules() with an array. + $rectorConfig->ruleWithConfiguration(ReplaceRecipeRunnerInstallModuleRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3184242 + // https://www.drupal.org/node/3526344 (change record) + // system.performance css.gzip and js.gzip config keys deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by css.compress and js.compress. + $rectorConfig->ruleWithConfiguration(ReplaceSystemPerformanceGzipKeyRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3564937 + // https://www.drupal.org/node/3564958 (change record) + // CachePluginBase::getRowCacheKeys() and getRowId() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Remove array items whose value is one of these calls. + $rectorConfig->rule(RemoveViewsRowCacheKeysRector::class); + + // https://www.drupal.org/node/3576556 + // https://www.drupal.org/node/3576855 (change record) + // CachePluginBase::cacheExpire() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Subclass overrides are dead code; remove them. + $rectorConfig->rule(RemoveCacheExpireOverrideRector::class); + + // https://www.drupal.org/node/3347842 + // https://www.drupal.org/node/3348180 (change record) + // trustData() deprecated in drupal:11.4.0, removed in drupal:13.0.0. Remove from fluent chains. + // Config::save($has_trusted_data) boolean arg deprecated in drupal:11.4.0, removed in drupal:13.0.0. + $rectorConfig->ruleWithConfiguration(RemoveTrustDataCallRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + $rectorConfig->ruleWithConfiguration(RemoveConfigSaveTrustedDataArgRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3093118 + // https://www.drupal.org/node/3554139 (change record) + // LinkWidget::validateTitleElement() deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Validation is now handled by LinkTitleRequiredConstraint on the LinkItem field type. + $rectorConfig->rule(RemoveLinkWidgetValidateTitleElementRector::class); + + // https://www.drupal.org/node/3566768 + // https://www.drupal.org/node/3566774 (change record) + // $form['#submit'][] = 'automated_cron_settings_submit' deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Config saving is now handled automatically via #config_target on the interval element. + $rectorConfig->rule(RemoveAutomatedCronSubmitHandlerRector::class); + + // https://www.drupal.org/node/3572243 + // https://www.drupal.org/node/3572594 (change record) + // views_view_is_enabled(), views_view_is_disabled(), views_enable_view(), + // views_disable_view(), views_get_view_result() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by OO equivalents on the view object or Views::getViewResult(). + $rectorConfig->ruleWithConfiguration(ReplaceViewsProceduralFunctionsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3557461 + // https://www.drupal.org/node/3557464 (change record) + // EntityTypeInterface::getOriginalClass() deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by getDecoratedClasses()[0]. + $rectorConfig->ruleWithConfiguration(GetOriginalClassToGetDecoratedClassesRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3566801 + // https://www.drupal.org/node/3566814 (change record) + // getEntityTypeIdKeyType() === 'integer', entityTypeSupportsComments(), and hasIntegerId($entityType) + // deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by EntityTypeInterface::hasIntegerId() called on the entity type object. + $rectorConfig->ruleWithConfiguration(UseEntityTypeHasIntegerIdRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3568144 + // editor_filter_xss() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal::service('element.editor')->filterXss(). + // https://www.drupal.org/node/3570917 + // editor_image_upload_settings_form() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal::service(EditorImageUploadSettings::class)->getForm(). + // https://www.drupal.org/node/2907780 + // field_purge_batch() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal::service(FieldPurger::class)->purgeBatch(). + // https://www.drupal.org/node/3566774 + // _media_library_media_type_form_submit() and _media_library_views_form_media_library_after_build() + // deprecated in drupal:11.4.0, removed in drupal:12.0.0. Replaced by MediaLibraryHooks service. + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.4.0', 'editor_filter_xss', 'element.editor', 'filterXss'), + new FunctionToServiceConfiguration('11.4.0', 'editor_image_upload_settings_form', 'Drupal\editor\EditorImageUploadSettings', 'getForm'), + new FunctionToServiceConfiguration('11.4.0', 'field_purge_batch', 'Drupal\Core\Field\FieldPurger', 'purgeBatch'), + new FunctionToServiceConfiguration('11.4.0', '_media_library_media_type_form_submit', 'Drupal\media_library\Hook\MediaLibraryHooks', 'mediaTypeFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', '_media_library_views_form_media_library_after_build', 'Drupal\media_library\Hook\MediaLibraryHooks', 'viewsFormAfterBuild'), + ]); + + // https://www.drupal.org/node/3566774 + // _media_library_configure_form_display() and _media_library_configure_view_display() + // deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by MediaLibraryDisplayManager static methods. + $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ + new FunctionToStaticConfiguration('11.4.0', '_media_library_configure_form_display', 'Drupal\media_library\MediaLibraryDisplayManager', 'configureFormDisplay'), + new FunctionToStaticConfiguration('11.4.0', '_media_library_configure_view_display', 'Drupal\media_library\MediaLibraryDisplayManager', 'configureViewDisplay'), + ]); + + // https://www.drupal.org/node/3574727 + // language_configuration_element_submit() deprecated in 11.4.0, removed in 13.0.0. + // Replaced by LanguageConfiguration::submit(). + // https://www.drupal.org/node/3566774 + // views_ui/admin.inc static trait functions deprecated in 11.4.0, removed in 13.0.0. + $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ + new FunctionToStaticConfiguration('11.4.0', 'language_configuration_element_submit', 'Drupal\language\Element\LanguageConfiguration', 'submit'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_form_button_was_clicked', 'Drupal\views\ViewsFormHelperTrait', 'formButtonWasClicked'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_add_limited_validation', 'Drupal\views\ViewsFormAjaxHelperTrait', 'addLimitedValidation'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_add_ajax_wrapper', 'Drupal\views\ViewsFormAjaxHelperTrait', 'addAjaxWrapper'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_nojs_submit', 'Drupal\views\ViewsFormAjaxHelperTrait', 'noJsSubmit'), + ]); + + // https://www.drupal.org/node/3568087 + // contextual_links_to_id() and contextual_id_to_links() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by ContextualLinksSerializer service. + // https://www.drupal.org/node/3567618 + // image_path_flush() and image_style_options() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by ImageDerivativeUtilities service. + // https://www.drupal.org/node/3577675 + // locale_translate_get_interface_translation_files() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by LocaleFileManager::getInterfaceTranslationFiles(). + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.4.0', '_contextual_links_to_id', 'Drupal\contextual\ContextualLinksSerializer', 'linksToId'), + new FunctionToServiceConfiguration('11.4.0', '_contextual_id_to_links', 'Drupal\contextual\ContextualLinksSerializer', 'idToLinks'), + new FunctionToServiceConfiguration('11.4.0', 'image_path_flush', 'Drupal\image\ImageDerivativeUtilities', 'pathFlush'), + new FunctionToServiceConfiguration('11.4.0', 'image_style_options', 'Drupal\image\ImageDerivativeUtilities', 'styleOptions'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translate_get_interface_translation_files', 'Drupal\locale\File\LocaleFileManager', 'getInterfaceTranslationFiles'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_http_check', 'Drupal\locale\File\LocaleFileManager', 'checkRemoteFileStatus'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translate_delete_translation_files', 'Drupal\locale\File\LocaleFileManager', 'deleteTranslationFiles'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_download_source', 'Drupal\locale\File\LocaleFileManager', 'downloadTranslationSource'), + ]); + + // https://www.drupal.org/node/2571679 + // https://www.drupal.org/node/3382344 (change record) + // views_add_contextual_links() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal\views\ContextualLinksHelper::addLinks() service call. + $rectorConfig->ruleWithConfiguration(FunctionToServiceRector::class, [ + new FunctionToServiceConfiguration('11.4.0', 'views_add_contextual_links', 'Drupal\views\ContextualLinksHelper', 'addLinks', true), + ]); + + // https://www.drupal.org/node/3567619 + // IMAGE_DERIVATIVE_TOKEN deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by \Drupal\image\ImageStyleInterface::TOKEN. + $rectorConfig->ruleWithConfiguration(ConstantToClassConstantRector::class, [ + new ConstantToClassConfiguration('IMAGE_DERIVATIVE_TOKEN', 'Drupal\image\ImageStyleInterface', 'TOKEN', '11.4.0'), + ]); + + // https://www.drupal.org/node/2940605 + // EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by literal 20. + $rectorConfig->ruleWithConfiguration(ReplaceEntityReferenceRecursiveLimitRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/3015812 + // system_region_list() and system_default_region() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by Theme object methods via \Drupal::service('theme_handler')->getTheme(). + $rectorConfig->ruleWithConfiguration(SystemRegionFunctionsRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); + + // https://www.drupal.org/node/455724 + // https://www.drupal.org/node/3588040 (change record) + // check_markup() deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by a processed_text render array. + $rectorConfig->rule(CheckMarkupToProcessedTextRector::class); + + // https://www.drupal.org/node/3505370 + // https://www.drupal.org/node/3567879 (change record) + // FilterInterface::tips() $long parameter deprecated in drupal:11.4.0, removed in drupal:12.0.0. + $rectorConfig->rule(RemoveFilterTipsLongParamRector::class); + + // https://www.drupal.org/node/3571172 + // https://www.drupal.org/node/3566774 (change record) + // system_sort_themes() string callback deprecated in drupal:11.4.0, removed in drupal:12.0.0. + // Replaced by an inline static closure. + $rectorConfig->rule(SystemSortThemesRector::class); + + // https://www.drupal.org/node/3037031 + // locale_translation_flush_projects(), locale_translation_build_projects(), locale_translation_check_projects(), + // and locale_translation_check_projects_local() deprecated in drupal:11.4.0, removed in drupal:13.0.0. + // Replaced by LocaleProjectRepository and LocaleProjectChecker service methods. + $rectorConfig->ruleWithConfiguration(LocaleCompareIncToServiceRector::class, [ + new DrupalIntroducedVersionConfiguration('11.4.0'), + ]); +}; diff --git a/config/drupal-8/drupal-8.0-deprecations.php b/config/drupal-8/drupal-8.0-deprecations.php index ac787c6a8..bed90f66c 100644 --- a/config/drupal-8/drupal-8.0-deprecations.php +++ b/config/drupal-8/drupal-8.0-deprecations.php @@ -56,7 +56,7 @@ $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ // https://www.drupal.org/node/2614344 - new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityInterface', 'urlInfo', 'toUrl'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityInterface', 'urlInfo', 'toUrl', '8.0.0'), ]); $rectorConfig->ruleWithConfiguration(EntityLoadRector::class, [ diff --git a/config/drupal-8/drupal-8.2-deprecations.php b/config/drupal-8/drupal-8.2-deprecations.php index 439256bee..6094a0a5f 100644 --- a/config/drupal-8/drupal-8.2-deprecations.php +++ b/config/drupal-8/drupal-8.2-deprecations.php @@ -10,7 +10,7 @@ $rectorConfig->singleton(AddCommentService::class, function () { return new AddCommentService(); }); - // https://www.drupal.org/node/2802569 + // https://www.drupal.org/node/2418133 $rectorConfig->ruleWithConfiguration(FunctionToStaticRector::class, [ new DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration( '8.2.0', diff --git a/config/drupal-8/drupal-8.5-deprecations.php b/config/drupal-8/drupal-8.5-deprecations.php index f04301792..6ed1a5517 100644 --- a/config/drupal-8/drupal-8.5-deprecations.php +++ b/config/drupal-8/drupal-8.5-deprecations.php @@ -20,8 +20,8 @@ * See https://www.drupal.org/node/2912980 for change record. */ $rectorConfig->ruleWithConfiguration(ConstantToClassConstantRector::class, [ - new ConstantToClassConfiguration('DATETIME_DATE_STORAGE_FORMAT', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'DATE_STORAGE_FORMAT'), - new ConstantToClassConfiguration('DATETIME_DATETIME_STORAGE_FORMAT', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'DATETIME_STORAGE_FORMAT'), - new ConstantToClassConfiguration('DATETIME_STORAGE_TIMEZONE', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'STORAGE_TIMEZONE'), + new ConstantToClassConfiguration('DATETIME_DATE_STORAGE_FORMAT', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'DATE_STORAGE_FORMAT', '8.5.0'), + new ConstantToClassConfiguration('DATETIME_DATETIME_STORAGE_FORMAT', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'DATETIME_STORAGE_FORMAT', '8.5.0'), + new ConstantToClassConfiguration('DATETIME_STORAGE_TIMEZONE', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'STORAGE_TIMEZONE', '8.5.0'), ]); }; diff --git a/config/drupal-8/drupal-8.7-deprecations.php b/config/drupal-8/drupal-8.7-deprecations.php index 1942122ba..9e115eba0 100644 --- a/config/drupal-8/drupal-8.7-deprecations.php +++ b/config/drupal-8/drupal-8.7-deprecations.php @@ -25,22 +25,22 @@ * * No change record found. */ - $constantToClassFileCreateDirectory = new ConstantToClassConfiguration('FILE_CREATE_DIRECTORY', 'Drupal\Core\File\FileSystemInterface', 'CREATE_DIRECTORY'); + $constantToClassFileCreateDirectory = new ConstantToClassConfiguration('FILE_CREATE_DIRECTORY', 'Drupal\Core\File\FileSystemInterface', 'CREATE_DIRECTORY', '8.7.0'); /** * Replaces deprecated FILE_EXISTS_REPLACE, FILE_EXISTS_RENAME constant use. * * See https://www.drupal.org/node/3006851 for change record. */ - $constantToClassFileExistReplace = new ConstantToClassConfiguration('FILE_EXISTS_REPLACE', 'Drupal\Core\File\FileSystemInterface', 'EXISTS_REPLACE'); - $constantToClassFileExistsRename = new ConstantToClassConfiguration('FILE_EXISTS_RENAME', 'Drupal\Core\File\FileSystemInterface', 'EXISTS_RENAME'); + $constantToClassFileExistReplace = new ConstantToClassConfiguration('FILE_EXISTS_REPLACE', 'Drupal\Core\File\FileSystemInterface', 'EXISTS_REPLACE', '8.7.0'); + $constantToClassFileExistsRename = new ConstantToClassConfiguration('FILE_EXISTS_RENAME', 'Drupal\Core\File\FileSystemInterface', 'EXISTS_RENAME', '8.7.0'); /** * Replaces deprecated FILE_MODIFY_PERMISSIONS constant use. * * No change record found. */ - $constantToClassFileModifyPermissions = new ConstantToClassConfiguration('FILE_MODIFY_PERMISSIONS', 'Drupal\Core\File\FileSystemInterface', 'MODIFY_PERMISSIONS'); + $constantToClassFileModifyPermissions = new ConstantToClassConfiguration('FILE_MODIFY_PERMISSIONS', 'Drupal\Core\File\FileSystemInterface', 'MODIFY_PERMISSIONS', '8.7.0'); $rectorConfig->ruleWithConfiguration(ConstantToClassConstantRector::class, [ $constantToClassFileCreateDirectory, diff --git a/config/drupal-8/drupal-8.8-deprecations.php b/config/drupal-8/drupal-8.8-deprecations.php index ad10ee458..6a42e81f4 100644 --- a/config/drupal-8/drupal-8.8-deprecations.php +++ b/config/drupal-8/drupal-8.8-deprecations.php @@ -43,7 +43,7 @@ $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ // https://www.drupal.org/node/3075567 - new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityTypeInterface', 'getLowercaseLabel', 'getSingularLabel'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityTypeInterface', 'getLowercaseLabel', 'getSingularLabel', '8.8.0'), ]); // https://www.drupal.org/node/3083055 diff --git a/config/drupal-9/drupal-9.1-deprecations.php b/config/drupal-9/drupal-9.1-deprecations.php index 536fdb495..8878b917d 100644 --- a/config/drupal-9/drupal-9.1-deprecations.php +++ b/config/drupal-9/drupal-9.1-deprecations.php @@ -117,18 +117,21 @@ 'ROUTE_NAME', 'Drupal\Core\Routing\RouteObjectInterface', 'ROUTE_NAME', + '9.1.0', ), new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'ROUTE_OBJECT', 'Drupal\Core\Routing\RouteObjectInterface', 'ROUTE_OBJECT', + '9.1.0', ), new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'CONTROLLER_NAME', 'Drupal\Core\Routing\RouteObjectInterface', 'CONTROLLER_NAME', + '9.1.0', ), ]); }; diff --git a/config/drupal-9/drupal-9.2-deprecations.php b/config/drupal-9/drupal-9.2-deprecations.php index 3d093076e..a2213ab01 100644 --- a/config/drupal-9/drupal-9.2-deprecations.php +++ b/config/drupal-9/drupal-9.2-deprecations.php @@ -19,6 +19,6 @@ $rectorConfig->ruleWithConfiguration(MethodToMethodWithCheckRector::class, [ // https://www.drupal.org/node/3187914 - new MethodToMethodWithCheckConfiguration('Drupal\Core\Session\MetadataBag', 'clearCsrfTokenSeed', 'stampNew'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Session\MetadataBag', 'clearCsrfTokenSeed', 'stampNew', '9.2.0'), ]); }; diff --git a/config/drupal-9/drupal-9.3-deprecations.php b/config/drupal-9/drupal-9.3-deprecations.php index c51996a42..7f60ca18c 100644 --- a/config/drupal-9/drupal-9.3-deprecations.php +++ b/config/drupal-9/drupal-9.3-deprecations.php @@ -75,6 +75,7 @@ 'FILE_STATUS_PERMANENT', 'Drupal\file\FileInterface', 'STATUS_PERMANENT', + '9.3.0', ), ]); }; diff --git a/docs/rules_overview.md b/docs/rules_overview.md deleted file mode 100644 index e57fbac02..000000000 --- a/docs/rules_overview.md +++ /dev/null @@ -1,1210 +0,0 @@ -# 56 Rules Overview - -
- -## Categories - -- [Drupal10](#drupal10) (4) - -- [Drupal8](#drupal8) (18) - -- [Drupal9](#drupal9) (26) - -- [DrupalRector](#drupalrector) (8) - -
- -## Drupal10 - -### AnnotationToAttributeRector - -Change annotations with value to attribute - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal10\Rector\Deprecation\AnnotationToAttributeRector`](../src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php) - -```diff - namespace Drupal\Core\Action\Plugin\Action; - -+use Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver; -+use Drupal\Core\Action\Attribute\Action; - use Drupal\Core\Session\AccountInterface; -+use Drupal\Core\StringTranslation\TranslatableMarkup; - - /** - * Publishes an entity. -- * -- * @Action( -- * id = "entity:publish_action", -- * action_label = @Translation("Publish"), -- * deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver", -- * ) - */ -+#[Action( -+ id: 'entity:publish_action', -+ action_label: new TranslatableMarkup('Publish'), -+ deriver: EntityPublishedActionDeriver::class -+)] - class PublishAction extends EntityActionBase { -``` - -
- -### SystemTimeZonesRector - -Fixes deprecated `system_time_zones()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal10\Rector\Deprecation\SystemTimeZonesRector`](../src/Drupal10/Rector/Deprecation/SystemTimeZonesRector.php) - -```diff --system_time_zones(); --system_time_zones(FALSE, TRUE); --system_time_zones(NULL, FALSE); --system_time_zones(TRUE, FALSE); -+\Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(); -+\Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsListByRegion(); -+\Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(NULL); -+\Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(TRUE); -``` - -
- -### VersionedFunctionToServiceRector - -Fixes deprecated function to service calls, used in Drupal 8 and 9 deprecations - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal10\Rector\Deprecation\VersionedFunctionToServiceRector`](../src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php) - -```diff --_drupal_flush_css_js(); -+\Drupal::service('asset.query_string')->reset(); -``` - -
- -### WatchdogExceptionRector - -Fixes deprecated watchdog_exception('update', `$exception)` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal10\Rector\Deprecation\WatchdogExceptionRector`](../src/Drupal10/Rector/Deprecation/WatchdogExceptionRector.php) - -```diff --watchdog_exception('update', $exception); -+use \Drupal\Core\Utility\Error; -+$logger = \Drupal::logger('update'); -+Error::logException($logger, $exception); -``` - -
- -## Drupal8 - -### DBRector - -Fixes deprecated `db_delete()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\DBRector`](../src/Drupal8/Rector/Deprecation/DBRector.php) - -```diff --db_delete($table, $options); -+\Drupal::database()->delete($table, $options); -``` - -
- -```diff --db_insert($table, $options); -+\Drupal::database()->insert($table, $options); -``` - -
- -```diff --db_query($query, $args, $options); -+\Drupal::database()->query($query, $args, $options); -``` - -
- -```diff --db_select($table, $alias, $options); -+\Drupal::database()->select($table, $alias, $options); -``` - -
- -```diff --db_update($table, $options); -+\Drupal::database()->update($table, $options); -``` - -
- -### DrupalLRector - -Fixes deprecated `\Drupal::l()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\DrupalLRector`](../src/Drupal8/Rector/Deprecation/DrupalLRector.php) - -```diff --\Drupal::l('User Login', \Drupal\Core\Url::fromRoute('user.login')); -+\Drupal\Core\Link::fromTextAndUrl('User Login', \Drupal\Core\Url::fromRoute('user.login')); -``` - -
- -### DrupalServiceRenameRector - -Renames the IDs in `Drupal::service()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\DrupalServiceRenameRector`](../src/Drupal8/Rector/Deprecation/DrupalServiceRenameRector.php) - -```diff --\Drupal::service('old')->foo(); -+\Drupal::service('bar')->foo(); -``` - -
- -### DrupalSetMessageRector - -Fixes deprecated `drupal_set_message()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\DrupalSetMessageRector`](../src/Drupal8/Rector/Deprecation/DrupalSetMessageRector.php) - -```diff --drupal_set_message('example status', 'status'); -+\Drupal::messenger()->addStatus('example status'); -``` - -
- -### DrupalURLRector - -Fixes deprecated `\Drupal::url()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\DrupalURLRector`](../src/Drupal8/Rector/Deprecation/DrupalURLRector.php) - -```diff --\Drupal::url('user.login'); -+\Drupal\Core\Url::fromRoute('user.login')->toString(); -``` - -
- -### EntityCreateRector - -Fixes deprecated `entity_create()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\EntityCreateRector`](../src/Drupal8/Rector/Deprecation/EntityCreateRector.php) - -```diff --entity_create('node', ['bundle' => 'page', 'title' => 'Hello world']); -+\Drupal::service('entity_type.manager)->getStorage('node')->create(['bundle' => 'page', 'title' => 'Hello world']); -``` - -
- -### EntityDeleteMultipleRector - -Fixes deprecated `entity_delete_multiple()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\EntityDeleteMultipleRector`](../src/Drupal8/Rector/Deprecation/EntityDeleteMultipleRector.php) - -```diff --entity_delete_multiple('node', [1, 2, 42]); -+\Drupal::service('entity_type.manager')->getStorage('node')->delete(\Drupal::service('entity_type.manager')->getStorage('node')->loadMultiple(1, 2, 42)); -``` - -
- -### EntityInterfaceLinkRector - -Fixes deprecated `link()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\EntityInterfaceLinkRector`](../src/Drupal8/Rector/Deprecation/EntityInterfaceLinkRector.php) - -```diff --$url = $entity->link(); -+$url = $entity->toLink()->toString(); -``` - -
- -### EntityLoadRector - -Fixes deprecated `ENTITY_TYPE_load()` or `entity_load()` use - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\EntityLoadRector`](../src/Drupal8/Rector/Deprecation/EntityLoadRector.php) - -```diff --$entity = ENTITY_TYPE_load(123); --$node = entity_load('node', 123); -+$entity = \Drupal::entityManager()->getStorage('ENTITY_TYPE')->load(123); -+$node = \Drupal::entityManager()->getStorage('node')->load(123); -``` - -
- -### EntityManagerRector - -Fixes deprecated `\Drupal::entityManager()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\EntityManagerRector`](../src/Drupal8/Rector/Deprecation/EntityManagerRector.php) - -```diff --$entity_manager = \Drupal::entityManager(); -+$entity_manager = \Drupal::entityTypeManager(); -``` - -
- -### EntityViewRector - -Fixes deprecated `entity_view()` use - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\EntityViewRector`](../src/Drupal8/Rector/Deprecation/EntityViewRector.php) - -```diff --$rendered = entity_view($entity, 'default'); -+$rendered = \Drupal::entityTypeManager()->getViewBuilder($entity -+ ->getEntityTypeId())->view($entity, 'default'); -``` - -
- -### FileDefaultSchemeRector - -Fixes deprecated file_default_scheme calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\FileDefaultSchemeRector`](../src/Drupal8/Rector/Deprecation/FileDefaultSchemeRector.php) - -```diff --$file_default_scheme = file_default_scheme(); -+$file_default_scheme = \Drupal::config('system.file')->get('default_scheme'); -``` - -
- -### FunctionalTestDefaultThemePropertyRector - -Adds `$defaultTheme` property to Functional and FunctionalJavascript tests which do not have them. - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\FunctionalTestDefaultThemePropertyRector`](../src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector.php) - -```diff - class SomeClassTest { -+ protected $defaultTheme = 'stark' - } -``` - -
- -### GetMockRector - -Fixes deprecated `getMock()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\GetMockRector`](../src/Drupal8/Rector/Deprecation/GetMockRector.php) - -```diff --$this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); -+$this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); -``` - -
- -### LinkGeneratorTraitLRector - -Fixes deprecated `l()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\LinkGeneratorTraitLRector`](../src/Drupal8/Rector/Deprecation/LinkGeneratorTraitLRector.php) - -```diff --$this->l($text, $url); -+\Drupal\Core\Link::fromTextAndUrl($text, $url); -``` - -
- -### RequestTimeConstRector - -Fixes deprecated REQUEST_TIME calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\RequestTimeConstRector`](../src/Drupal8/Rector/Deprecation/RequestTimeConstRector.php) - -```diff --$request_time = REQUEST_TIME; -+$request_time = \Drupal::time()->getRequestTime(); -``` - -
- -### SafeMarkupFormatRector - -Fixes deprecated `SafeMarkup::format()` calls - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\SafeMarkupFormatRector`](../src/Drupal8/Rector/Deprecation/SafeMarkupFormatRector.php) - -```diff --$safe_string_markup_object = \Drupal\Component\Utility\SafeMarkup::format('hello world'); -+$safe_string_markup_object = new \Drupal\Component\Render\FormattableMarkup('hello world'); -``` - -
- -### StaticToFunctionRector - -Fixes deprecated `\Drupal\Component\Utility\Unicode::strlen()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal8\Rector\Deprecation\StaticToFunctionRector`](../src/Drupal8/Rector/Deprecation/StaticToFunctionRector.php) - -```diff --$length = \Drupal\Component\Utility\Unicode::strlen('example'); -+$length = mb_strlen('example'); -``` - -
- -```diff --$string = \Drupal\Component\Utility\Unicode::strtolower('example'); -+$string = mb_strtolower('example'); -``` - -
- -```diff --$string = \Drupal\Component\Utility\Unicode::substr('example', 0, 2); -+$string = mb_substr('example', 0, 2); -``` - -
- -## Drupal9 - -### AssertFieldByIdRector - -Fixes deprecated `AssertLegacyTrait::assertFieldById()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertFieldByIdRector`](../src/Drupal9/Rector/Deprecation/AssertFieldByIdRector.php) - -```diff --$this->assertFieldById('edit-name', NULL); -- $this->assertFieldById('edit-name', 'Test name'); -- $this->assertFieldById('edit-description', NULL); -- $this->assertFieldById('edit-description'); -+$this->assertSession()->fieldExists('edit-name'); -+ $this->assertSession()->fieldValueEquals('edit-name', 'Test name'); -+ $this->assertSession()->fieldExists('edit-description'); -+ $this->assertSession()->fieldValueEquals('edit-description', ''); -``` - -
- -### AssertFieldByNameRector - -Fixes deprecated `AssertLegacyTrait::assertFieldByName()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertFieldByNameRector`](../src/Drupal9/Rector/Deprecation/AssertFieldByNameRector.php) - -```diff --$this->assertFieldByName('field_name', 'expected_value'); --$this->assertFieldByName("field_name[0][value][date]", '', 'Date element found.'); --$this->assertFieldByName("field_name[0][value][time]", null, 'Time element found.'); -+$this->assertSession()->fieldValueEquals('field_name', 'expected_value'); -+$this->assertSession()->fieldValueEquals("field_name[0][value][date]", ''); -+$this->assertSession()->fieldExists("field_name[0][value][time]"); -``` - -
- -### AssertLegacyTraitRector - -Fixes deprecated `AssertLegacyTrait::METHOD()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertLegacyTraitRector`](../src/Drupal9/Rector/Deprecation/AssertLegacyTraitRector.php) - -```diff --$this->assertLinkByHref('user/1/translations'); -+$this->assertSession()->linkByHrefExists('user/1/translations'); -``` - -
- -```diff --$this->assertLink('Anonymous comment title'); -+$this->assertSession()->linkExists('Anonymous comment title'); -``` - -
- -```diff --$this->assertNoEscaped('
'); -+$this->assertSession()->assertNoEscaped('
'); -``` - -
- -```diff --$this->assertNoFieldChecked('edit-settings-view-mode', 'default'); -+$this->assertSession()->checkboxNotChecked('edit-settings-view-mode', 'default'); -``` - -
- -```diff --$this->assertNoField('files[upload]', 'Found file upload field.'); -+$this->assertSession()->fieldNotExists('files[upload]', 'Found file upload field.'); -``` - -
- -```diff --$this->assertNoLinkByHref('user/2/translations'); -+$this->assertSession()->linkByHrefNotExists('user/2/translations'); -``` - -
- -```diff --$this->assertNoLink('Anonymous comment title'); -+$this->assertSession()->linkNotExists('Anonymous comment title'); -``` - -
- -```diff --$this->assertNoOption('edit-settings-view-mode', 'default'); -+$this->assertSession()->optionNotExists('edit-settings-view-mode', 'default'); -``` - -
- -```diff --$this->assertNoPattern('|]*>|', 'No empty H4 element found.'); -+$this->assertSession()->responseNotMatches('|]*>|', 'No empty H4 element found.'); -``` - -
- -```diff --$this->assertPattern('|]*>|', 'No empty H4 element found.'); -+$this->assertSession()->responseMatches('|]*>|', 'No empty H4 element found.'); -``` - -
- -```diff --$this->assertNoRaw('bartik/logo.svg'); -+$this->assertSession()->responseNotContains('bartik/logo.svg'); -``` - -
- -```diff --$this->assertRaw('bartik/logo.svg'); -+$this->assertSession()->responseContains('bartik/logo.svg'); -``` - -
- -### AssertNoFieldByIdRector - -Fixes deprecated `AssertLegacyTrait::assertNoFieldById()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertNoFieldByIdRector`](../src/Drupal9/Rector/Deprecation/AssertNoFieldByIdRector.php) - -```diff --$this->assertNoFieldById('name'); -- $this->assertNoFieldById('name', 'not the value'); -- $this->assertNoFieldById('notexisting', NULL); -+$this->assertSession()->assertNoFieldById('name'); -+ $this->assertSession()->fieldValueNotEquals('name', 'not the value'); -+ $this->assertSession()->fieldNotExists('notexisting'); -``` - -
- -### AssertNoFieldByNameRector - -Fixes deprecated `AssertLegacyTrait::assertNoFieldByName()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertNoFieldByNameRector`](../src/Drupal9/Rector/Deprecation/AssertNoFieldByNameRector.php) - -```diff --$this->assertNoFieldByName('name'); -- $this->assertNoFieldByName('name', 'not the value'); -- $this->assertNoFieldByName('notexisting'); -- $this->assertNoFieldByName('notexisting', NULL); -+$this->assertSession()->fieldValueNotEquals('name', ''); -+ $this->assertSession()->fieldValueNotEquals('name', 'not the value'); -+ $this->assertSession()->fieldValueNotEquals('notexisting', ''); -+ $this->assertSession()->fieldNotExists('notexisting'); -``` - -
- -### AssertNoUniqueTextRector - -Fixes deprecated `AssertLegacyTrait::assertNoUniqueText()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertNoUniqueTextRector`](../src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector.php) - -```diff --$this->assertNoUniqueText('Duplicated message'); -+$page_text = $this->getSession()->getPage()->getText(); -+$nr_found = substr_count($page_text, 'Duplicated message'); -+$this->assertGreaterThan(1, $nr_found, "'Duplicated message' found more than once on the page"); -``` - -
- -### AssertOptionSelectedRector - -Fixes deprecated `AssertLegacyTrait::assertOptionSelected()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\AssertOptionSelectedRector`](../src/Drupal9/Rector/Deprecation/AssertOptionSelectedRector.php) - -```diff --$this->assertOptionSelected('options', 2); -+$this->assertTrue($this->assertSession()->optionExists('options', 2)->hasAttribute('selected')); -``` - -
- -### ConstructFieldXpathRector - -Fixes deprecated `AssertLegacyTrait::constructFieldXpath()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\ConstructFieldXpathRector`](../src/Drupal9/Rector/Deprecation/ConstructFieldXpathRector.php) - -```diff --$this->constructFieldXpath('id', 'edit-preferred-admin-langcode'); -+$this->getSession()->getPage()->findField('edit-preferred-admin-langcode'); -``` - -
- -### ExtensionPathRector - -Fixes deprecated `drupal_get_filename()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\ExtensionPathRector`](../src/Drupal9/Rector/Deprecation/ExtensionPathRector.php) - -```diff --drupal_get_filename('module', 'node'); --drupal_get_filename('theme', 'seven'); --drupal_get_filename('profile', 'standard'); -+\Drupal::service('extension.list.module')->getPathname('node'); -+\Drupal::service('extension.list.theme')->getPathname('seven'); -+\Drupal::service('extension.list.profile')->getPathname('standard'); -``` - -
- -```diff --drupal_get_path('module', 'node'); --drupal_get_path('theme', 'seven'); --drupal_get_path('profile', 'standard'); -+\Drupal::service('extension.list.module')->getPath('node'); -+\Drupal::service('extension.list.theme')->getPath('seven'); -+\Drupal::service('extension.list.profile')->getPath('standard'); -``` - -
- -### FileBuildUriRector - -Fixes deprecated `file_build_uri()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\FileBuildUriRector`](../src/Drupal9/Rector/Deprecation/FileBuildUriRector.php) - -```diff --$uri1 = file_build_uri('path/to/file.txt'); -+$uri1 = \Drupal::service('stream_wrapper_manager')->normalizeUri(\Drupal::config('system.file')->get('default_scheme') . ('://' . 'path/to/file.txt')); - $path = 'path/to/other/file.png'; --$uri2 = file_build_uri($path); -+$uri2 = \Drupal::service('stream_wrapper_manager')->normalizeUri(\Drupal::config('system.file')->get('default_scheme') . ('://' . $path)); -``` - -
- -### FileCreateUrlRector - -Fixes deprecated `file_create_url()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\FileCreateUrlRector`](../src/Drupal9/Rector/Deprecation/FileCreateUrlRector.php) - -```diff --file_create_url($uri); -+\Drupal::service('file_url_generator')->generateAbsoluteString($uri); -``` - -
- -### FileUrlTransformRelativeRector - -Fixes deprecated `file_url_transform_relative()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\FileUrlTransformRelativeRector`](../src/Drupal9/Rector/Deprecation/FileUrlTransformRelativeRector.php) - -```diff --file_url_transform_relative($uri); -+\Drupal::service('file_url_generator')->transformRelative($uri); -``` - -
- -### FromUriRector - -Fixes deprecated `file_create_url()` calls from `\Drupal\Core\Url::fromUri().` - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\FromUriRector`](../src/Drupal9/Rector/Deprecation/FromUriRector.php) - -```diff --\Drupal\Core\Url::fromUri(file_create_url($uri)); -+\Drupal::service('file_url_generator')->generate($uri); -``` - -
- -### FunctionToEntityTypeStorageMethod - -Refactor function call to an entity storage method - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\FunctionToEntityTypeStorageMethod`](../src/Drupal9/Rector/Deprecation/FunctionToEntityTypeStorageMethod.php) - -```diff --taxonomy_terms_static_reset(); -+\Drupal::entityTypeManager()->getStorage('taxonomy_term')->resetCache(); - --taxonomy_vocabulary_static_reset($vids); -+\Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary')->resetCache($vids); -``` - -
- -### FunctionToFirstArgMethodRector - -Fixes deprecated `taxonomy_implode_tags()` calls - -:wrench: **configure it!** - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\FunctionToFirstArgMethodRector`](../src/Drupal9/Rector/Deprecation/FunctionToFirstArgMethodRector.php) - -```diff --$url = taxonomy_term_uri($term); --$name = taxonomy_term_title($term); -+$url = $term->toUrl(); -+$name = $term->label(); -``` - -
- -### GetAllOptionsRector - -Fixes deprecated `AssertLegacyTrait::getAllOptions()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\GetAllOptionsRector`](../src/Drupal9/Rector/Deprecation/GetAllOptionsRector.php) - -```diff - $this->drupalGet('/form-test/select'); -- $this->assertCount(6, $this->getAllOptions($this->cssSelect('select[name="opt_groups"]')[0])); -+ $this->assertCount(6, $this->cssSelect('select[name="opt_groups"]')[0]->findAll('xpath', '//option')); -``` - -
- -### GetRawContentRector - -Fixes deprecated `AssertLegacyTrait::getRawContent()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\GetRawContentRector`](../src/Drupal9/Rector/Deprecation/GetRawContentRector.php) - -```diff --$this->getRawContent(); -+$this->getSession()->getPage()->getContent(); -``` - -
- -### ModuleLoadRector - -Fixes deprecated `module_load_install()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\ModuleLoadRector`](../src/Drupal9/Rector/Deprecation/ModuleLoadRector.php) - -```diff --module_load_install('example'); -+\Drupal::moduleHandler()->loadInclude('example', 'install'); - $type = 'install'; - $module = 'example'; - $name = 'name'; --module_load_include($type, $module, $name); --module_load_include($type, $module); -+\Drupal::moduleHandler()->loadInclude($module, $type, $name); -+\Drupal::moduleHandler()->loadInclude($module, $type); -``` - -
- -### PassRector - -Fixes deprecated `AssertLegacyTrait::pass()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\PassRector`](../src/Drupal9/Rector/Deprecation/PassRector.php) - -```diff --// Check for pass --$this->pass('The whole transaction is rolled back when a duplicate key insert occurs.'); -+// Check for pass -``` - -
- -### ProtectedStaticModulesPropertyRector - -"public static `$modules"` will have its visibility changed to protected. - -- class: [`DrupalRector\Drupal9\Rector\Property\ProtectedStaticModulesPropertyRector`](../src/Drupal9/Rector/Property/ProtectedStaticModulesPropertyRector.php) - -```diff - class SomeClassTest { -- public static $modules = []; -+ protected static $modules = []; - } -``` - -
- -### SystemSortByInfoNameRector - -Fixes deprecated `system_sort_modules_by_info_name()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\SystemSortByInfoNameRector`](../src/Drupal9/Rector/Deprecation/SystemSortByInfoNameRector.php) - -```diff --uasort($modules, 'system_sort_modules_by_info_name'); -+uasort($modules, [ModuleExtensionList::class, 'sortByName']); -``` - -
- -### TaxonomyTermLoadMultipleByNameRector - -Refactor function call to an entity storage method - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\TaxonomyTermLoadMultipleByNameRector`](../src/Drupal9/Rector/Deprecation/TaxonomyTermLoadMultipleByNameRector.php) - -```diff --$terms = taxonomy_term_load_multiple_by_name( -- 'Foo', -- 'topics' --); -+$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([ -+ 'name' => 'Foo', -+ 'vid' => 'topics', -+]); -``` - -
- -### TaxonomyVocabularyGetNamesDrupalStaticResetRector - -Refactor drupal_static_reset('taxonomy_vocabulary_get_names') to entity storage reset cache - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\TaxonomyVocabularyGetNamesDrupalStaticResetRector`](../src/Drupal9/Rector/Deprecation/TaxonomyVocabularyGetNamesDrupalStaticResetRector.php) - -```diff --drupal_static_reset('taxonomy_vocabulary_get_names'); -+\Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary')->resetCache(); -``` - -
- -### TaxonomyVocabularyGetNamesRector - -Refactor function call to an entity storage method - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\TaxonomyVocabularyGetNamesRector`](../src/Drupal9/Rector/Deprecation/TaxonomyVocabularyGetNamesRector.php) - -```diff --$vids = taxonomy_vocabulary_get_names(); -+$vids = \Drupal::entityQuery('taxonomy_vocabulary')->execute(); -``` - -
- -### UiHelperTraitDrupalPostFormRector - -Fixes deprecated `UiHelperTrait::drupalPostForm()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\UiHelperTraitDrupalPostFormRector`](../src/Drupal9/Rector/Deprecation/UiHelperTraitDrupalPostFormRector.php) - -```diff - $edit = []; - $edit['action'] = 'action_goto_action'; --$this->drupalPostForm('admin/config/system/actions', $edit, 'Create'); -+$this->drupalGet('admin/config/system/actions'); -+$this->submitForm($edit, 'Create'); - $edit['action'] = 'action_goto_action_1'; --$this->drupalPostForm(null, $edit, 'Edit'); -+$this->submitForm($edit, 'Edit'); -``` - -
- -### UserPasswordRector - -Fixes deprecated `user_password()` calls - -- class: [`DrupalRector\Drupal9\Rector\Deprecation\UserPasswordRector`](../src/Drupal9/Rector/Deprecation/UserPasswordRector.php) - -```diff --$pass = user_password(); --$shorter_pass = user_password(8); -+$pass = \Drupal::service('password_generator')->generate(); -+$shorter_pass = \Drupal::service('password_generator')->generate(8); -``` - -
- -## DrupalRector - -### ClassConstantToClassConstantRector - -Fixes deprecated class contant use, used in Drupal 9.1 deprecations - -:wrench: **configure it!** - -- class: [`DrupalRector\Rector\Deprecation\ClassConstantToClassConstantRector`](../src/Rector/Deprecation/ClassConstantToClassConstantRector.php) - -```diff --$value = Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_NAME; --$value2 = Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_OBJECT; --$value3 = Symfony\Cmf\Component\Routing\RouteObjectInterface::CONTROLLER_NAME; -+$value = \Drupal\Core\Routing\RouteObjectInterface::ROUTE_NAME; -+$value2 = \Drupal\Core\Routing\RouteObjectInterface::ROUTE_OBJECT; -+$value3 = \Drupal\Core\Routing\RouteObjectInterface::CONTROLLER_NAME; -``` - -
- -### ConstantToClassConstantRector - -Fixes deprecated contant use, used in Drupal 8 and 9 deprecations - -:wrench: **configure it!** - -- class: [`DrupalRector\Rector\Deprecation\ConstantToClassConstantRector`](../src/Rector/Deprecation/ConstantToClassConstantRector.php) - -```diff --$result = file_unmanaged_copy($source, $destination, DEPRECATED_CONSTANT); -+$result = file_unmanaged_copy($source, $destination, \Drupal\MyClass::CONSTANT); -``` - -
- -### DeprecationHelperRemoveRector - -Remove DeprecationHelper calls for versions before configured minimum requirement - -:wrench: **configure it!** - -- class: [`DrupalRector\Rector\Deprecation\DeprecationHelperRemoveRector`](../src/Rector/Deprecation/DeprecationHelperRemoveRector.php) - -```diff - $settings = []; - $filename = 'simple_filename.yaml'; --DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '9.1.0', fn() => new_function(), fn() => old_function()); --DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.5.0', fn() => SettingsEditor::rewrite($filename, $settings), fn() => drupal_rewrite_settings($settings, $filename)); -+drupal_rewrite_settings($settings, $filename); - DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => new_function(), fn() => old_function()); -``` - -
- -### FunctionToServiceRector - -Fixes deprecated function to service calls, used in Drupal 8 and 9 deprecations - -:wrench: **configure it!** - -- class: [`DrupalRector\Rector\Deprecation\FunctionToServiceRector`](../src/Rector/Deprecation/FunctionToServiceRector.php) - -```diff --$path = drupal_realpath($path); -+$path = \Drupal::service('file_system') -+ ->realpath($path); -``` - -
- -```diff --$result = drupal_render($elements); -+$result = \Drupal::service('renderer')->render($elements); -``` - -
- -```diff --$result = drupal_render_root($elements); -+$result = \Drupal::service('renderer')->renderRoot($elements); -``` - -
- -```diff --$display = entity_get_display($entity_type, $bundle, $view_mode) -+$display = \Drupal::service('entity_display.repository') -+ ->getViewDisplay($entity_type, $bundle, $view_mode); -``` - -
- -```diff --$display = entity_get_form_display($entity_type, $bundle, $form_mode) -+$display = \Drupal::service('entity_display.repository') -+ ->getFormDisplay($entity_type, $bundle, $form_mode); -``` - -
- -```diff --file_copy(); -+\Drupal::service('file.repository')->copy(); -``` - -
- -```diff --$dir = file_directory_temp(); -+$dir = \Drupal::service('file_system')->getTempDirectory(); -``` - -
- -```diff --file_move(); -+\Drupal::service('file.repository')->move(); -``` - -
- -```diff --$result = file_prepare_directory($directory, $options); -+$result = \Drupal::service('file_system')->prepareDirectory($directory, $options); -``` - -
- -```diff --file_save_data($data); -+\Drupal::service('file.repository')->writeData($data); -``` - -
- -```diff --$files = file_scan_directory($directory); -+$files = \Drupal::service('file_system')->scanDirectory($directory); -``` - -
- -```diff --$result = file_unmanaged_save_data($data, $destination, $replace); -+$result = \Drupal::service('file_system')->saveData($data, $destination, $replace); -``` - -
- -```diff --$result = file_uri_target($uri) -+$result = \Drupal::service('stream_wrapper_manager')->getTarget($uri); -``` - -
- -```diff --$date = format_date($timestamp, $type, $format, $timezone, $langcode); -+$date = \Drupal::service('date.formatter')->format($timestamp, $type, $format, $timezone, $langcode); -``` - -
- -```diff --$date = format_date($timestamp, $type, $format, $timezone, $langcode); -+$date = \Drupal::service('date.formatter')->format($timestamp, $type, $format, $timezone, $langcode); -``` - -
- -```diff --$output = render($build); -+$output = \Drupal::service('renderer')->render($build); -``` - -
- -### FunctionToStaticRector - -Fixes deprecated `file_directory_os_temp()` calls, used in Drupal 8, 9 and 10 deprecations - -:wrench: **configure it!** - -- class: [`DrupalRector\Rector\Deprecation\FunctionToStaticRector`](../src/Rector/Deprecation/FunctionToStaticRector.php) - -```diff --$dir = file_directory_os_temp(); -+$dir = \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory(); -``` - -
- -```diff - $settings = []; - $filename = 'simple_filename.yaml'; --drupal_rewrite_settings($settings, $filename); -+SettingsEditor::rewrite($filename, $settings); -``` - -
- -```diff - $settings = []; - $filename = 'simple_filename.yaml'; --drupal_rewrite_settings($settings, $filename); -+SettingsEditor::rewrite($filename, $settings); -``` - -
- -### HookConvertRector - -Hook conversion script - -- class: [`DrupalRector\Rector\Convert\HookConvertRector`](../src/Rector/Convert/HookConvertRector.php) - -```diff - /** -- * Implements hook_user_cancel(). -+ * Hook implementations for hookconvertrector. - */ --function hookconvertrector_user_cancel($edit, UserInterface $account, $method) { -- $red = 'red'; -- $method = ['red', 'green', 'blue']; -- $edit = [ -- 'red' => 'red', -- 'green' => 'green', -- 'blue' => 'blue', -- ]; -+class HookconvertrectorHooks -+{ -+ /** -+ * Implements hook_user_cancel(). -+ */ -+ #[Hook('user_cancel')] -+ public function userCancel($edit, \UserInterface $account, $method) -+ { -+ $red = 'red'; -+ $method = [ -+ 'red', -+ 'green', -+ 'blue', -+ ]; -+ $edit = [ -+ 'red' => 'red', -+ 'green' => 'green', -+ 'blue' => 'blue', -+ ]; -+ } - } -``` - -
- -### MethodToMethodWithCheckRector - -Fixes deprecated `MetadataBag::clearCsrfTokenSeed()` calls, used in Drupal 8 and 9 deprecations - -:wrench: **configure it!** - -- class: [`DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector`](../src/Rector/Deprecation/MethodToMethodWithCheckRector.php) - -```diff - $metadata_bag = new \Drupal\Core\Session\MetadataBag(new \Drupal\Core\Site\Settings([])); --$metadata_bag->clearCsrfTokenSeed(); -+$metadata_bag->stampNew(); -``` - -
- -```diff --$url = $entity->urlInfo(); -+$url = $entity->toUrl(); -``` - -
- -```diff - /* @var \Drupal\node\Entity\Node $node */ - $node = \Drupal::entityTypeManager()->getStorage('node')->load(123); - $entity_type = $node->getEntityType(); --$entity_type->getLowercaseLabel(); -+$entity_type->getSingularLabel(); -``` - -
- -### ShouldCallParentMethodsRector - -PHPUnit based tests should call parent methods (setUp, tearDown) - -- class: [`DrupalRector\Rector\PHPUnit\ShouldCallParentMethodsRector`](../src/Rector/PHPUnit/ShouldCallParentMethodsRector.php) - -```diff - namespace Drupal\Tests\Rector\Deprecation\PHPUnit\ShouldCallParentMethodsRector\fixture; - - use Drupal\KernelTests\KernelTestBase; - - final class SetupVoidTest extends KernelTestBase { - - protected function setUp(): void - { -+ parent::setUp(); - $test = 'doing things'; - } - - protected function tearDown(): void - { -+ parent::tearDown(); - $test = 'doing things'; - } - - } -``` - -
diff --git a/phpstan-1-baseline.neon b/phpstan-1-baseline.neon deleted file mode 100644 index 3e6d9e915..000000000 --- a/phpstan-1-baseline.neon +++ /dev/null @@ -1,189 +0,0 @@ -parameters: - ignoreErrors: - - - message: """ - #^Call to deprecated method locateRoot\\(\\) of class DrupalFinder\\\\DrupalFinder\\: - Will be removed in v2\\. Future usage should instantiate - a new DrupalFinder object by passing the starting path to its - constructor\\.$# - """ - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_contrib_extension_directory_roots\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_find_extension_directories\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_get_extension_namespaces\\(\\) has parameter \\$dirs with no value type specified in iterable type array\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_get_extension_namespaces\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_populate_class_loader\\(\\) has no return type specified\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_populate_class_loader\\(\\) has parameter \\$drupalRoot with no type specified\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: "#^Function drupal_phpunit_populate_class_loader\\(\\) has parameter \\$vendorRoot with no type specified\\.$#" - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: """ - #^Instantiation of deprecated class DrupalFinder\\\\DrupalFinder\\: - in drupal\\-finder\\:1\\.3\\.0 and is removed from drupal\\-finder\\:2\\.0\\.0\\. - Use \\\\DrupalFinder\\\\DrupalFinderComposerRuntime instead\\.$# - """ - count: 1 - path: config/drupal-phpunit-bootstrap-file.php - - - - message: """ - #^Call to deprecated method locateRoot\\(\\) of class DrupalFinder\\\\DrupalFinder\\: - Will be removed in v2\\. Future usage should instantiate - a new DrupalFinder object by passing the starting path to its - constructor\\.$# - """ - count: 1 - path: rector.php - - - - message: """ - #^Instantiation of deprecated class DrupalFinder\\\\DrupalFinder\\: - in drupal\\-finder\\:1\\.3\\.0 and is removed from drupal\\-finder\\:2\\.0\\.0\\. - Use \\\\DrupalFinder\\\\DrupalFinderComposerRuntime instead\\.$# - """ - count: 1 - path: rector.php - - - - message: """ - #^Instantiation of deprecated class PhpParser\\\\Node\\\\Expr\\\\ArrayItem\\: - use \\\\PhpParser\\\\Node\\\\ArrayItem instead\\.$# - """ - count: 1 - path: src/Drupal10/Rector/Deprecation/WatchdogExceptionRector.php - - - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\:\\:\\$value\\.$#" - count: 2 - path: src/Drupal8/Rector/Deprecation/DBRector.php - - - - message: "#^Call to an undefined method PHPStan\\\\Type\\\\Type\\:\\:getValue\\(\\)\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/DrupalSetMessageRector.php - - - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\:\\:\\$expr\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityLoadRector.php - - - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\:\\:\\$expr\\.$#" - count: 2 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\:\\:\\$name\\.$#" - count: 3 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Method DrupalRector\\\\Drupal8\\\\Rector\\\\Deprecation\\\\EntityManagerRector\\:\\:getServiceByMethodName\\(\\) has no return type specified\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$node$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Parameter \\#1 \\$assign of method DrupalRector\\\\Drupal8\\\\Rector\\\\Deprecation\\\\EntityManagerRector\\:\\:findInstanceByNameInAssign\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\Assign, PhpParser\\\\Node\\\\Expr given\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Parameter \\#1 \\$assign of method DrupalRector\\\\Drupal8\\\\Rector\\\\Deprecation\\\\EntityManagerRector\\:\\:replaceInstanceByNameInAssign\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\Assign, PhpParser\\\\Node\\\\Expr given\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Parameter \\#1 \\$expr of method DrupalRector\\\\Drupal8\\\\Rector\\\\Deprecation\\\\EntityManagerRector\\:\\:refactorExpression\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\MethodCall\\|PhpParser\\\\Node\\\\Expr\\\\StaticCall, PhpParser\\\\Node given\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Parameter \\#1 \\$expr of method DrupalRector\\\\Drupal8\\\\Rector\\\\Deprecation\\\\EntityManagerRector\\:\\:refactorExpression\\(\\) expects PhpParser\\\\Node\\\\Expr\\\\MethodCall\\|PhpParser\\\\Node\\\\Expr\\\\StaticCall, PhpParser\\\\Node\\\\Expr given\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Property PhpParser\\\\Node\\\\Expr\\\\Assign\\:\\:\\$expr \\(PhpParser\\\\Node\\\\Expr\\) does not accept PhpParser\\\\Node\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/EntityManagerRector.php - - - - message: "#^Calling PHPStan\\\\Php\\\\PhpVersionFactory\\:\\:create\\(\\) is not covered by backward compatibility promise\\. The method might change in a minor PHPStan version\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector.php - - - - message: "#^Method PHPStan\\\\Type\\\\Type\\:\\:isSmallerThanOrEqual\\(\\) invoked with 2 parameters, 1 required\\.$#" - count: 1 - path: src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector.php - - - - message: """ - #^Instantiation of deprecated class PhpParser\\\\Node\\\\Scalar\\\\Encapsed\\: - use \\\\PhpParser\\\\Node\\\\Scalar\\\\InterpolatedString instead\\.$# - """ - count: 1 - path: src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector.php - - - - message: """ - #^Instantiation of deprecated class PhpParser\\\\Node\\\\Scalar\\\\EncapsedStringPart\\: - use \\\\PhpParser\\\\Node\\\\InterpolatedStringPart instead\\.$# - """ - count: 3 - path: src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector.php - - - - message: """ - #^Instantiation of deprecated class PhpParser\\\\Node\\\\Scalar\\\\LNumber\\: - use \\\\PhpParser\\\\Node\\\\Scalar\\\\Int_ instead\\.$# - """ - count: 1 - path: src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector.php - - - - message: "#^Call to an undefined method PHPStan\\\\Type\\\\Type\\:\\:getValue\\(\\)\\.$#" - count: 1 - path: src/Drupal9/Rector/Deprecation/ExtensionPathRector.php - - - - message: """ - #^Instantiation of deprecated class PhpParser\\\\Node\\\\Stmt\\\\UseUse\\: - use \\\\PhpParser\\\\Node\\\\UseItem instead\\.$# - """ - count: 1 - path: src/Rector/Convert/HookConvertRector.php diff --git a/phpstan-1.neon b/phpstan-1.neon deleted file mode 100644 index 81cb4d365..000000000 --- a/phpstan-1.neon +++ /dev/null @@ -1,10 +0,0 @@ -parameters: - level: 6 - paths: - - config - - src - - rector.php - dynamicConstantNames: - - Drupal::VERSION -includes: - - phpstan-1-baseline.neon diff --git a/rector.php b/rector.php index 8d140f134..d2e82bc5a 100644 --- a/rector.php +++ b/rector.php @@ -2,7 +2,11 @@ declare(strict_types=1); +use DrupalRector\Rector\Deprecation\DeprecationHelperRemoveRector; +use DrupalRector\Rector\ValueObject\DeprecationHelperRemoveConfiguration; +use DrupalRector\Services\DrupalRectorSettings; use DrupalRector\Set\Drupal10SetList; +use DrupalRector\Set\Drupal11SetList; use DrupalRector\Set\Drupal8SetList; use DrupalRector\Set\Drupal9SetList; use Rector\Config\RectorConfig; @@ -17,8 +21,42 @@ Drupal8SetList::DRUPAL_8, Drupal9SetList::DRUPAL_9, Drupal10SetList::DRUPAL_10, + Drupal11SetList::DRUPAL_11, ]); + // Configure DrupalRectorSettings to control rule behaviour. + // By default, backward-compatibility wrapping is enabled. Disable it if + // you don't need to support multiple Drupal versions simultaneously. + $rectorConfig->singleton(DrupalRectorSettings::class, fn () => (new DrupalRectorSettings()) + ->disableBackwardCompatibility() + // Contrib module developers: set the minimum Drupal version your + // module needs to support so that BC wrappers are emitted correctly. + // Example: ->setMinimumCoreVersionSupported('10.5.0') + ); + + // Contrib modules: once you raise your minimum supported Drupal version, + // uncomment and configure this rule to strip DeprecationHelper BC wrappers + // for any deprecation introduced before that version. The wrappers are + // replaced with the new API call directly. + // + // $rectorConfig->ruleWithConfiguration(DeprecationHelperRemoveRector::class, [ + // new DeprecationHelperRemoveConfiguration('10.3.0'), + // ]); + + // If you wish to remove all DeprecationHelper BC wrappers based on your + // installed Drupal version, you can use the following rule instead: + // + // $rectorConfig->ruleWithConfiguration(DeprecationHelperRemoveRector::class, [ + // new DeprecationHelperRemoveConfiguration(\Drupal::VERSION), + // ]); + + // When phsptan-drupal is available, we should load it to get better type + // inference to use in rectors. + $phpstanDrupalExtension = __DIR__.'/vendor/mglaman/phpstan-drupal/extension.neon'; + if (file_exists($phpstanDrupalExtension)) { + $rectorConfig->phpstanConfigs([$phpstanDrupalExtension]); + } + if (class_exists('DrupalFinder\DrupalFinderComposerRuntime')) { $drupalFinder = new DrupalFinder\DrupalFinderComposerRuntime(); } else { @@ -27,10 +65,10 @@ } $drupalRoot = $drupalFinder->getDrupalRoot(); $rectorConfig->autoloadPaths([ - $drupalRoot . '/core', - $drupalRoot . '/modules', - $drupalRoot . '/profiles', - $drupalRoot . '/themes' + $drupalRoot.'/core', + $drupalRoot.'/modules', + $drupalRoot.'/profiles', + $drupalRoot.'/themes', ]); $rectorConfig->skip(['*/upgrade_status/tests/modules/*']); diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..fad952938 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,3 @@ +# Do not use + +Some analysis scripts that were used to check for coverage, etc. They are not complete, but are useful to keep around. diff --git a/scripts/check-rector-coverage.php b/scripts/check-rector-coverage.php new file mode 100644 index 000000000..14d931b94 --- /dev/null +++ b/scripts/check-rector-coverage.php @@ -0,0 +1,721 @@ +#!/usr/bin/env php + [--csv] + * + * The patches directory can also be provided via the RECTOR_PATCHES_DIR + * environment variable. + */ +$rootDir = dirname(__DIR__); +$patchesDir = null; +$verbose = false; +$csvMode = false; + +foreach (array_slice($argv, 1) as $arg) { + if ($arg === '--help' || $arg === '-h') { + echo <<<'HELP' +Usage: + php scripts/check-rector-coverage.php [--csv] [--verbose] + +Arguments: + patches-dir Directory containing /*.patch files. + May also be supplied via the RECTOR_PATCHES_DIR env var. + +Options: + --csv Output results as CSV instead of a human-readable table. + --verbose, -v Show extra debug output. + --help, -h Show this help message. + +How it works: + For each rector in three tiers (config files → custom rector classes → fixture diffs), + the script extracts the "before" patterns the rector looks for. It then scans the removed + lines (-) of every .patch file in patches-dir and counts how many distinct modules contain + each pattern. The final table is sorted by match count descending. + +Tiers of pattern extraction: + 1. Config files (config/drupal-*/*.php) — FunctionToService, ConstantToClass, etc. + 2. Custom rector classes (src/Drupal*/Rector/Deprecation/*.php) — match/case/getName patterns. + 3. Fixture diffs (tests/src/Rector/*/fixture/*.php.inc) — fallback for anything not covered above. + +HELP; + exit(0); + } elseif ($arg === '--verbose' || $arg === '-v') { + $verbose = true; + } elseif ($arg === '--csv') { + $csvMode = true; + } elseif (!str_starts_with($arg, '-')) { + $patchesDir = $arg; + } +} + +$patchesDir ??= getenv('RECTOR_PATCHES_DIR') ?: null; + +if ($patchesDir === null) { + fwrite(STDERR, "Error: patches directory is required.\nRun with: php scripts/check-rector-coverage.php \nOr set RECTOR_PATCHES_DIR.\n"); + exit(1); +} + +if (!is_dir($patchesDir)) { + fwrite(STDERR, "Patches directory not found: $patchesDir\n"); + exit(1); +} + +// ============================================================ +// Registry: key => ['label', 'patterns', 'version', 'class', 'source', 'configuration'] +// ============================================================ + +$registry = []; + +function reg(array &$registry, string $key, string $label, string $pattern, string $version, string $class, string $source, string $configEntry = ''): void +{ + if (!isset($registry[$key])) { + $registry[$key] = [ + 'label' => $label, + 'patterns' => [], + 'version' => $version, + 'class' => $class, + 'source' => $source, + 'configuration' => [], + ]; + } + if (!in_array($pattern, $registry[$key]['patterns'], true)) { + $registry[$key]['patterns'][] = $pattern; + } + if ($configEntry !== '' && !in_array($configEntry, $registry[$key]['configuration'], true)) { + $registry[$key]['configuration'][] = $configEntry; + } +} + +// ============================================================ +// TIER 1: Config files +// ============================================================ + +$configFiles = glob("$rootDir/config/drupal-*/*.php") ?: []; +sort($configFiles); + +// Build short-class → FQCN map from use statements in config files. +$fqcnMap = []; +foreach ($configFiles as $f) { + if (preg_match_all('/^use\s+([\w\\\\]+);/m', file_get_contents($f), $um)) { + foreach ($um[1] as $fqcn) { + $short = substr($fqcn, strrpos($fqcn, '\\') + 1); + $fqcnMap[$short] = $fqcn; + } + } +} + +foreach ($configFiles as $configFile) { + $base = basename($configFile); + + // Skip "all" aggregate files to avoid double-counting + if (str_contains($base, '-all-')) { + continue; + } + + // Extract Drupal version from filename: drupal-11.4-deprecations.php → "11.4" + if (!preg_match('/drupal-(\d+(?:\.\d+)*)-/', $base, $vm)) { + continue; + } + $version = $vm[1]; + + $content = file_get_contents($configFile); + + // --- FunctionToServiceConfiguration / FunctionToStaticConfiguration --- + // Format: new FunctionToServiceConfiguration('version', 'func_name', 'Service\\Class', 'method') + // The first string is always the version, the second is always the function name. + if (preg_match_all( + '/new FunctionTo(Service|Static)Configuration\(\s*[\'"][^\'\"]+[\'"],\s*[\'"]([a-z_][a-zA-Z0-9_]*)[\'"],\s*[\'"]([^\'\"]+)[\'"],\s*[\'"]([^\'\"]+)[\'"]/', + $content, + $matches, + PREG_SET_ORDER + )) { + foreach ($matches as $m) { + $rClass = 'FunctionTo'.$m[1].'Rector'; + $key = $rClass.'@'.$version; + $configEntry = $m[2].'( → '.$m[3].'::'.$m[4].'()'; + reg($registry, $key, "$rClass [$version]", $m[2].'(', $version, $fqcnMap[$rClass] ?? $rClass, 'config', $configEntry); + } + } + + // --- FunctionCallRemovalConfiguration --- + // Format: new FunctionCallRemovalConfiguration('func_name') — no version prefix + if (preg_match_all( + '/new FunctionCallRemovalConfiguration\(\s*[\'"]([a-z_][a-zA-Z0-9_]*)[\'"]/', + $content, + $matches + )) { + foreach ($matches[1] as $funcName) { + $rClass = 'FunctionCallRemovalRector'; + $key = $rClass.'@'.$version; + $configEntry = $funcName.'( → (removed)'; + reg($registry, $key, "$rClass [$version]", $funcName.'(', $version, $fqcnMap[$rClass] ?? $rClass, 'config', $configEntry); + } + } + + // --- FunctionToFirstArgMethodConfiguration --- + // Old format (D8/D9): ('func_name', 'method') + // New format (D11+): ('version', 'func_name', '$arg', 'method') + if (preg_match_all( + '/new FunctionToFirstArgMethodConfiguration\(\s*[\'"]([^\'\"]+)[\'"],\s*[\'"]([^\'\"]+)[\'"],\s*(?:[\'"][^\'\"]*[\'"],\s*)?[\'"]([a-zA-Z][a-zA-Z0-9_]*)[\'"]/', + $content, + $matches, + PREG_SET_ORDER + )) { + foreach ($matches as $m) { + // If the first arg looks like a version string (starts with digit), use second as func name + $funcName = preg_match('/^\d+\.\d+/', $m[1]) ? $m[2] : $m[1]; + $methodArg = $m[3]; + if (preg_match('/^\d/', $funcName)) { + continue; // skip if still a version string + } + $rClass = 'FunctionToFirstArgMethodRector'; + $key = $rClass.'@'.$version; + $configEntry = $funcName.'($arg) → $arg->'.$methodArg.'()'; + reg($registry, $key, "$rClass [$version]", $funcName.'(', $version, $fqcnMap[$rClass] ?? $rClass, 'config', $configEntry); + } + } + + // --- FunctionToEntityTypeStorageConfiguration --- + // Format: new FunctionToEntityTypeStorageConfiguration('func_name', ...) + if (preg_match_all( + '/new FunctionToEntityTypeStorageConfiguration\(\s*[\'"]([a-z_][a-zA-Z0-9_]*)[\'"]/', + $content, + $matches + )) { + foreach ($matches[1] as $funcName) { + $rClass = 'FunctionToEntityTypeStorageRector'; + $key = $rClass.'@'.$version; + reg($registry, $key, "$rClass [$version]", $funcName.'(', $version, $fqcnMap[$rClass] ?? $rClass, 'config'); + } + } + + // --- ConstantToClassConfiguration --- + // Format: new ConstantToClassConfiguration('CONST_NAME', 'Target\\Class', 'NEW_CONST') + if (preg_match_all( + '/new ConstantToClass(?:Constant)?Configuration\(\s*[\'"]([A-Z_][A-Z0-9_]{3,})[\'"],\s*[\'"]([^\'\"]+)[\'"],\s*[\'"]([^\'\"]+)[\'"]/', + $content, + $matches, + PREG_SET_ORDER + )) { + foreach ($matches as $m) { + $constName = $m[1]; + $rClass = 'ConstantToClassConstantRector'; + $key = $rClass.'@'.$version; + $shortTarget = substr($m[2], strrpos($m[2], '\\') + 1); + $configEntry = $constName.' → '.$shortTarget.'::'.$m[3]; + reg($registry, $key, "$rClass [$version]", $constName, $version, $fqcnMap[$rClass] ?? $rClass, 'config', $configEntry); + } + } + + // --- ClassConstantToClassConstantConfiguration --- + // Format: new ClassConstantToClassConstantConfiguration('OldClass', 'OLD_CONST', 'NewClass', 'NEW_CONST') + if (preg_match_all( + '/new ClassConstantToClassConstantConfiguration\(\s*[\'"]([^\'\"]+)[\'"],\s*[\'"]([A-Z_][A-Z0-9_]{2,})[\'"],\s*[\'"]([^\'\"]+)[\'"],\s*[\'"]([^\'\"]+)[\'"]/', + $content, + $matches, + PREG_SET_ORDER + )) { + foreach ($matches as $m) { + $oldClass = $m[1]; + $oldConst = $m[2]; + $shortOld = substr($oldClass, strrpos($oldClass, '\\') + 1); + $shortNew = substr($m[3], strrpos($m[3], '\\') + 1); + $rClass = 'ClassConstantToClassConstantRector'; + $key = $rClass.'@'.$version; + $configEntry = $shortOld.'::'.$oldConst.' → '.$shortNew.'::'.$m[4]; + reg($registry, $key, "$rClass [$version]", $shortOld.'::'.$oldConst, $version, $fqcnMap[$rClass] ?? $rClass, 'config', $configEntry); + } + } + + // --- MethodToMethodWithCheckConfiguration --- + // Format: new MethodToMethodWithCheckConfiguration('ClassName', 'old_method', 'new_method') + if (preg_match_all( + '/new MethodToMethodWithCheckConfiguration\(\s*[\'"][^\'\"]+[\'"],\s*[\'"]([a-z][a-zA-Z0-9_]{2,})[\'"],\s*[\'"]([a-z][a-zA-Z0-9_]{2,})[\'"]/', + $content, + $matches, + PREG_SET_ORDER + )) { + foreach ($matches as $m) { + $rClass = 'MethodToMethodWithCheckRector'; + $key = $rClass.'@'.$version; + $configEntry = '->'.$m[1].'() → ->'.$m[2].'()'; + reg($registry, $key, "$rClass [$version]", '->'.$m[1].'(', $version, $fqcnMap[$rClass] ?? $rClass, 'config', $configEntry); + } + } + + // --- RenameClassRector --- + // Format: ['Old\\ClassName' => 'New\\ClassName'] inside ruleWithConfiguration blocks + if (preg_match_all( + '/[\'"]([A-Z][a-zA-Z0-9]*(?:\\\\[A-Z][a-zA-Z0-9]+)+)[\'"][\s\n]*=>[\s\n]*[\'"](?:[A-Z][a-zA-Z0-9\\\\]+)[\'"]/', + $content, + $matches + )) { + foreach ($matches[1] as $oldFqcn) { + $shortClass = substr($oldFqcn, strrpos($oldFqcn, '\\') + 1); + if (strlen($shortClass) < 5) { + continue; + } + $key = 'RenameClassRector@'.$version; + reg($registry, $key, "RenameClassRector [$version]", $shortClass, $version, 'RenameClassRector', 'config'); + } + } +} + +// ============================================================ +// TIER 2: Custom rector classes +// ============================================================ + +// Explicit pattern overrides for rectors where generic AST-based extraction +// produces wrong results — typically because the heuristics pick up internal +// node-accessor names or new service-method names instead of the deprecated +// user-facing function/method calls we actually want to find in patches. +// [] means "skip tracking entirely" (e.g. addition-only rectors where no +// lines are removed and removed-line scanning cannot detect the transformation). +$patternOverrides = [ + // Generic extraction picks up 'stmts' (an AST node accessor) instead of + // the deprecated method name. + 'LoadAllIncludesRector' => ['loadAllIncludes('], + // Generic extraction picks up the new service method names ('rebuild', + // 'needsRebuild', 'setNeedsRebuild') rather than the deprecated procedural + // function names that will appear in the removed lines of a patch. + 'NodeAccessRebuildFunctionsRector' => ['node_access_rebuild(', 'node_access_needs_rebuild('], + // This rector only adds parent::setUp()/tearDown() calls — nothing is + // removed, so removed-line scanning produces only fixture noise. Skip it. + 'ShouldCallParentMethodsRector' => [], + // Generic extraction picks up the new service method names (getRoles, + // getAllFormats, getFallbackFormatId, …) from the replacement values in the + // function-name → service-call map. Only the map keys are the deprecated + // user-facing functions that appear in removed patch lines. + 'FilterFormatFunctionsToServiceRector' => [ + 'filter_fallback_format(', + 'filter_formats(', + 'filter_get_roles_by_format(', + 'filter_get_formats_by_role(', + 'filter_default_format(', + ], +]; + +// Classes with an empty override are excluded from all tiers so Tier 3 does +// not accidentally pick them up via fixture diffs. +$skippedClasses = array_keys(array_filter($patternOverrides, fn ($p) => $p === [])); + +$customRectorFiles = array_merge( + glob("$rootDir/src/Drupal*/Rector/Deprecation/*.php") ?: [], + glob("$rootDir/src/Drupal*/Rector/Convert/*.php") ?: [] +); + +// Build short class name → FQCN map from namespace declarations in rector files. +$customRectorFqcnMap = []; +foreach ($customRectorFiles as $rf) { + $cn = basename($rf, '.php'); + if (preg_match('/^namespace\s+([\w\\\\]+);/m', file_get_contents($rf), $nm)) { + $customRectorFqcnMap[$cn] = $nm[1].'\\'.$cn; + } +} + +// Track which custom rector classes are already covered by config entries +$configClasses = array_unique(array_column($registry, 'class')); + +foreach ($customRectorFiles as $rectorFile) { + $className = basename($rectorFile, '.php'); + + // Skip if this rector is already represented as a config-driven entry + // (the generic rectors like FunctionToServiceRector are config-only) + if (in_array($className, $configClasses, true)) { + continue; + } + + $content = file_get_contents($rectorFile); + + // Extract version from the first version string in the class + preg_match('/\'(\d+\.\d+)\.\d+\'/', $content, $vm); + $version = $vm[1] ?? '?'; + + // Use explicit override if available; skip addition-only rectors entirely. + if (array_key_exists($className, $patternOverrides)) { + $patterns = $patternOverrides[$className]; + if (empty($patterns)) { + continue; // not trackable via removed-line scanning + } + $key = $className.'@'.$version; + if (!isset($registry[$key])) { + $registry[$key] = [ + 'label' => $className.' ['.$version.']', + 'patterns' => $patterns, + 'version' => $version, + 'class' => $customRectorFqcnMap[$className] ?? $className, + 'source' => 'custom-rector', + 'configuration' => [], + ]; + } + continue; + } + + $patterns = []; + + // Extract from match($node->name->toString()) style: 'function_name' => ... + if (preg_match_all('/match\s*\([^)]+toString\(\)[^)]*\)[^{]*\{([^}]+)\}/s', $content, $blocks)) { + foreach ($blocks[1] as $block) { + if (preg_match_all('/[\'"]([a-z_][a-zA-Z0-9_]{4,})[\'"]/', $block, $fm)) { + foreach ($fm[1] as $name) { + $patterns[] = $name.'('; + } + } + } + } + + // Extract from case 'function_name': style + if (preg_match_all('/case\s+[\'"]([a-z_][a-zA-Z0-9_]{4,})[\'"]/', $content, $fm)) { + foreach ($fm[1] as $name) { + $patterns[] = $name.'('; + } + } + + // Extract from getName() === 'function_name' style + if (preg_match_all('/getName\(\)[^=]*===\s*[\'"]([a-z_][a-zA-Z0-9_]{4,})[\'"]/', $content, $fm)) { + foreach ($fm[1] as $name) { + $patterns[] = $name.'('; + } + } + + // Extract from string array keys: 'function_name' => SomeClass::method(...) + if (preg_match_all('/[\'"]([a-z_][a-zA-Z0-9_]{4,})[\'"][\s\n]*=>[\s\n]*(?:new |fn\s*\(|\[|\'|Drupal)/', $content, $fm)) { + foreach ($fm[1] as $name) { + $patterns[] = $name.'('; + } + } + + $patterns = array_values(array_unique($patterns)); + if (empty($patterns)) { + continue; + } + + $key = $className.'@'.$version; + if (!isset($registry[$key])) { + $registry[$key] = [ + 'label' => $className.' ['.$version.']', + 'patterns' => $patterns, + 'version' => $version, + 'class' => $customRectorFqcnMap[$className] ?? $className, + 'source' => 'custom-rector', + 'configuration' => [], + ]; + } +} + +// ============================================================ +// TIER 3: Fixture file diffs (fallback for remaining rectors) +// ============================================================ + +$fixtureDirs = array_merge( + glob("$rootDir/tests/src/Rector/*/fixture", GLOB_ONLYDIR) ?: [], + glob("$rootDir/tests/src/Rector/*/*/fixture", GLOB_ONLYDIR) ?: [] +); + +// Custom rectors already registered, plus addition-only rectors that are +// intentionally skipped so Tier 3 does not process their fixture diffs. +$registeredClasses = array_unique(array_merge( + array_column($registry, 'class'), + $skippedClasses +)); + +foreach ($fixtureDirs as $fixtureDir) { + // Skip bc/below-version variant dirs + if (str_contains($fixtureDir, 'fixture-')) { + continue; + } + + // Derive rector name from path + $parts = explode('/', rtrim($fixtureDir, '/')); + $fixtureIdx = array_search('fixture', $parts); + $rectorName = $parts[$fixtureIdx - 1] ?? ''; + + // Skip if already covered + if (in_array($rectorName, $registeredClasses, true)) { + continue; + } + + // Skip generic/abstract rectors + if (in_array($rectorName, ['AbstractDrupalCoreRector', 'FunctionToServiceRector', 'FunctionToStaticRector'], true)) { + continue; + } + + $patterns = []; + + foreach (glob("$fixtureDir/*.php.inc") ?: [] as $fixture) { + $fixtureContent = file_get_contents($fixture); + $parts = explode("\n-----\n", $fixtureContent, 2); + if (count($parts) < 2) { + continue; + } + + [$before, $after] = $parts; + + // Write temp files and diff to find removed lines + $tmpBefore = tempnam(sys_get_temp_dir(), 'before_'); + $tmpAfter = tempnam(sys_get_temp_dir(), 'after_'); + file_put_contents($tmpBefore, $before); + file_put_contents($tmpAfter, $after); + + exec(sprintf('diff %s %s', escapeshellarg($tmpBefore), escapeshellarg($tmpAfter)), $diffLines); + + foreach ($diffLines as $line) { + if (!str_starts_with($line, '< ')) { + continue; + } + $removed = ltrim(substr($line, 2)); + + // Skip trivial lines + if ( + strlen($removed) < 10 + || in_array($removed, ['', '{', '}', ''], true) + || str_starts_with($removed, '//') + || str_starts_with($removed, '*') + || str_starts_with($removed, 'namespace ') + || str_starts_with($removed, 'use ') + ) { + continue; + } + + $patterns[] = trim($removed); + } + + unlink($tmpBefore); + unlink($tmpAfter); + } + + $patterns = array_values(array_unique($patterns)); + if (empty($patterns)) { + continue; + } + + // Infer a plausible version from the patterns (look for rector class in src/) + $version = 'unknown'; + + $key = $rectorName.'@fixture'; + if (!isset($registry[$key])) { + $registry[$key] = [ + 'label' => $rectorName.' [fixture]', + 'patterns' => $patterns, + 'version' => $version, + 'class' => $customRectorFqcnMap[$rectorName] ?? $rectorName, + 'source' => 'fixture', + 'configuration' => [], + ]; + } +} + +// ============================================================ +// Scan patch files +// ============================================================ + +$patchFiles = glob("$patchesDir/*/*.patch") ?: []; +$totalPatches = count($patchFiles); + +if ($totalPatches === 0) { + fwrite(STDERR, "No .patch files found in: $patchesDir\n"); + exit(1); +} + +// Build a flat pattern → keys lookup for efficiency +$patternIndex = []; +foreach ($registry as $key => $entry) { + foreach (array_unique($entry['patterns']) as $pattern) { + $patternIndex[$pattern][] = $key; + } +} + +// $results[key] = [ +// 'modules' => [module => true], // which modules this rector matched +// 'patterns' => [pattern => moduleCount], // how many modules each pattern hit +// ] +$results = []; +$processed = 0; +$startTime = microtime(true); + +foreach ($patchFiles as $patchFile) { + $moduleName = basename(dirname($patchFile)); + $patchContent = file_get_contents($patchFile); + + $removedLines = extractPhpRemovedLines($patchContent); + if (empty($removedLines)) { + ++$processed; + continue; + } + + $removedText = implode("\n", $removedLines); + + foreach ($patternIndex as $pattern => $keys) { + if (str_contains($removedText, $pattern)) { + foreach ($keys as $key) { + $results[$key]['modules'][$moduleName] = true; + $results[$key]['patterns'][$pattern] = ($results[$key]['patterns'][$pattern] ?? 0) + 1; + } + } + } + + ++$processed; + if ($processed % 1000 === 0) { + $elapsed = round(microtime(true) - $startTime, 1); + fwrite(STDERR, "Processed $processed/$totalPatches patches ({$elapsed}s)...\r"); + } +} + +$elapsed = round(microtime(true) - $startTime, 1); +fwrite(STDERR, "Processed $totalPatches patches in {$elapsed}s. \n"); + +// ============================================================ +// Compile & sort output +// ============================================================ + +$rows = []; +foreach ($registry as $key => $entry) { + $res = $results[$key] ?? []; + $modules = array_keys($res['modules'] ?? []); + sort($modules); + + // Build per-pattern hit counts; patterns with 0 hits get 0 + $patternHits = []; + foreach (array_unique($entry['patterns']) as $p) { + $patternHits[$p] = $res['patterns'][$p] ?? 0; + } + arsort($patternHits); // highest hit count first + + $rows[] = [ + 'key' => $key, + 'class' => $entry['class'], + 'version' => $entry['version'], + 'label' => $entry['label'], + 'patternCount' => count($patternHits), + 'count' => count($modules), + 'modules' => $modules, + 'source' => $entry['source'], + 'patternHits' => $patternHits, + 'configuration' => $entry['configuration'] ?? [], + ]; +} + +// Sort by count descending, then by version, then by class name +usort($rows, static function (array $a, array $b): int { + if ($b['count'] !== $a['count']) { + return $b['count'] <=> $a['count']; + } + + return strnatcmp($a['label'], $b['label']); +}); + +// ============================================================ +// Output +// ============================================================ + +if ($csvMode) { + echo "rector,version,source,pattern_count,match_count,matched_patterns,sample_modules\n"; + foreach ($rows as $row) { + $matchedPatterns = implode(';', array_keys(array_filter($row['patternHits']))); + $sample = implode(';', array_slice($row['modules'], 0, 20)); + printf( + '"%s","%s","%s",%d,%d,"%s","%s"'."\n", + $row['class'], + $row['version'], + $row['source'], + $row['patternCount'], + $row['count'], + $matchedPatterns, + $sample + ); + } +} else { + $labelWidth = 52; + $verWidth = 6; + $matchWidth = 8; + + printf("%-{$labelWidth}s %-{$verWidth}s %-{$matchWidth}s %s\n", + 'Rector', 'Ver', 'Matches', 'Sample modules (first 5)'); + echo str_repeat('─', 120)."\n"; + + foreach ($rows as $row) { + $label = $row['class']; + if (strlen($label) > $labelWidth) { + $label = '…'.substr($label, -($labelWidth - 1)); + } + + if ($row['count'] === 0) { + $moduleStr = '—'; + } else { + $sample = array_slice($row['modules'], 0, 5); + $moduleStr = implode(', ', $sample); + if ($row['count'] > 5) { + $moduleStr .= ' … (+'.($row['count'] - 5).' more)'; + } + } + + printf("%-{$labelWidth}s %-{$verWidth}s %-{$matchWidth}d %s\n", + $label, $row['version'], $row['count'], $moduleStr); + + // Show "from" patterns with per-pattern hit count + foreach ($row['patternHits'] as $pattern => $hits) { + $indicator = $hits > 0 ? ' ✓' : ' ✗'; + $hitStr = $hits > 0 ? sprintf('%5d hits', $hits) : ' 0 hits'; + // Truncate long patterns (fixture-sourced full lines) + $displayPattern = strlen($pattern) > 60 ? substr($pattern, 0, 57).'...' : $pattern; + printf(" %s %-62s %s\n", $indicator, $displayPattern, $hitStr); + } + + foreach ($row['configuration'] as $cfg) { + printf(" · %s\n", $cfg); + } + + if (!empty($row['patternHits']) || !empty($row['configuration'])) { + echo "\n"; + } + } + + $withMatches = count(array_filter($rows, fn ($r) => $r['count'] > 0)); + printf("Patches: %d | Rectors tracked: %d | With matches: %d | Time: %ss\n", + $totalPatches, count($rows), $withMatches, $elapsed); +} + +// ============================================================ +// Helper: extract removed lines from .php/.module/.inc hunks only +// ============================================================ + +function extractPhpRemovedLines(string $patchContent): array +{ + static $phpExtensions = ['php', 'module', 'inc', 'install', 'theme', 'profile', 'engine']; + + $lines = explode("\n", $patchContent); + $result = []; + $inPhpHunk = false; + + foreach ($lines as $line) { + if (str_starts_with($line, 'diff --git ')) { + $inPhpHunk = false; + // "diff --git a/path/to/file.php b/path/to/file.php" + if (preg_match('/diff --git a\/(\S+)/', $line, $m)) { + $ext = strtolower(pathinfo($m[1], PATHINFO_EXTENSION)); + $inPhpHunk = in_array($ext, $phpExtensions, true); + } + continue; + } + + if (!$inPhpHunk) { + continue; + } + + // Collect removed lines (starts with - but NOT ---) + if (isset($line[0]) && $line[0] === '-' && (!isset($line[1]) || $line[1] !== '-')) { + $result[] = substr($line, 1); + } + } + + return $result; +} diff --git a/scripts/generate-deprecation-map.php b/scripts/generate-deprecation-map.php new file mode 100755 index 000000000..26a0d3f26 --- /dev/null +++ b/scripts/generate-deprecation-map.php @@ -0,0 +1,912 @@ +#!/usr/bin/env php + $phpArray = true, + '--debug' => $debug = true, + '--verify' => $verify = true, + '--help', '-h' => exitHelp(), + default => null, + }; +} + +function exitHelp(): never +{ + echo <<<'HELP' +Usage: + php scripts/generate-deprecation-map.php [--php-array] [--debug] [--verify] + +Options: + --php-array Output a PHP array keyed by deprecation message string. + --debug Print extracted URLs and index sizes to stderr. + --verify Run correctness assertions and exit non-zero on failure. + --help, -h Show this help. + +HELP; + exit(0); +} + +// ============================================================ +// U5 helpers: resolve git SHA → earliest containing tag. +// ============================================================ + +$tagCache = []; + +function resolveTag(string $rootDir, string $sha, array &$tagCache): string +{ + if (isset($tagCache[$sha])) { + return $tagCache[$sha]; + } + exec( + sprintf('git -C %s tag --contains %s 2>/dev/null | sort -V | head -1', + escapeshellarg($rootDir), escapeshellarg($sha)), + $out + ); + $tag = trim($out[0] ?? ''); + $result = $tag !== '' ? $tag : 'unreleased'; + + return $tagCache[$sha] = $result; +} + +function introducedForFile(string $rootDir, string $relPath, array &$tagCache): string +{ + exec( + sprintf('git -C %s log --diff-filter=A --format="%%H" -- %s 2>/dev/null | tail -1', + escapeshellarg($rootDir), escapeshellarg($relPath)), + $out + ); + $sha = trim($out[0] ?? ''); + + return $sha !== '' ? resolveTag($rootDir, $sha, $tagCache) : 'unreleased'; +} + +function relPath(string $rootDir, string $absPath): string +{ + return ltrim(str_replace($rootDir, '', $absPath), '/'); +} + +// ============================================================ +// U1: Rector source file crawler +// ============================================================ + +$rectorIndex = []; + +$customRectorFiles = array_merge( + glob("$rootDir/src/Drupal*/Rector/Deprecation/*.php") ?: [], + glob("$rootDir/src/Drupal*/Rector/Convert/*.php") ?: [] +); + +foreach ($customRectorFiles as $absPath) { + $content = file_get_contents($absPath); + + // Derive major version from directory (src/Drupal11/... → 11) + if (!preg_match('/\/Drupal(\d+)\//', $absPath, $vm)) { + continue; + } + $major = $vm[1]; + + // Extract the class-level docblock — the /** ... */ immediately before the class declaration. + if (!preg_match('/\/\*\*(.*?)\*\/\s*(?:(?:final|abstract|readonly)\s+)*class\s+/s', $content, $dm)) { + continue; + } + $docblock = $dm[1]; + + // All @see https://www.drupal.org/node/XXXXXX lines in the docblock. + if (!preg_match_all('/@see\s+https:\/\/www\.drupal\.org\/node\/(\d+)/', $docblock, $sm)) { + if ($debug) { + fwrite(STDERR, 'UNMAPPED (no @see): '.basename($absPath, '.php')."\n"); + } + continue; + } + + $nodeIds = $sm[1]; + // Convention: last @see = change record (appears in trigger_error strings), + // first @see = issue (may equal change_record when there's only one URL). + $changeRecordId = end($nodeIds); + $issueId = $nodeIds[0]; + + // FQCN from namespace declaration. + preg_match('/^namespace\s+([\w\\\\]+);/m', $content, $nm); + $namespace = $nm[1] ?? ''; + $className = basename($absPath, '.php'); + $fqcn = $namespace !== '' ? $namespace.'\\'.$className : $className; + $relPath = relPath($rootDir, $absPath); + + $rectorIndex[$className] = [ + 'class' => $className, + 'fqcn' => $fqcn, + 'file' => $relPath, + 'major' => $major, + 'issue_id' => $issueId, + 'change_record_id' => $changeRecordId, + 'type' => 'custom', + 'introduced' => introducedForFile($rootDir, $relPath, $tagCache), + 'configuration_class' => null, + 'configuration' => [], + 'source_content' => $content, + ]; +} + +// ============================================================ +// U2: Config file crawler for generic (config-driven) rector entries +// ============================================================ + +$configFiles = glob("$rootDir/config/drupal-*/*.php") ?: []; +sort($configFiles); + +$configIntroducedCache = []; + +foreach ($configFiles as $absPath) { + $base = basename($absPath); + + // Skip aggregate "all" files to avoid double-counting. + if (str_contains($base, '-all-')) { + continue; + } + + // Major version from filename (e.g. drupal-11.4-deprecations.php → 11). + if (!preg_match('/drupal-(\d+)(?:\.\d+)*-/', $base, $vm)) { + continue; + } + $major = $vm[1]; + $relPath = relPath($rootDir, $absPath); + $lines = file($absPath, FILE_IGNORE_NEW_LINES); + + $urlBuffer = []; + $configAccum = null; // non-null while buffering a ruleWithConfiguration([...]) body + $configIndexKey = null; // rector index key to attach configuration to + + foreach ($lines as $line) { + $trimmed = trim($line); + + // Inside a configuration array block: accumulate until ']);' + if ($configAccum !== null) { + $configAccum .= $line."\n"; + if (str_contains($trimmed, ']);')) { + // Extract configuration class name (first 'new XxxClass(' hit) + $cfgClass = null; + if (preg_match('/new\s+(\w+)\s*\(/', $configAccum, $cm)) { + $cfgClass = $cm[1]; + } + // Extract one entry per 'new XxxClass(args)' call: keep only string args in order + $cfgEntries = []; + if (preg_match_all('/new\s+\w+\s*\(([^)]*)\)/', $configAccum, $calls)) { + foreach ($calls[1] as $argsStr) { + preg_match_all('/[\'"]([^\'"]*)[\'"]/', $argsStr, $am); + $cfgEntries[] = $am[1]; + } + } + if ($configIndexKey !== null && isset($rectorIndex[$configIndexKey])) { + // Keep the first non-null class name seen; accumulate entries across + // multiple ruleWithConfiguration() blocks that share the same key + // (same rector + same CR appearing in several config file sections). + $rectorIndex[$configIndexKey]['configuration_class'] ??= $cfgClass; + $rectorIndex[$configIndexKey]['configuration'] = array_merge( + $rectorIndex[$configIndexKey]['configuration'], + $cfgEntries + ); + } + $configAccum = null; + $configIndexKey = null; + $urlBuffer = []; + } + continue; + } + + // Buffer every // https://www.drupal.org/node/XXXXXX comment. + if (preg_match('#^\s*//\s*https://www\.drupal\.org/node/(\d+)#', $line, $cm)) { + $urlBuffer[] = $cm[1]; + continue; + } + + // Other comment lines — preserve buffer (descriptive text between URL lines). + if (str_starts_with($trimmed, '//')) { + continue; + } + + // Blank lines — preserve buffer. + if ($trimmed === '') { + continue; + } + + // Code line: check if it's a rule() or ruleWithConfiguration() call. + if (preg_match('/\$rectorConfig->(rule|ruleWithConfiguration)\s*\(\s*(\w+)::class/', $line, $rm)) { + $rectorClass = $rm[2]; + $configIndexKey = null; + + if (!empty($urlBuffer)) { + // Last buffered URL = change record; first = issue. + $changeRecordId = end($urlBuffer); + $issueId = $urlBuffer[0]; + + // Skip if already indexed as a custom rector (its class docblock takes precedence). + if (!isset($rectorIndex[$rectorClass])) { + $key = $rectorClass.'@'.$changeRecordId; + if (!isset($rectorIndex[$key])) { + $configIntroducedCache[$relPath] ??= introducedForFile($rootDir, $relPath, $tagCache); + $rectorIndex[$key] = [ + 'class' => $rectorClass, + 'fqcn' => $rectorClass, + 'file' => $relPath, + 'major' => $major, + 'issue_id' => $issueId, + 'change_record_id' => $changeRecordId, + 'type' => 'config', + 'introduced' => $configIntroducedCache[$relPath], + 'configuration_class' => null, + 'configuration' => [], + ]; + } + $configIndexKey = $rectorClass.'@'.$changeRecordId; + } else { + // Custom rector from U1: attach configuration to its index entry. + $configIndexKey = $rectorClass; + } + } + + // For ruleWithConfiguration, start buffering the configuration array body. + if ($rm[1] === 'ruleWithConfiguration') { + $configAccum = $line."\n"; + continue; // URL buffer reset happens when the block closes + } + } + + // Any non-comment code line resets the buffer. + $urlBuffer = []; + } +} + +if ($debug) { + fwrite(STDERR, sprintf("Rector index: %d entries\n", count($rectorIndex))); + foreach (array_slice($rectorIndex, 0, 5, true) as $key => $e) { + fwrite(STDERR, sprintf(" %s: issue=%s change_record=%s major=%s\n", + $key, $e['issue_id'], $e['change_record_id'], $e['major'])); + } +} + +// ============================================================ +// U3: Drupal core trigger_error index (per major branch) +// ============================================================ + +$coreDir = "$rootDir/repos/drupal-core"; +$coreIndex = []; // $coreIndex[$major][$nodeId][] = $message + +function ensureCoreBranch(string $coreDir, string $major): string +{ + exec(sprintf('git -C %s branch -a 2>/dev/null', escapeshellarg($coreDir)), $rawBranches); + + $localBranches = []; + foreach ($rawBranches as $b) { + $b = trim($b, " *\t"); + // Strip remote tracking prefix (remotes/origin/...) + if (str_starts_with($b, 'remotes/')) { + $b = preg_replace('@^remotes/[^/]+/@', '', $b); + } + $localBranches[] = $b; + } + $localBranches = array_unique($localBranches); + + // 1. Prefer exact X.x branch (Drupal 11 development branch style). + if (in_array($major.'.x', $localBranches, true)) { + return $major.'.x'; + } + + // 2. Find the highest locally available X.Y.x branch for this major. + $minorCandidates = []; + foreach ($localBranches as $b) { + if (preg_match('/^'.preg_quote($major, '/').'\.(\d+)\.x$/', $b, $vm)) { + $minorCandidates[(int) $vm[1]] = $b; + } + } + if (!empty($minorCandidates)) { + krsort($minorCandidates); + + return array_values($minorCandidates)[0]; + } + + // 3. Not found locally: attempt to fetch X.x from origin. + $branch = $major.'.x'; + fwrite(STDERR, "Fetching $branch from origin (local gitcache)...\n"); + exec(sprintf('git -C %s fetch origin %s 2>&1', escapeshellarg($coreDir), escapeshellarg($branch)), $out, $rc); + if ($rc !== 0) { + fwrite(STDERR, "Warning: could not fetch $branch: ".implode("\n", $out)."\n"); + } + + return $branch; +} + +function buildCoreIndex(string $coreDir, string $major, bool $debug): array +{ + $branch = ensureCoreBranch($coreDir, $major); + $index = []; // $index[$nodeId][$message] = true — flattened to arrays at return + + $cmd = sprintf( + 'git -C %s grep -p "_DEPRECATED" %s -- "*.php" "*.module" "*.inc" 2>/dev/null', + escapeshellarg($coreDir), + escapeshellarg($branch) + ); + exec($cmd, $lines); + + $currentFunction = ''; + $currentFile = ''; + $matched = 0; + + foreach ($lines as $line) { + // Split on the first two colons: BRANCH:PATH:CONTENT (match) or BRANCH:PATH=DECL (context). + // Context lines always have = after the filepath; match lines have :. + $colonPos = strpos($line, ':'); + if ($colonPos === false) { + continue; + } + $rest = substr($line, $colonPos + 1); + + // Context line: rest = "filepath=function_decl" + $eqPos = strpos($rest, '='); + $coPos = strpos($rest, ':'); + + if ($eqPos !== false && ($coPos === false || $eqPos < $coPos)) { + $currentFile = substr($rest, 0, $eqPos); + // Extract function name from the declaration after '='. + $decl = substr($rest, $eqPos + 1); + if (preg_match('/function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/', $decl, $fm)) { + $currentFunction = $fm[1]; + } + continue; + } + + // Match line: rest = "filepath:content" + if ($coPos === false) { + continue; + } + $currentFile = substr($rest, 0, $coPos); + $content = substr($rest, $coPos + 1); + + if (!str_contains($content, '_DEPRECATED')) { + continue; + } + + // Skip @see docblock lines (they contain the node ID but are not trigger_error calls). + if (str_contains($content, '@see ')) { + continue; + } + + $message = extractTriggerErrorMessage($content, $currentFunction, $currentFile); + if ($message === null) { + continue; + } + + // The message must end with "See https://www.drupal.org/node/XXXXXX". + if (!preg_match('/See https:\/\/www\.drupal\.org\/node\/(\d+)/', $message, $nm)) { + continue; + } + + $nodeId = $nm[1]; + ++$matched; + + $index[$nodeId][$message] = true; + } + + if ($debug) { + fwrite(STDERR, sprintf("Core index [%s]: %d node IDs, %d messages\n", + $major, count($index), $matched)); + } + + return array_map('array_keys', $index); +} + +function classFromFilePath(string $filePath): string +{ + $className = basename($filePath, '.php'); + + // core/lib/Drupal/Ns1/.../Class.php → Drupal\Ns1\...\Class + if (preg_match('#core/lib/(.+)/[^/]+\.php$#', $filePath, $m)) { + return str_replace('/', '\\', $m[1]).'\\'.$className; + } + + // core/modules/M/src/[Sub/...]Class.php → Drupal\M[\Sub\...]\Class + if (preg_match('#core/modules/([^/]+)/src(/.*?)?/[^/]+\.php$#', $filePath, $m)) { + $sub = !empty($m[2]) ? str_replace('/', '\\', $m[2]) : ''; + + return 'Drupal\\'.$m[1].$sub.'\\'.$className; + } + + return $className; +} + +function resolveMethod(string $filePath, string $currentFunction): string +{ + if ($filePath === '' || $currentFunction === '') { + return '__METHOD__'; + } + + return classFromFilePath($filePath).'::'.$currentFunction; +} + +function extractTriggerErrorMessage(string $content, string $currentFunction, string $currentFile = ''): ?string +{ + // Case 0: __CLASS__ . '::' . __FUNCTION__ . 'rest' — class::method context + if (preg_match( + '/\@?trigger_error\s*\(\s*__CLASS__\s*\.\s*[\'"]::[\'"]\s*\.\s*__FUNCTION__\s*\.\s*([\'"])(.+?)\1\s*,\s*E_(?:USER_)?DEPRECATED/s', + $content, $m + )) { + return $currentFunction !== '' ? $currentFunction.$m[2] : $m[2]; + } + + // Case 1: __FUNCTION__ . 'rest' or __FUNCTION__ . "rest" + if (preg_match( + '/\@?trigger_error\s*\(\s*__FUNCTION__\s*\.\s*([\'"])(.+?)\1\s*,\s*E_(?:USER_)?DEPRECATED/s', + $content, $m + )) { + if ($currentFunction === '') { + return null; + } + $suffix = $m[2]; + // A trigger_error may concatenate __FUNCTION__ twice, e.g.: + // __FUNCTION__ . '() is deprecated ... ' . __FUNCTION__ . '() may also ...' + // The regex captures the suffix between the first quote pair, which can still + // contain the literal string ' . __FUNCTION__ . ' — resolve it here. + if (str_contains($suffix, '__FUNCTION__')) { + $suffix = preg_replace("/'\\s*\\.\\s*__FUNCTION__\\s*\\.\\s*'/", $currentFunction, $suffix); + $suffix = str_replace('__FUNCTION__', $currentFunction, $suffix); + } + + return $currentFunction.$suffix; + } + + // Case 2: __METHOD__ . 'rest' — resolve to ClassName::method using file path + function context + if (preg_match( + '/\@?trigger_error\s*\(\s*__METHOD__\s*\.\s*([\'"])(.+?)\1\s*,\s*E_(?:USER_)?DEPRECATED/s', + $content, $m + )) { + return resolveMethod($currentFile, $currentFunction).$m[2]; + } + + // Case 3: static string (single or double quote) — may embed magic constants literally + if (preg_match( + '/\@?trigger_error\s*\(\s*([\'"])(.+?)\1\s*,\s*E_(?:USER_)?DEPRECATED/s', + $content, $m + )) { + $message = $m[2]; + // Resolve embedded ' . __METHOD__ . ' / ' . __CLASS__ . ' / ' . __FUNCTION__ . ' patterns. + foreach ([ + '__METHOD__' => resolveMethod($currentFile, $currentFunction), + '__CLASS__' => classFromFilePath($currentFile), + '__FUNCTION__' => $currentFunction, + ] as $magic => $replacement) { + if ($replacement !== '' && str_contains($message, $magic)) { + $message = preg_replace( + "/'\\s*\\.\\s*".preg_quote($magic, '/')."\\s*\\.\\s*'/", + $replacement, + $message + ); + $message = str_replace($magic, $replacement, $message); + } + } + + return $message; + } + + // Case 4: sprintf(format, ...) — URL is embedded in the format string + if (preg_match( + '/\@?trigger_error\s*\(\s*sprintf\s*\(\s*([\'"])(.+?)\1/s', + $content, $m + )) { + $message = $m[2]; + if (str_contains($message, '%s') && $currentFunction !== '') { + // Replace first %s with resolved method name (common pattern: sprintf("Calling %s()...", __METHOD__)) + $method = resolveMethod($currentFile, $currentFunction); + $message = preg_replace('/%s/', $method, $message, 1); + } + + return $message; + } + + return null; +} + +// ============================================================ +// U3b: Drupal core @deprecated annotation index (per major branch) +// ============================================================ + +function buildDeprecatedAnnotationIndex(string $coreDir, string $major, bool $debug): array +{ + $branch = ensureCoreBranch($coreDir, $major); + $index = []; + + // Grep for @deprecated docblocks, capturing 20 lines of context after each match. + // This is enough to reach the @see URL that appears later in the same docblock. + $cmd = sprintf( + 'git -C %s grep -A 20 "@deprecated in drupal:" %s -- "*.php" "*.module" "*.inc" 2>/dev/null', + escapeshellarg($coreDir), + escapeshellarg($branch) + ); + + $handle = popen($cmd, 'r'); + if ($handle === false) { + return []; + } + + $pending = false; // true = inside a @deprecated docblock, scanning for @see + $annotation = ''; + $matched = 0; + + while (false !== ($line = fgets($handle))) { + $line = rtrim($line, "\n\r"); + + // Section separator between match groups. + if ($line === '--') { + $pending = false; + $annotation = ''; + continue; + } + + if (str_contains($line, '@deprecated in drupal:')) { + $pending = true; + // Capture the "in drupal:X.Y.0 ..." fragment as the human-readable message. + $annotation = '@deprecated annotation'; + if (preg_match('/@deprecated (in drupal:[^*\n]+)/', $line, $am)) { + $annotation = '@deprecated '.rtrim(trim($am[1]), ". \t"); + } + // @see may appear on the same line as @deprecated (rare, handle anyway). + if (preg_match('/@see\s+https:\/\/www\.drupal\.org\/node\/(\d+)/', $line, $sm)) { + $index[$sm[1]][$annotation] = true; + ++$matched; + $pending = false; + $annotation = ''; + } + continue; + } + + if (!$pending) { + continue; + } + + // End of docblock reached without finding @see. + if (str_contains($line, '*/')) { + $pending = false; + $annotation = ''; + continue; + } + + if (preg_match('/@see\s+https:\/\/www\.drupal\.org\/node\/(\d+)/', $line, $sm)) { + $index[$sm[1]][$annotation] = true; + ++$matched; + $pending = false; + $annotation = ''; + continue; + } + + // Accumulate continuation lines of the @deprecated text (e.g. " * Use Foo instead."). + // Stop accumulating at any new docblock tag (@param, @see, etc.). + if (preg_match('/\*\s+(\S.*)/', $line, $cm) && !str_starts_with(trim($cm[1]), '@')) { + $annotation = rtrim($annotation, ". \t").' '.trim($cm[1]); + } + } + + pclose($handle); + + if ($debug) { + fwrite(STDERR, sprintf("@deprecated annotation index [%s]: %d node IDs, %d entries\n", + $major, count($index), $matched)); + } + + return array_map('array_keys', $index); +} + +// Build core indices for all major versions referenced by our rector index. +$majorsNeeded = array_unique(array_column($rectorIndex, 'major')); +foreach ($majorsNeeded as $major) { + $triggerIndex = buildCoreIndex($coreDir, $major, $debug); + $annotIndex = buildDeprecatedAnnotationIndex($coreDir, $major, $debug); + // Merge annotation entries alongside trigger_error entries. + foreach ($annotIndex as $nodeId => $messages) { + foreach ($messages as $message) { + if (!in_array($message, $triggerIndex[$nodeId] ?? [], true)) { + $triggerIndex[$nodeId][] = $message; + } + } + } + $coreIndex[$major] = $triggerIndex; +} + +// ============================================================ +// U4: Join rector index with core index and build output rows +// ============================================================ + +/** + * Extract the pure snake_case function name from the start of a deprecation message. + * Returns '' for annotation messages, class::method patterns, or anything unrecognisable. + * We deliberately only filter on plain function names — FQCN/method messages are rarely + * the source of shared-CR false positives and are harder to match in source files. + * + * Handles three Drupal core trigger_error patterns: + * "func_name() is deprecated..." — normal + * "func_name is deprecated..." — no parens + * "func_nameis deprecated..." — Drupal core typo (missing space+parens) + */ +function extractFunctionName(string $message): string +{ + if (str_starts_with($message, '@deprecated')) { + return ''; + } + // Lazy match: stop as soon as we see () OR a lookahead for "is deprecated" + // (with or without a preceding space/parens). + if (preg_match('/^([a-z_][a-z0-9_]*?)(?:\(\)|(?=is\s+deprecated)|(?=\s+is\s+deprecated))/', $message, $m)) { + return $m[1]; + } + + return ''; +} + +/** + * Return true if this rector entry is known to handle the given plain function name. + * + * Only custom rectors are filtered: their source file is the definitive record of + * which function(s) they target, so str_contains() is reliable. + * + * Config rectors are NOT filtered here because many use omnibus + * ruleWithConfiguration() blocks that span functions from multiple change records. + * The CR attached to the block may not match the trigger_error CR for every function + * listed, so excluding them would silently drop valid coverage rows. + */ +function rectorHandlesFunction(array $entry, string $funcName): bool +{ + if ($entry['type'] !== 'custom') { + return true; + } + + return isset($entry['source_content']) && str_contains($entry['source_content'], $funcName); +} + +$csvRows = []; + +foreach ($rectorIndex as $entry) { + $major = $entry['major']; + $changeRecordId = $entry['change_record_id']; + $messages = $coreIndex[$major][$changeRecordId] ?? []; + + if (empty($messages)) { + fwrite(STDERR, sprintf("UNMAPPED: %s (change_record: [%s](https://www.drupal.org/node/%s), major: %s, introduced: %s)\n", + $entry['class'], $changeRecordId, $changeRecordId, $major, $entry['introduced'])); + continue; + } + + foreach ($messages as $message) { + // When multiple rectors share a CR, each rector only covers a subset of the + // deprecation messages for that CR. Filter out messages whose deprecated + // function name does not appear in this rector's source or configuration. + $funcName = extractFunctionName($message); + if ($funcName !== '' && !rectorHandlesFunction($entry, $funcName)) { + continue; + } + + $csvRows[] = [ + 'rector_class' => $entry['class'], + 'fqcn' => $entry['fqcn'], + 'source_path' => $entry['file'], + 'issue_node_id' => $entry['issue_id'], + 'change_record_node_id' => $changeRecordId, + 'deprecation_message' => $message, + 'introduced' => $entry['introduced'], + 'configuration_class' => $entry['configuration_class'] ?? null, + 'configuration' => $entry['configuration'] ?? [], + ]; + } +} + +// ============================================================ +// Output dispatch +// ============================================================ + +if ($verify) { + runVerify($csvRows, $coreIndex, $rectorIndex); +} elseif ($phpArray) { + outputPhpArray($csvRows); +} else { + outputCsv($csvRows); +} + +// ============================================================ +// Output: CSV +// ============================================================ + +function outputCsv(array $rows): void +{ + echo "rector_class,source_path,issue_node_id,change_record_node_id,deprecation_message,introduced\n"; + foreach ($rows as $row) { + printf( + '"%s","%s","%s","%s","%s","%s"'."\n", + csvEsc($row['rector_class']), + csvEsc($row['source_path']), + csvEsc($row['issue_node_id']), + csvEsc($row['change_record_node_id']), + csvEsc($row['deprecation_message']), + csvEsc($row['introduced']) + ); + } +} + +function csvEsc(string $s): string +{ + return str_replace('"', '""', $s); +} + +// ============================================================ +// Output: PHP array +// ============================================================ + +function phpSqEsc(string $s): string +{ + return str_replace(['\\', "'"], ['\\\\', "\\'"], $s); +} + +function outputPhpArray(array $rows): void +{ + echo " [\n"; + echo " 'class' => '$cls',\n"; + echo " 'issue' => '{$row['issue_node_id']}',\n"; + echo " 'change_record' => '{$row['change_record_node_id']}',\n"; + echo " 'introduced' => '$introduced',\n"; + if ($row['configuration_class'] !== null) { + $cfgCls = phpSqEsc($row['configuration_class']); + echo " 'configuration_class' => '$cfgCls',\n"; + if (!empty($row['configuration'])) { + echo " 'configuration' => [\n"; + foreach ($row['configuration'] as $args) { + $parts = array_map(static fn ($a) => "'".phpSqEsc((string) $a)."'", $args); + echo ' ['.implode(', ', $parts)."],\n"; + } + echo " ],\n"; + } + } + echo " ],\n"; + } + echo "];\n"; +} + +// ============================================================ +// U6: --verify mode +// ============================================================ + +function runVerify(array $csvRows, array $coreIndex, array $rectorIndex): never +{ + $passes = 0; + $fails = 0; + + $check = static function (bool $ok, string $desc) use (&$passes, &$fails): void { + if ($ok) { + echo "[PASS] $desc\n"; + ++$passes; + } else { + echo "[FAIL] $desc\n"; + ++$fails; + } + }; + + // Index rows by rector class for easy lookup. + $byClass = []; + foreach ($csvRows as $row) { + $byClass[$row['rector_class']][] = $row; + } + + // A1: CheckMarkupToProcessedTextRector has correct issue and change_record IDs. + $cmRows = $byClass['CheckMarkupToProcessedTextRector'] ?? []; + $check( + !empty($cmRows) + && ($cmRows[0]['issue_node_id'] ?? '') === '455724' + && ($cmRows[0]['change_record_node_id'] ?? '') === '3588040', + 'CheckMarkupToProcessedTextRector: issue=455724 https://www.drupal.org/i/455724, change_record=3588040 https://www.drupal.org/i/3588040' + ); + + // A2: CheckMarkupToProcessedTextRector deprecation message starts with expected prefix. + $cmMsg = $cmRows[0]['deprecation_message'] ?? ''; + $check( + str_starts_with($cmMsg, 'check_markup() is deprecated in drupal:11.4.0'), + 'CheckMarkupToProcessedTextRector: message starts with "check_markup() is deprecated in drupal:11.4.0"' + ); + + // A3: change_record 3588040 is indexed under major 11 (confirming 11.x branch was used). + $check( + isset($coreIndex['11']['3588040']), + 'CheckMarkupToProcessedTextRector: change record 3588040 https://www.drupal.org/i/3588040 sourced from 11.x branch' + ); + + // A4: FilterFormatFunctionsToServiceRector has ≥5 rows for change_record 3035368. + $ffRows = array_filter( + $byClass['FilterFormatFunctionsToServiceRector'] ?? [], + static fn ($r) => $r['change_record_node_id'] === '3035368' + ); + $check(count($ffRows) >= 5, sprintf( + 'FilterFormatFunctionsToServiceRector: ≥5 rows for change_record=3035368 https://www.drupal.org/i/3035368 (found %d)', + count($ffRows) + )); + + // A5: All five expected filter function message prefixes appear. + $expectedPrefixes = [ + 'filter_formats(', + 'filter_fallback_format(', + 'filter_get_roles_by_format(', + 'filter_get_formats_by_role(', + 'filter_default_format(', + ]; + $foundPrefixes = []; + foreach ($ffRows as $r) { + foreach ($expectedPrefixes as $prefix) { + if (str_starts_with($r['deprecation_message'], $prefix)) { + $foundPrefixes[$prefix] = true; + } + } + } + $check( + count($foundPrefixes) === count($expectedPrefixes), + sprintf('FilterFormatFunctionsToServiceRector: all 5 expected function message prefixes found (%d/5)', + count($foundPrefixes)) + ); + + // A6: All rows have non-empty introduced value. + $emptyIntroduced = array_filter($csvRows, static fn ($r) => trim($r['introduced']) === ''); + $check(empty($emptyIntroduced), sprintf( + 'All %d rows have non-empty introduced value', + count($csvRows) + )); + + // A7: All rows have non-empty deprecation_message (catches silent join failures). + $emptyMsg = array_filter($csvRows, static fn ($r) => trim($r['deprecation_message']) === ''); + $check(empty($emptyMsg), sprintf( + 'All %d rows have non-empty deprecation_message', + count($csvRows) + )); + + // A8: Total row count ≥ 350 (342 trigger_error rows + annotation entries). + $check(count($csvRows) >= 350, sprintf('Total rows: %d (≥ 350)', count($csvRows))); + + // A9: No date-shaped introduced values (all should be tags or "unreleased"). + $dateRows = array_filter($csvRows, static fn ($r) => (bool) preg_match('/^\d{4}-\d{2}-\d{2}/', $r['introduced'])); + $check(empty($dateRows), 'No date-shaped introduced values (all are version tags or "unreleased")'); + + // A10: A known D10 deprecation is indexed from 10.x branch but absent from 11.x. + // Uses watchdog_exception() (change_record=2932520, deprecated in 10.1.0, removed in 11.0.0) + // as the routing test: it must appear in coreIndex[10] but not coreIndex[11]. + $d10InTen = isset($coreIndex['10']['2932520']); + $d10NotInElev = !isset($coreIndex['11']['2932520']); + $check( + $d10InTen && $d10NotInElev, + 'watchdog_exception() (change_record=2932520 https://www.drupal.org/i/2932520): found in 10.x branch index, absent from 11.x (confirms branch routing)' + ); + + $total = $passes + $fails; + echo "\n$passes/$total checks passed.\n"; + exit($fails > 0 ? 1 : 0); +} diff --git a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php index 6c7bc649e..fde3ba283 100644 --- a/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php +++ b/src/Drupal10/Rector/Deprecation/AnnotationToAttributeRector.php @@ -8,6 +8,7 @@ use DrupalRector\Drupal10\Rector\ValueObject\AnnotationToAttributeConfiguration; use DrupalRector\Rector\AbstractDrupalCoreRector; use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; +use DrupalRector\Services\DrupalRectorSettings; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Attribute; @@ -57,8 +58,9 @@ final class AnnotationToAttributeRector extends AbstractDrupalCoreRector impleme */ private AnnotationToAttributeMapper $annotationToAttributeMapper; - public function __construct(PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $docBlockUpdater, PhpDocInfoFactory $phpDocInfoFactory, ArrayParser $arrayParser, TokenIteratorFactory $tokenIteratorFactory, AnnotationToAttributeMapper $annotationToAttributeMapper) + public function __construct(DrupalRectorSettings $drupalRectorSettings, PhpDocTagRemover $phpDocTagRemover, DocBlockUpdater $docBlockUpdater, PhpDocInfoFactory $phpDocInfoFactory, ArrayParser $arrayParser, TokenIteratorFactory $tokenIteratorFactory, AnnotationToAttributeMapper $annotationToAttributeMapper) { + parent::__construct($drupalRectorSettings); $this->phpDocTagRemover = $phpDocTagRemover; $this->docBlockUpdater = $docBlockUpdater; $this->phpDocInfoFactory = $phpDocInfoFactory; diff --git a/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector.php b/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector.php new file mode 100644 index 000000000..96da5ab4d --- /dev/null +++ b/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector.php @@ -0,0 +1,80 @@ +isName($node->name, 'getName')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Extension\ModuleHandlerInterface'))) { + return null; + } + + $service = new Node\Expr\StaticCall( + new Node\Name\FullyQualified('Drupal'), + 'service', + [new Node\Arg(new Node\Scalar\String_('extension.list.module'))] + ); + + return new Node\Expr\MethodCall($service, 'getName', $node->args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition("Replaces deprecated ModuleHandlerInterface::getName() with \\Drupal::service('extension.list.module')->getName()", [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$this->moduleHandler->getName($module); +CODE_BEFORE, + <<<'CODE_AFTER' +\Drupal::service('extension.list.module')->getName($module); +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('10.3.0')] + ), + ]); + } +} diff --git a/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector.php b/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector.php new file mode 100644 index 000000000..9e26f391f --- /dev/null +++ b/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector.php @@ -0,0 +1,86 @@ +isName($node->name, 'rebuildThemeData')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Extension\ThemeHandlerInterface'))) { + return null; + } + + if (!empty($node->args)) { + return null; + } + + $service = new Node\Expr\StaticCall( + new Node\Name\FullyQualified('Drupal'), + 'service', + [new Node\Arg(new Node\Scalar\String_('extension.list.theme'))] + ); + + $reset = new Node\Expr\MethodCall($service, 'reset'); + + return new Node\Expr\MethodCall($reset, 'getList'); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition("Replaces removed ThemeHandlerInterface::rebuildThemeData() with \\Drupal::service('extension.list.theme')->reset()->getList()", [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$this->themeHandler->rebuildThemeData(); +CODE_BEFORE, + <<<'CODE_AFTER' +\Drupal::service('extension.list.theme')->reset()->getList(); +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('10.3.0')] + ), + ]); + } +} diff --git a/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector.php b/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector.php new file mode 100644 index 000000000..cfe5a079e --- /dev/null +++ b/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector.php @@ -0,0 +1,69 @@ +getRequestTime(). + * + * Deprecated in drupal:8.3.0, removed in drupal:11.0.0. + * + * @see https://www.drupal.org/node/3395986 + */ +final class ReplaceRequestTimeConstantRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Node\Expr\ConstFetch::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Node\Expr\ConstFetch); + + if (!$this->isName($node, 'REQUEST_TIME')) { + return null; + } + + $staticCall = new Node\Expr\StaticCall( + new Node\Name\FullyQualified('Drupal'), + new Node\Identifier('time') + ); + + return new Node\Expr\MethodCall($staticCall, new Node\Identifier('getRequestTime')); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated REQUEST_TIME constant with \\Drupal::time()->getRequestTime() (deprecated drupal:8.3.0, removed drupal:11.0.0)', [ + new ConfiguredCodeSample( + '$cutoff = REQUEST_TIME - $lifespan;', + '$cutoff = \\Drupal::time()->getRequestTime() - $lifespan;', + [new DrupalIntroducedVersionConfiguration('11.0.0')] + ), + ]); + } +} diff --git a/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector.php b/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector.php index 002445d20..9c050c8fb 100644 --- a/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector.php +++ b/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector.php @@ -7,6 +7,7 @@ use DrupalRector\Contract\VersionedConfigurationInterface; use DrupalRector\Rector\AbstractDrupalCoreRector; use DrupalRector\Rector\ValueObject\DrupalIntroducedVersionConfiguration; +use DrupalRector\Services\DrupalRectorSettings; use PhpParser\Node; use PhpParser\Node\Expr\ConstFetch; use Rector\PhpParser\Node\Value\ValueResolver; @@ -25,8 +26,9 @@ class SystemTimeZonesRector extends AbstractDrupalCoreRector */ private ValueResolver $valueResolver; - public function __construct(ValueResolver $valueResolver) + public function __construct(DrupalRectorSettings $drupalRectorSettings, ValueResolver $valueResolver) { + parent::__construct($drupalRectorSettings); $this->valueResolver = $valueResolver; } @@ -63,6 +65,15 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte return $this->nodeFactory->createStaticCall('Drupal\Core\Datetime\TimeZoneFormHelper', 'getOptionsListByRegion'); } + if (count($args) == 2 && !($args[1]->value instanceof ConstFetch && $this->valueResolver->isFalse($args[1]->value))) { + // Second arg is dynamic or not a literal FALSE — emit a ternary so the grouped flag is preserved. + return new Node\Expr\Ternary( + $args[1]->value, + $this->nodeFactory->createStaticCall('Drupal\Core\Datetime\TimeZoneFormHelper', 'getOptionsListByRegion', [$args[0]]), + $this->nodeFactory->createStaticCall('Drupal\Core\Datetime\TimeZoneFormHelper', 'getOptionsList', [$args[0]]) + ); + } + if (count($args) == 0) { return $this->nodeFactory->createStaticCall('Drupal\Core\Datetime\TimeZoneFormHelper', 'getOptionsList'); } diff --git a/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php b/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php deleted file mode 100644 index 8ec7105f0..000000000 --- a/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php +++ /dev/null @@ -1,82 +0,0 @@ -getName($node->name) === $configuration->getDeprecatedFunctionName()) { - // This creates a service call like `\Drupal::service('file_system'). - $service = new Node\Expr\StaticCall(new Node\Name\FullyQualified('Drupal'), 'service', [new Node\Arg(new Node\Scalar\String_($configuration->getServiceName()))]); - - $method_name = new Node\Identifier($configuration->getServiceMethodName()); - - return new Node\Expr\MethodCall($service, $method_name, $node->args); - } - - return null; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Fixes deprecated function to service calls, used in Drupal 8 and 9 deprecations', [ - new ConfiguredCodeSample( - <<<'CODE_BEFORE' -_drupal_flush_css_js(); -CODE_BEFORE, - <<<'CODE_AFTER' -\Drupal::service('asset.query_string')->reset(); -CODE_AFTER, - [ - new VersionedFunctionToServiceConfiguration('10.2.0', '_drupal_flush_css_js', 'asset.query_string', 'reset'), - ] - ), - ]); - } -} diff --git a/src/Drupal10/Rector/ValueObject/VersionedFunctionToServiceConfiguration.php b/src/Drupal10/Rector/ValueObject/VersionedFunctionToServiceConfiguration.php deleted file mode 100644 index e00681627..000000000 --- a/src/Drupal10/Rector/ValueObject/VersionedFunctionToServiceConfiguration.php +++ /dev/null @@ -1,55 +0,0 @@ -deprecatedFunctionName = $deprecatedFunctionName; - $this->serviceName = $serviceName; - $this->serviceMethodName = $serviceMethodName; - $this->introducedVersion = $introducedVersion; - } - - public function getDeprecatedFunctionName(): string - { - return $this->deprecatedFunctionName; - } - - public function getServiceName(): string - { - return $this->serviceName; - } - - public function getServiceMethodName(): string - { - return $this->serviceMethodName; - } - - public function getIntroducedVersion(): string - { - return $this->introducedVersion; - } -} diff --git a/src/Drupal11/Rector/Deprecation/.gitkeep b/src/Drupal11/Rector/Deprecation/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector.php b/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector.php new file mode 100644 index 000000000..e44d6dd89 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector.php @@ -0,0 +1,88 @@ + 'basic'] instead of a plain string. + * + * @see https://www.drupal.org/node/3196937 + * @see https://www.drupal.org/node/3473739 + */ +class BlockContentTestBaseStringToArrayRector extends AbstractRector +{ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + "Replace deprecated string \$values in BlockContentTestBase::createBlockContentType() with an ['id' => ...] array", + [ + new CodeSample( + <<<'CODE_BEFORE' +$this->createBlockContentType('basic', TRUE); +CODE_BEFORE, + <<<'CODE_AFTER' +$this->createBlockContentType(['id' => 'basic'], TRUE); +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** @param MethodCall $node */ + public function refactor(Node $node): ?Node + { + if (!$this->isName($node->name, 'createBlockContentType')) { + return null; + } + + if (count($node->args) === 0) { + return null; + } + + $firstArg = $node->args[0]; + if (!$firstArg instanceof Arg) { + return null; + } + + if (!$firstArg->value instanceof String_) { + return null; + } + + // Skip InlineBlockTestBase::createBlockContentType($id, $label) — two string args. + if (isset($node->args[1])) { + $secondArg = $node->args[1]; + if ($secondArg instanceof Arg && $secondArg->value instanceof String_) { + return null; + } + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Tests\block_content\Traits\BlockContentCreationTrait'))) { + return null; + } + + $firstArg->value = new Array_([new ArrayItem($firstArg->value, new String_('id'))]); + + return $node; + } +} diff --git a/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector.php b/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector.php new file mode 100644 index 000000000..fcfe30ee9 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector.php @@ -0,0 +1,97 @@ + '#text', + 'format_id' => '#format', + 'langcode' => '#langcode', + 'filter_types_to_skip' => '#filter_types_to_skip', + ]; + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated check_markup() calls with a processed_text render array.', + [ + new CodeSample( + <<<'CODE_BEFORE' +check_markup($text, $format_id); +CODE_BEFORE, + <<<'CODE_AFTER' +['#type' => 'processed_text', '#text' => $text, '#format' => $format_id]; +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + public function refactor(Node $node): ?Node + { + assert($node instanceof FuncCall); + + if (!$this->isName($node->name, 'check_markup')) { + return null; + } + + $args = $node->args; + if (count($args) === 0) { + return null; + } + + $items = [ + new ArrayItem(new String_('processed_text'), new String_('#type')), + ]; + + $paramNames = array_keys(self::PARAM_MAP); + $positionalIndex = 0; + + foreach ($args as $arg) { + if (!$arg instanceof Arg) { + continue; + } + + if ($arg->name !== null) { + $paramName = $arg->name->toString(); + if (isset(self::PARAM_MAP[$paramName])) { + $items[] = new ArrayItem($arg->value, new String_(self::PARAM_MAP[$paramName])); + } + } else { + if (isset($paramNames[$positionalIndex])) { + $key = self::PARAM_MAP[$paramNames[$positionalIndex]]; + $items[] = new ArrayItem($arg->value, new String_($key)); + } + + ++$positionalIndex; + } + } + + return new Array_($items); + } +} diff --git a/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector.php b/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector.php new file mode 100644 index 000000000..b3b71ac62 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector.php @@ -0,0 +1,126 @@ + + */ + private const FUNCTION_TO_PLUGIN_ID = [ + '_filter_autop' => 'filter_autop', + '_filter_html_escape' => 'filter_html_escape', + '_filter_html_image_secure_process' => 'filter_html_image_secure', + ]; + + /** @var DrupalIntroducedVersionConfiguration[] */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated _filter_autop(), _filter_html_escape(), and _filter_html_image_secure_process() with plugin manager calls.', + [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +_filter_autop($text) +CODE_BEFORE, + <<<'CODE_AFTER' +\Drupal::service('plugin.manager.filter')->createInstance('filter_autop')->process($text, \Drupal::languageManager()->getCurrentLanguage()->getId())->getProcessedText() +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + $funcName = $this->getName($node->name); + + if (!isset(self::FUNCTION_TO_PLUGIN_ID[$funcName])) { + return null; + } + + if (count($node->args) < 1) { + return null; + } + + $pluginId = self::FUNCTION_TO_PLUGIN_ID[$funcName]; + + $textArg = $node->args[0]; + $textExpr = $textArg instanceof Arg ? $textArg->value : $textArg; + + // Build: \Drupal::languageManager()->getCurrentLanguage()->getId() + $drupalLanguageManager = $this->nodeFactory->createStaticCall('Drupal', 'languageManager'); + $getCurrentLanguage = $this->nodeFactory->createMethodCall($drupalLanguageManager, 'getCurrentLanguage'); + $getLangcodeExpr = $this->nodeFactory->createMethodCall($getCurrentLanguage, 'getId'); + + // Build: \Drupal::service('plugin.manager.filter') + $drupalService = $this->nodeFactory->createStaticCall('Drupal', 'service', [ + new String_('plugin.manager.filter'), + ]); + + // ->createInstance('filter_autop') (or other plugin id) + $createInstance = $this->nodeFactory->createMethodCall($drupalService, 'createInstance', [ + new String_($pluginId), + ]); + + // ->process($text, $langcode) + $process = $this->nodeFactory->createMethodCall($createInstance, 'process', [ + $textExpr, + $getLangcodeExpr, + ]); + + // ->getProcessedText() + return $this->nodeFactory->createMethodCall($process, 'getProcessedText'); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector.php b/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector.php new file mode 100644 index 000000000..6d2783886 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector.php @@ -0,0 +1,75 @@ +> */ + public function getNodeTypes(): array + { + return [StaticCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof StaticCall); + if (!$this->isName($node->name, 'currentErrorHandler')) { + return null; + } + if (!$this->isObjectType($node->class, new ObjectType('Drupal\Core\Utility\Error'))) { + return null; + } + + return new FuncCall(new Name('get_error_handler'), []); + } +} diff --git a/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector.php b/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector.php new file mode 100644 index 000000000..2c13ec158 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector.php @@ -0,0 +1,91 @@ +> */ + public function getNodeTypes(): array + { + return [String_::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof String_); + + if ($node->value !== self::DEPRECATED_FUNCTION) { + return null; + } + + return new Array_([ + new ArrayItem( + new ClassConstFetch( + new FullyQualified(self::NEW_CLASS), + 'class' + ) + ), + new ArrayItem( + new String_(self::NEW_METHOD) + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector.php b/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector.php new file mode 100644 index 000000000..7fa31fe07 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector.php @@ -0,0 +1,85 @@ +basename($uri, $suffix);', + 'basename($uri, $suffix);', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof MethodCall); + if (!$this->isName($node->name, 'basename')) { + return null; + } + + $isFileSystem = false; + foreach (['Drupal\Core\File\FileSystemInterface', 'Drupal\Core\File\FileSystem'] as $class) { + if ($this->isObjectType($node->var, new ObjectType($class))) { + $isFileSystem = true; + break; + } + } + + if (!$isFileSystem) { + return null; + } + + return new FuncCall(new Name('basename'), $node->getArgs()); + } +} diff --git a/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector.php b/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector.php new file mode 100644 index 000000000..a125dbc73 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector.php @@ -0,0 +1,123 @@ +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + return match ($node->name->toString()) { + 'filter_fallback_format' => $this->buildServiceCall('getFallbackFormatId', []), + 'filter_formats' => count($node->args) === 0 + ? $this->buildServiceCall('getAllFormats', []) + : $this->buildServiceCall('getFormatsForAccount', [$node->args[0]->value]), + 'filter_get_roles_by_format' => count($node->args) >= 1 + ? new MethodCall($node->args[0]->value, 'getRoles') + : null, + 'filter_get_formats_by_role' => count($node->args) >= 1 + ? $this->buildServiceCall('getFormatsByRole', [$node->args[0]->value]) + : null, + 'filter_default_format' => $this->buildDefaultFormatCall($node), + default => null, + }; + } + + private function buildDefaultFormatCall(FuncCall $node): MethodCall + { + $args = count($node->args) > 0 ? [$node->args[0]->value] : []; + + return new MethodCall( + $this->buildServiceCall('getDefaultFormat', $args), + 'id' + ); + } + + /** @param Node\Expr[] $argExprs */ + private function buildServiceCall(string $method, array $argExprs): MethodCall + { + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch(new FullyQualified(self::SERVICE_CLASS), 'class'))] + ); + + $args = array_map(static fn ($expr) => new Arg($expr), $argExprs); + + return new MethodCall($serviceCall, $method, $args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated filter module procedural functions with FilterFormatRepositoryInterface service methods.', + [ + new ConfiguredCodeSample( + 'filter_fallback_format();', + '\Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getFallbackFormatId();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'filter_formats();', + '\Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getAllFormats();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'filter_get_roles_by_format($format);', + '$format->getRoles();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/GetNameToNameRector.php b/src/Drupal11/Rector/Deprecation/GetNameToNameRector.php new file mode 100644 index 000000000..ac8d43509 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/GetNameToNameRector.php @@ -0,0 +1,76 @@ +getName()', + '$this->name()' + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** @param MethodCall $node */ + public function refactor(Node $node): ?Node + { + if (!$this->isName($node->name, 'getName')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('PHPUnit\\Framework\\TestCase'))) { + return null; + } + + $args = $node->args; + if (count($args) === 0) { + // No args — replace directly. + } elseif (count($args) === 1) { + $arg = $args[0]; + if (!$arg instanceof Node\Arg) { + return null; + } + if (!$arg->value instanceof Node\Expr\ConstFetch) { + return null; + } + $constName = $this->getName($arg->value->name); + if (strtolower((string) $constName) !== 'false') { + return null; + } + } else { + return null; + } + + $node->name = new Identifier('name'); + $node->args = []; + + return $node; + } +} diff --git a/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector.php b/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector.php new file mode 100644 index 000000000..997571408 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector.php @@ -0,0 +1,82 @@ +> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + if (!$node instanceof MethodCall) { + return null; + } + + if (!$this->isName($node->name, 'getOriginalClass')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Entity\EntityTypeInterface'))) { + return null; + } + + $newMethodCall = new MethodCall($node->var, 'getDecoratedClasses', []); + + return new ArrayDimFetch($newMethodCall, new Int_(0)); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated EntityTypeInterface::getOriginalClass() with getDecoratedClasses()[0].', + [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$originalClass = $entityType->getOriginalClass(); +CODE_BEFORE, + <<<'CODE_AFTER' +$originalClass = $entityType->getDecoratedClasses()[0]; +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector.php b/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector.php new file mode 100644 index 000000000..6b4efb48d --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector.php @@ -0,0 +1,78 @@ +expr instanceof Node\Expr\MethodCall) { + return null; + } + + $methodCall = $node->expr; + + if (!$this->isName($methodCall->name, 'loadAllIncludes')) { + return null; + } + + if (!$this->isObjectType($methodCall->var, new ObjectType('Drupal\Core\Extension\ModuleHandlerInterface'))) { + return null; + } + + $caller = $methodCall->var; + $getModuleListCall = new Node\Expr\MethodCall(clone $caller, 'getModuleList'); + + $moduleVar = new Node\Expr\Variable('module'); + $filenameVar = new Node\Expr\Variable('filename'); + + $loadIncludeArgs = [new Node\Arg($moduleVar)]; + foreach ($methodCall->args as $arg) { + $loadIncludeArgs[] = $arg; + } + + $loadIncludeCall = new Node\Expr\MethodCall(clone $caller, 'loadInclude', $loadIncludeArgs); + + return new Node\Stmt\Foreach_( + $getModuleListCall, + $filenameVar, + [ + 'keyVar' => $moduleVar, + 'stmts' => [new Node\Stmt\Expression($loadIncludeCall)], + ] + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated ModuleHandler::loadAllIncludes() with getModuleList() + loadInclude() loop', [ + new CodeSample( + "\$this->moduleHandler->loadAllIncludes('install');", + "foreach (\$this->moduleHandler->getModuleList() as \$module => \$filename) {\n \$this->moduleHandler->loadInclude(\$module, 'install');\n}" + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector.php b/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector.php new file mode 100644 index 000000000..284c28116 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector.php @@ -0,0 +1,132 @@ +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + return match ($node->name->toString()) { + 'locale_translation_flush_projects' => $this->buildServiceCall(self::LOCALE_PROJECT_REPOSITORY, 'deleteAll', $node->args), + 'locale_translation_build_projects' => $this->buildServiceCall(self::LOCALE_PROJECT_REPOSITORY, 'buildProjects', $node->args), + 'locale_translation_check_projects' => $this->buildCheckerCall('checkProjects', $node->args), + 'locale_translation_check_projects_local' => $this->buildCheckerCall('checkLocalProjects', $node->args), + default => null, + }; + } + + /** + * Builds a checker service call, expanding empty $projects to array_keys(getAll()). + * + * @param Arg[] $args + */ + private function buildCheckerCall(string $method, array $args): MethodCall + { + if (count($args) === 0) { + $getAll = new MethodCall( + $this->buildDrupalServiceCall(self::LOCALE_PROJECT_REPOSITORY), + 'getAll', + [] + ); + $args = [new Arg(new FuncCall(new Name('array_keys'), [new Arg($getAll)]))]; + } + + return $this->buildServiceCall(self::LOCALE_PROJECT_CHECKER, $method, $args); + } + + /** @param Arg[] $args */ + private function buildServiceCall(string $serviceClass, string $method, array $args): MethodCall + { + return new MethodCall($this->buildDrupalServiceCall($serviceClass), $method, $args); + } + + private function buildDrupalServiceCall(string $serviceClass): StaticCall + { + return new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch(new FullyQualified($serviceClass), 'class'))] + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated locale.compare.inc functions with LocaleProjectRepository and LocaleProjectChecker service methods.', + [ + new ConfiguredCodeSample( + 'locale_translation_flush_projects();', + '\Drupal::service(\Drupal\locale\LocaleProjectRepository::class)->deleteAll();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'locale_translation_build_projects();', + '\Drupal::service(\Drupal\locale\LocaleProjectRepository::class)->buildProjects();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'locale_translation_check_projects();', + '\Drupal::service(\Drupal\locale\LocaleProjectChecker::class)->checkProjects(array_keys(\Drupal::service(\Drupal\locale\LocaleProjectRepository::class)->getAll()));', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + "locale_translation_check_projects_local(['drupal'], ['de']);", + "\Drupal::service(\Drupal\locale\LocaleProjectChecker::class)->checkLocalProjects(['drupal'], ['de']);", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector.php b/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector.php new file mode 100644 index 000000000..965e62aff --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector.php @@ -0,0 +1,107 @@ +> */ + public function getNodeTypes(): array + { + return [FuncCall::class, String_::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + if ($node instanceof FuncCall) { + if (!$node->name instanceof Name || $node->name->toString() !== self::DEPRECATED_FUNCTION) { + return null; + } + + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch(new FullyQualified(self::MEDIA_HOOKS_CLASS), 'class'))] + ); + + return new MethodCall($serviceCall, 'formatEditFormValidate', $node->args); + } + + if ($node instanceof String_) { + if ($node->value !== self::DEPRECATED_FUNCTION) { + return null; + } + + return new Array_([ + new ArrayItem(new ClassConstFetch(new FullyQualified(self::MEDIA_HOOKS_CLASS), 'class')), + new ArrayItem(new String_('formatEditFormValidate')), + ]); + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated media_filter_format_edit_form_validate() with \Drupal\media\Hook\MediaHooks::formatEditFormValidate().', + [ + new ConfiguredCodeSample( + 'media_filter_format_edit_form_validate($form, $form_state);', + '\Drupal::service(\Drupal\media\Hook\MediaHooks::class)->formatEditFormValidate($form, $form_state);', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + "\$form['#validate'][] = 'media_filter_format_edit_form_validate';", + "\$form['#validate'][] = [\\Drupal\\media\\Hook\\MediaHooks::class, 'formatEditFormValidate'];", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector.php b/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector.php new file mode 100644 index 000000000..892b09bb5 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector.php @@ -0,0 +1,103 @@ +migrationPluginManager directly. + * + * @see https://www.drupal.org/node/3439369 + * @see https://www.drupal.org/node/3282894 + */ +final class MigrateSqlGetMigrationPluginManagerRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated Sql::getMigrationPluginManager() with $this->migrationPluginManager', + [ + new ConfiguredCodeSample( + '$manager = $this->getMigrationPluginManager();', + '$manager = $this->migrationPluginManager;', + [new DrupalIntroducedVersionConfiguration('11.0.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + if ($node instanceof StaticCall) { + return $this->refactorStaticCall($node); + } + + assert($node instanceof MethodCall); + if (!$node->var instanceof Variable || $node->var->name !== 'this') { + return null; + } + if ($this->getName($node->name) !== 'getMigrationPluginManager') { + return null; + } + if ($node->args !== []) { + return null; + } + if (!$this->isObjectType($node->var, new ObjectType('Drupal\migrate\Plugin\migrate\id_map\Sql'))) { + return null; + } + + return new PropertyFetch(new Variable('this'), 'migrationPluginManager'); + } + + private function refactorStaticCall(StaticCall $node): ?Node + { + if ($this->getName($node->name) !== 'getMigrationPluginManager') { + return null; + } + if ($node->args !== []) { + return null; + } + if (!$node->class instanceof Name || $node->class->toString() !== 'parent') { + return null; + } + + return new PropertyFetch(new Variable('this'), 'migrationPluginManager'); + } +} diff --git a/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector.php b/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector.php new file mode 100644 index 000000000..d7d2a8409 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector.php @@ -0,0 +1,93 @@ +getDriver()->mouseOver(). + * + * Deprecated in drupal:11.1.0 and removed in drupal:12.0.0. The replacement + * requires an XPath selector instead of a CSS selector. This rule handles + * simple CSS ID selectors (#foo), converting them to .//*[@id="foo"]. + * + * @see https://www.drupal.org/node/3421202 + * @see https://www.drupal.org/node/3460567 + */ +class MovePointerToMouseOverRector extends AbstractRector +{ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated movePointerTo() with getSession()->getDriver()->mouseOver()', + [ + new CodeSample( + <<<'CODE_BEFORE' +$this->movePointerTo('#my-element'); +CODE_BEFORE, + <<<'CODE_AFTER' +$this->getSession()->getDriver()->mouseOver('.//*[@id="my-element"]'); +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** @param MethodCall $node */ + public function refactor(Node $node): ?Node + { + if (!$this->isName($node->name, 'movePointerTo')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Tests\layout_builder\FunctionalJavascript\LayoutBuilderDisableInteractionsTest'))) { + return null; + } + + if (count($node->args) !== 1) { + return null; + } + + $arg = $node->args[0]; + if (!$arg instanceof Arg) { + return null; + } + + $argValue = $arg->value; + if (!$argValue instanceof String_) { + return null; + } + + $cssSelector = $argValue->value; + + if (!preg_match('/^#([a-zA-Z][a-zA-Z0-9_-]*)$/', $cssSelector, $matches)) { + return null; + } + + $xpathSelector = './/*[@id="'.$matches[1].'"]'; + + $getSession = new MethodCall($node->var, 'getSession', []); + $getDriver = new MethodCall($getSession, 'getDriver', []); + + return new MethodCall( + $getDriver, + 'mouseOver', + [new Arg(new String_($xpathSelector))] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector.php b/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector.php new file mode 100644 index 000000000..2043878b4 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector.php @@ -0,0 +1,102 @@ +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + return match ($node->name->toString()) { + 'node_access_rebuild' => $this->buildServiceCall('rebuild', $node->args), + 'node_access_needs_rebuild' => count($node->args) === 0 + ? $this->buildServiceCall('needsRebuild', []) + : $this->buildServiceCall('setNeedsRebuild', $node->args), + default => null, + }; + } + + /** @param Arg[] $args */ + private function buildServiceCall(string $method, array $args): MethodCall + { + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch(new FullyQualified('Drupal\node\NodeAccessRebuild'), 'class'))] + ); + + return new MethodCall($serviceCall, $method, $args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated node_access_rebuild() and node_access_needs_rebuild() with the NodeAccessRebuild service.', + [ + new ConfiguredCodeSample( + 'node_access_rebuild();', + '\Drupal::service(\Drupal\node\NodeAccessRebuild::class)->rebuild();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'node_access_needs_rebuild();', + '\Drupal::service(\Drupal\node\NodeAccessRebuild::class)->needsRebuild();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'node_access_needs_rebuild(TRUE);', + '\Drupal::service(\Drupal\node\NodeAccessRebuild::class)->setNeedsRebuild(TRUE);', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector.php b/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector.php new file mode 100644 index 000000000..2aaf96b1b --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector.php @@ -0,0 +1,115 @@ +expr instanceof Node\Expr\MethodCall) { + return null; + } + $methodCall = $node->expr; + if ($this->getName($methodCall->name) !== 'countDefaultLanguageRevisions') { + return null; + } + if (!$this->isObjectType($methodCall->var, new ObjectType('Drupal\node\NodeStorageInterface'))) { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + assert($node instanceof Node\Expr\MethodCall); + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\node\NodeStorageInterface'))) { + return null; + } + + $methodName = $this->getName($node->name); + + if ($methodName === 'revisionIds') { + if (count($node->args) !== 1) { + return null; + } + + $nodeArg = $node->args[0] instanceof Node\Arg ? $node->args[0]->value : $node->args[0]; + $getQuery = $this->nodeFactory->createMethodCall($node->var, 'getQuery'); + $allRevisions = $this->nodeFactory->createMethodCall($getQuery, 'allRevisions'); + $nodeId = $this->nodeFactory->createMethodCall($nodeArg, 'id'); + $condition = new Node\Expr\MethodCall($allRevisions, 'condition', [ + new Node\Arg(new Node\Scalar\String_('nid')), + new Node\Arg($nodeId), + ]); + $accessCheck = new Node\Expr\MethodCall($condition, 'accessCheck', [ + new Node\Arg(new Node\Expr\ConstFetch(new Node\Name('FALSE'))), + ]); + $execute = $this->nodeFactory->createMethodCall($accessCheck, 'execute'); + + return $this->nodeFactory->createFuncCall('array_keys', [new Node\Arg($execute)]); + } + + if ($methodName === 'userRevisionIds') { + if (count($node->args) !== 1) { + return null; + } + + $accountArg = $node->args[0] instanceof Node\Arg ? $node->args[0]->value : $node->args[0]; + $getQuery = $this->nodeFactory->createMethodCall($node->var, 'getQuery'); + $allRevisions = $this->nodeFactory->createMethodCall($getQuery, 'allRevisions'); + $accessCheck = new Node\Expr\MethodCall($allRevisions, 'accessCheck', [ + new Node\Arg(new Node\Expr\ConstFetch(new Node\Name('FALSE'))), + ]); + $accountId = $this->nodeFactory->createMethodCall($accountArg, 'id'); + $condition = new Node\Expr\MethodCall($accessCheck, 'condition', [ + new Node\Arg(new Node\Scalar\String_('uid')), + new Node\Arg($accountId), + ]); + $execute = $this->nodeFactory->createMethodCall($condition, 'execute'); + + return $this->nodeFactory->createFuncCall('array_keys', [new Node\Arg($execute)]); + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated NodeStorage::revisionIds() and userRevisionIds() with entity queries; removes countDefaultLanguageRevisions() (no replacement)', [ + new CodeSample( + '$storage->revisionIds($node);', + "array_keys(\$storage->getQuery()->allRevisions()->condition('nid', \$node->id())->accessCheck(FALSE)->execute());" + ), + new CodeSample( + '$storage->userRevisionIds($account);', + "array_keys(\$storage->getQuery()->allRevisions()->accessCheck(FALSE)->condition('uid', \$account->id())->execute());" + ), + new CodeSample( + '$storage->countDefaultLanguageRevisions($node);', + '' + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector.php b/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector.php new file mode 100644 index 000000000..b7aa8f9ed --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector.php @@ -0,0 +1,77 @@ +getName($node->name) !== 'isConfigurable') { + return null; + } + + if ($node->args !== []) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Component\Plugin\PluginBase'))) { + return null; + } + + return new Node\Expr\Instanceof_( + $node->var, + new Node\Name\FullyQualified('Drupal\Component\Plugin\ConfigurableInterface') + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated PluginBase::isConfigurable() with instanceof ConfigurableInterface', [ + new ConfiguredCodeSample( + '$this->isConfigurable()', + '$this instanceof \Drupal\Component\Plugin\ConfigurableInterface', + [new DrupalIntroducedVersionConfiguration('11.1.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector.php b/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector.php new file mode 100644 index 000000000..871dfbc68 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector.php @@ -0,0 +1,69 @@ +expr instanceof Node\Expr\Assign) { + return null; + } + + $assign = $node->expr; + + if (!$assign->expr instanceof Node\Scalar\String_) { + return null; + } + + if ($assign->expr->value !== 'automated_cron_settings_submit') { + return null; + } + + // Must be an array append ([] = ...) with no explicit index. + if (!$assign->var instanceof Node\Expr\ArrayDimFetch) { + return null; + } + + if ($assign->var->dim !== null) { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition("Removes deprecated \$form['#submit'][] = 'automated_cron_settings_submit' handler assignments (drupal:11.4.0)", [ + new CodeSample( + "\$form['#submit'][] = 'automated_cron_settings_submit';", + '' + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector.php b/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector.php new file mode 100644 index 000000000..adf3d2343 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector.php @@ -0,0 +1,107 @@ +> */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + public function refactor(Node $node): ?Node + { + assert($node instanceof Class_); + if (!$this->isCachePluginBaseSubclass($node)) { + return null; + } + + $changed = false; + foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof ClassMethod && $this->isName($stmt, 'cacheExpire')) { + unset($node->stmts[$key]); + $changed = true; + } + } + + return $changed ? $node : null; + } + + private function isCachePluginBaseSubclass(Class_ $node): bool + { + if ($node->extends === null) { + return false; + } + + $parentName = $node->extends->toString(); + + // Match fully-qualified names (Rector resolves `use` aliases before calling refactor). + foreach (self::PARENT_FQCNS as $fqcn) { + if ($parentName === $fqcn) { + return true; + } + } + + // Match unqualified short names (no `use` statement, global-namespace usage). + if (!str_contains($parentName, '\\')) { + foreach (self::PARENT_SHORT_NAMES as $short) { + if ($parentName === $short) { + return true; + } + } + } + + try { + if ($this->isObjectType($node->extends, new ObjectType(self::CACHE_PLUGIN_BASE_FQCN))) { + return true; + } + } catch (\Throwable) { + } + + return false; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector.php b/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector.php new file mode 100644 index 000000000..169ab821c --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector.php @@ -0,0 +1,99 @@ +assertMetrics([ + 'CacheGetCount' => 5, + 'CacheTagChecksumCount' => 38, + 'CacheTagIsValidCount' => 43, + 'CacheTagInvalidationCount' => 0, +], $performance_data); +CODE_BEFORE, + <<<'CODE_AFTER' +$this->assertMetrics([ + 'CacheGetCount' => 5, + 'CacheTagInvalidationCount' => 0, +], $performance_data); +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** @param MethodCall $node */ + public function refactor(Node $node): ?Node + { + if (!$this->isName($node->name, 'assertMetrics')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Tests\PerformanceTestTrait'))) { + return null; + } + + if (!isset($node->args[0])) { + return null; + } + + $firstArg = $node->args[0]->value; + if (!$firstArg instanceof Array_) { + return null; + } + + $changed = false; + $newItems = []; + foreach ($firstArg->items as $item) { + $key = $item->key; + if ($key instanceof String_ && in_array($key->value, self::DEPRECATED_KEYS, true)) { + $changed = true; + continue; + } + $newItems[] = $item; + } + + if (!$changed) { + return null; + } + + $firstArg->items = $newItems; + + return $node; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector.php b/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector.php new file mode 100644 index 000000000..afad34fa3 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector.php @@ -0,0 +1,93 @@ +save(TRUE);', + '$config->save();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof MethodCall); + if (!$this->isName($node->name, 'save')) { + return null; + } + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Config\Config'))) { + return null; + } + if (count($node->args) !== 1) { + return null; + } + $arg = $node->args[0]; + if (!$arg instanceof Arg) { + return null; + } + if (!$arg->value instanceof ConstFetch) { + return null; + } + $constName = strtolower((string) $arg->value->name); + if ($constName !== 'true' && $constName !== 'false') { + return null; + } + + $cloned = clone $node; + $cloned->args = []; + + return $cloned; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector.php b/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector.php new file mode 100644 index 000000000..456725210 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector.php @@ -0,0 +1,165 @@ +t('No HTML tags allowed.'); +} +CODE_BEFORE, + <<<'CODE_AFTER' +public function tips() { + return $this->t('No HTML tags allowed.'); +} +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [Class_::class, FuncCall::class]; + } + + /** @param Class_|FuncCall $node */ + public function refactor(Node $node): ?Node + { + if ($node instanceof Class_) { + return $this->refactorClass($node); + } + + return $this->refactorFuncCall($node); + } + + private function refactorClass(Class_ $node): ?Class_ + { + if (!$this->classIsFilterPlugin($node)) { + return null; + } + + $changed = false; + foreach ($node->getMethods() as $method) { + if ($this->updateTipsMethod($method)) { + $changed = true; + } + } + + return $changed ? $node : null; + } + + private function classIsFilterPlugin(Class_ $classNode): bool + { + foreach ($classNode->implements as $implement) { + if ($this->nameMatchesFilterSymbol($implement)) { + return true; + } + } + + if ($classNode->extends !== null && $this->nameMatchesFilterSymbol($classNode->extends)) { + return true; + } + + return false; + } + + private function nameMatchesFilterSymbol(Name $name): bool + { + return in_array($name->toString(), self::FILTER_SYMBOLS, true); + } + + private function updateTipsMethod(ClassMethod $method): bool + { + if (!$this->isName($method, 'tips')) { + return false; + } + + if (count($method->params) === 0) { + return false; + } + + $firstParam = $method->params[0]; + if (!$firstParam->var instanceof Variable) { + return false; + } + + if (!$this->isName($firstParam->var, 'long')) { + return false; + } + + $usesLong = false; + $this->traverseNodesWithCallable((array) $method->stmts, function (Node $subNode) use (&$usesLong): ?Node { + if ($subNode instanceof Variable && $this->isName($subNode, 'long')) { + $usesLong = true; + } + + return null; + }); + + if ($usesLong) { + return false; + } + + $method->params = []; + + return true; + } + + private function refactorFuncCall(FuncCall $node): ?FuncCall + { + if (!$this->isName($node->name, '_filter_tips')) { + return null; + } + + if (count($node->getArgs()) < 2) { + return null; + } + + array_splice($node->args, 1); + + return $node; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector.php b/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector.php new file mode 100644 index 000000000..487e1b21a --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector.php @@ -0,0 +1,102 @@ +isHandlerBaseSubclass($node)) { + return null; + } + + $changed = false; + foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof ClassMethod && $this->isName($stmt, 'defineExtraOptions')) { + unset($node->stmts[$key]); + $changed = true; + } + } + + return $changed ? $node : null; + } + + private function isHandlerBaseSubclass(Class_ $node): bool + { + if ($node->extends === null) { + return false; + } + + $parentName = $node->extends->toString(); + + if ($parentName === self::HANDLER_BASE_FQCN) { + return true; + } + + foreach (self::PARENT_SHORT_NAMES as $short) { + if ($parentName === $short || str_ends_with($parentName, '\\'.$short)) { + return true; + } + } + + try { + if ($this->isObjectType($node->extends, new ObjectType(self::HANDLER_BASE_FQCN))) { + return true; + } + } catch (\Throwable) { + } + + return false; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector.php b/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector.php new file mode 100644 index 000000000..88b066393 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector.php @@ -0,0 +1,60 @@ +expr instanceof Node\Expr\StaticCall) { + return null; + } + + $staticCall = $node->expr; + + if (!$this->isName($staticCall->name, 'validateTitleElement')) { + return null; + } + + if (!$this->isName($staticCall->class, 'Drupal\link\Plugin\Field\FieldWidget\LinkWidget')) { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Removes deprecated LinkWidget::validateTitleElement() calls. Validation is now handled by LinkTitleRequiredConstraint on the LinkItem field type (drupal:11.4.0)', [ + new CodeSample( + 'LinkWidget::validateTitleElement($element, $form_state, $form);', + '' + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector.php b/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector.php new file mode 100644 index 000000000..e071816aa --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector.php @@ -0,0 +1,67 @@ +expr instanceof Node\Expr\MethodCall) { + return null; + } + + $methodCall = $node->expr; + + if (!$this->isNames($methodCall->name, ['addModule', 'addProfile'])) { + return null; + } + + $isModuleHandler = false; + foreach (['Drupal\Core\Extension\ModuleHandlerInterface', 'Drupal\Core\Extension\ModuleHandler'] as $class) { + if ($this->isObjectType($methodCall->var, new ObjectType($class))) { + $isModuleHandler = true; + break; + } + } + if (!$isModuleHandler) { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Removes deprecated ModuleHandlerInterface::addModule() and addProfile() calls, which are no-ops since drupal:11.2.0 and removed in drupal:12.0.0', [ + new CodeSample( + "\$moduleHandler->addModule('mymodule', 'modules/mymodule');", + '' + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector.php b/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector.php new file mode 100644 index 000000000..6720bf260 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector.php @@ -0,0 +1,79 @@ +moduleHandler->writeCache();', + '' + ), + new CodeSample( + '$hookInfo = $this->moduleHandler->getHookInfo();', + '$hookInfo = [];' + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [Expression::class, MethodCall::class]; + } + + public function refactor(Node $node): int|Node|null + { + if ($node instanceof Expression && $node->expr instanceof MethodCall) { + $call = $node->expr; + if ($this->isModuleHandlerMethodCall($call, 'writeCache') + || $this->isModuleHandlerMethodCall($call, 'getHookInfo') + ) { + return NodeVisitor::REMOVE_NODE; + } + } + + if ($node instanceof MethodCall + && $this->isModuleHandlerMethodCall($node, 'getHookInfo') + ) { + return new Array_([]); + } + + return null; + } + + private function isModuleHandlerMethodCall(MethodCall $call, string $methodName): bool + { + return $this->isName($call->name, $methodName) + && $this->isObjectType( + $call->var, + new ObjectType('Drupal\Core\Extension\ModuleHandlerInterface') + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector.php b/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector.php new file mode 100644 index 000000000..d69cdbd72 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector.php @@ -0,0 +1,112 @@ +root, TRUE);', + 'Database::convertDbUrlToConnectionInfo($url, TRUE);', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [StaticCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof StaticCall); + if (!$this->isName($node->class, 'Drupal\Core\Database\Database')) { + return null; + } + if (!$this->isName($node->name, 'convertDbUrlToConnectionInfo')) { + return null; + } + if (count($node->args) < 2) { + return null; + } + + $secondArg = $node->args[1]; + if (!$secondArg instanceof Arg) { + return null; + } + $secondArgValue = $secondArg->value; + + if ($secondArgValue instanceof ConstFetch) { + $constName = strtolower((string) $secondArgValue->name); + if ($constName === 'true' || $constName === 'false' || $constName === 'null') { + return null; + } + } elseif ($secondArgValue instanceof Variable) { + return null; + } elseif ( + !$secondArgValue instanceof String_ + && !$secondArgValue instanceof PropertyFetch + && !$secondArgValue instanceof NullsafePropertyFetch + && !$secondArgValue instanceof FuncCall + && !$secondArgValue instanceof StaticPropertyFetch + && !$secondArgValue instanceof MethodCall + ) { + return null; + } + + $cloned = clone $node; + array_splice($cloned->args, 1, 1); + + return $cloned; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector.php b/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector.php new file mode 100644 index 000000000..5de79fd56 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector.php @@ -0,0 +1,89 @@ +createConnectionOptionsFromUrl($url, $root); +CODE_BEFORE, + <<<'CODE_AFTER' +$connection->createConnectionOptionsFromUrl($url, NULL); +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class]; + } + + /** + * @param MethodCall|StaticCall $node + */ + public function refactor(Node $node): ?Node + { + if (!$this->isName($node->name, 'createConnectionOptionsFromUrl')) { + return null; + } + + if ($node instanceof MethodCall && !$this->isObjectType($node->var, new ObjectType('Drupal\Core\Database\Connection'))) { + return null; + } + + if ($node instanceof StaticCall && !$this->isObjectType($node->class, new ObjectType('Drupal\Core\Database\Connection'))) { + return null; + } + + // Must have at least two arguments. + if (count($node->args) < 2) { + return null; + } + + $secondArg = $node->args[1]; + + // Skip if the argument is already named or unpacked (edge cases). + if (!$secondArg instanceof Arg) { + return null; + } + + // Skip if the second argument is already null. + $value = $secondArg->value; + if ($value instanceof ConstFetch && strtolower((string) $value->name) === 'null') { + return null; + } + + // Replace the second argument with NULL. + $node->args[1] = new Arg(new ConstFetch(new Name('NULL'))); + + return $node; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector.php b/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector.php new file mode 100644 index 000000000..e5ebd1e46 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector.php @@ -0,0 +1,74 @@ +setUriCallback(\'my_entity_uri\');', + '' + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [Expression::class, MethodCall::class]; + } + + public function refactor(Node $node): int|Node|null + { + if ($node instanceof Expression) { + if ($node->expr instanceof MethodCall + && $this->isName($node->expr->name, 'setUriCallback') + && $this->isObjectType($node->expr->var, new ObjectType('Drupal\Core\Entity\EntityTypeInterface')) + ) { + return NodeVisitor::REMOVE_NODE; + } + + return null; + } + + // Fluent chain: $entity_type->setUriCallback('func')->someOtherMethod() + if ($node instanceof MethodCall) { + if ($node->var instanceof MethodCall + && $this->isName($node->var->name, 'setUriCallback') + && $this->isObjectType($node->var->var, new ObjectType('Drupal\Core\Entity\EntityTypeInterface')) + ) { + $node->var = $node->var->var; + + return $node; + } + } + + return null; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector.php b/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector.php new file mode 100644 index 000000000..dc14911c9 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector.php @@ -0,0 +1,70 @@ +expr instanceof Node\Expr\Assign) { + return null; + } + + $assign = $node->expr; + + if (!$assign->var instanceof Node\Expr\ArrayDimFetch) { + return null; + } + + $arrayDimFetch = $assign->var; + + if (!$this->isName($arrayDimFetch->var, 'settings')) { + return null; + } + + if (!$arrayDimFetch->dim instanceof Node\Scalar\String_) { + return null; + } + + if ($arrayDimFetch->dim->value !== 'state_cache') { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition("Removes deprecated \$settings['state_cache'] assignments. State caching is permanently enabled since drupal:11.0.0 and the setting has no effect", [ + new CodeSample( + "\$settings['state_cache'] = TRUE;", + '' + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector.php b/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector.php new file mode 100644 index 000000000..42fb72474 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector.php @@ -0,0 +1,75 @@ +trustData()->save();', + '$entity->save();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof MethodCall); + if (!$this->isName($node->name, 'trustData')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Config\Entity\ConfigEntityInterface'))) { + return null; + } + + return $node->var; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector.php b/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector.php new file mode 100644 index 000000000..c754fc175 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector.php @@ -0,0 +1,83 @@ +getTag());', + 'new TwigNodeTrans($body, $plural, $count, $options, $lineno);', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [New_::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof New_); + if (!$node->class instanceof Name) { + return null; + } + $className = $this->getName($node->class); + if ($className !== 'TwigNodeTrans' + && $className !== 'Drupal\Core\Template\TwigNodeTrans' + ) { + return null; + } + if (count($node->args) !== 6) { + return null; + } + + $cloned = clone $node; + array_pop($cloned->args); + + return $cloned; + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector.php b/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector.php new file mode 100644 index 000000000..4302c4b8b --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector.php @@ -0,0 +1,79 @@ +extends === null) { + return null; + } + + if (!in_array($node->extends->toString(), self::UPDATER_BASE_CLASSES, true)) { + return null; + } + + $modified = false; + $newStmts = []; + foreach ($node->stmts as $stmt) { + if ($stmt instanceof ClassMethod + && in_array($this->getName($stmt), self::DEPRECATED_METHODS, true) + ) { + $modified = true; + continue; + } + $newStmts[] = $stmt; + } + + if (!$modified) { + return null; + } + + $node->stmts = $newStmts; + + return $node; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Remove deprecated Updater::postInstall() and postInstallTasks() method overrides (drupal:11.1.0)', [ + new CodeSample( + 'class MyUpdater extends Updater { public function postInstallTasks() { return []; } }', + 'class MyUpdater extends Updater { }' + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector.php b/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector.php new file mode 100644 index 000000000..19c9741b1 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector.php @@ -0,0 +1,175 @@ + $plugin->getRowCacheKeys($row)] + * - Variable-first assignment: $keys = $plugin->getRowCacheKeys($row); [...'keys' => $keys] + * - Delegation method: public function getRowCacheKeys($row) { return $this->plugin->getRowCacheKeys($row); } + * + * @see https://www.drupal.org/node/3564937 + * @see https://www.drupal.org/node/3564958 + */ +final class RemoveViewsRowCacheKeysRector extends AbstractRector +{ + private const DEPRECATED_METHODS = ['getRowCacheKeys', 'getRowId']; + + private const CACHE_PLUGIN_BASE = 'Drupal\views\Plugin\views\cache\CachePluginBase'; + + /** @var list variable names whose assignments were removed in the current file */ + private array $removedVarNames = []; + + /** + * @param array $nodes + * + * @return array + */ + public function beforeTraversal(array $nodes): array + { + $this->removedVarNames = []; + + return $nodes; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Remove deprecated CachePluginBase::getRowCacheKeys() and getRowId() calls and array item values', + [ + new CodeSample( + "['keys' => \$cache_plugin->getRowCacheKeys(\$row), 'tags' => []]", + "['tags' => []]" + ), + new CodeSample( + "\$keys = \$cache_plugin->getRowCacheKeys(\$row);\n['keys' => \$keys, 'tags' => []]", + "['tags' => []]" + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [Array_::class, Expression::class, ClassMethod::class]; + } + + public function refactor(Node $node): int|Node|null + { + if ($node instanceof Expression) { + return $this->refactorExpression($node); + } + + if ($node instanceof ClassMethod) { + return $this->refactorClassMethod($node); + } + + assert($node instanceof Array_); + + return $this->refactorArray($node); + } + + /** @return NodeVisitor::REMOVE_NODE|null */ + private function refactorExpression(Expression $node): ?int + { + if (!$node->expr instanceof Assign) { + return null; + } + $assign = $node->expr; + if (!$assign->var instanceof Variable) { + return null; + } + if (!$assign->expr instanceof MethodCall) { + return null; + } + if (!$this->isDeprecatedMethodCall($assign->expr)) { + return null; + } + + $varName = $this->getName($assign->var); + if ($varName !== null) { + $this->removedVarNames[] = $varName; + } + + return NodeVisitor::REMOVE_NODE; + } + + /** @return NodeVisitor::REMOVE_NODE|null */ + private function refactorClassMethod(ClassMethod $node): ?int + { + if (count($node->stmts ?? []) !== 1) { + return null; + } + $stmt = $node->stmts[0]; + if (!$stmt instanceof Return_) { + return null; + } + if (!$stmt->expr instanceof MethodCall) { + return null; + } + if (!$this->isDeprecatedMethodCall($stmt->expr)) { + return null; + } + + return NodeVisitor::REMOVE_NODE; + } + + private function refactorArray(Array_ $node): ?Array_ + { + $modified = false; + $newItems = []; + + foreach ($node->items as $item) { + if ($item->value instanceof MethodCall + && $this->isDeprecatedMethodCall($item->value) + ) { + $modified = true; + continue; + } + + if ($item->value instanceof Variable + && in_array($this->getName($item->value), $this->removedVarNames, true) + ) { + $modified = true; + continue; + } + + $newItems[] = $item; + } + + if (!$modified) { + return null; + } + $node->items = $newItems; + + return $node; + } + + private function isDeprecatedMethodCall(MethodCall $call): bool + { + return $call->name instanceof Identifier + && in_array($call->name->toString(), self::DEPRECATED_METHODS, true) + && $this->isObjectType($call->var, new ObjectType(self::CACHE_PLUGIN_BASE)); + } +} diff --git a/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector.php b/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector.php new file mode 100644 index 000000000..29fa0a09b --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector.php @@ -0,0 +1,66 @@ +name->toString() === self::OLD_FQCN) { + $node->name = new Name(explode('\\', self::NEW_FQCN)); + + return $node; + } + + return null; + } + + assert($node instanceof Attribute); + + if ($node->name instanceof FullyQualified && $node->name->toString() === self::OLD_FQCN) { + $node->name = new Name(self::NEW_SHORT); + + return $node; + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Rename #[StopProceduralHookScan] attribute to #[ProceduralHookScanStop] and update its use statement (drupal:11.2.0)', [ + new CodeSample( + "use Drupal\\Core\\Hook\\Attribute\\StopProceduralHookScan;\n\n#[StopProceduralHookScan]\nfunction mymodule_helper(): void {}", + "use Drupal\\Core\\Hook\\Attribute\\ProceduralHookScanStop;\n\n#[ProceduralHookScanStop]\nfunction mymodule_helper(): void {}" + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector.php b/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector.php new file mode 100644 index 000000000..f8784b485 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector.php @@ -0,0 +1,127 @@ +> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof MethodCall); + + if (!$this->isName($node->name, 'addMethodCall')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Symfony\Component\DependencyInjection\Definition'))) { + return null; + } + + $args = $node->getArgs(); + if (count($args) < 2) { + return null; + } + + $firstArg = $args[0]->value; + if (!$firstArg instanceof String_ || $firstArg->value !== 'addCachedDiscovery') { + return null; + } + + $secondArg = $args[1]->value; + if (!$secondArg instanceof Node\Expr\Array_) { + return null; + } + + $serviceIdNode = null; + foreach ($secondArg->items as $item) { + $itemValue = $item->value; + if ($itemValue instanceof New_) { + $className = $itemValue->class; + if ($className instanceof Name) { + $shortName = $className->getLast(); + if ($shortName === 'Reference' && count($itemValue->getArgs()) >= 1) { + $serviceIdNode = $itemValue->getArgs()[0]->value; + break; + } + } + } + } + + if ($serviceIdNode === null) { + return null; + } + + $containerVar = new Variable('container'); + + $getDefinitionCall = new MethodCall( + $containerVar, + 'getDefinition', + [new Arg($serviceIdNode)] + ); + + return new MethodCall( + $getDefinitionCall, + 'addTag', + [new Arg(new String_('plugin_manager_cache_clear'))] + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + "Replace deprecated \$def->addMethodCall('addCachedDiscovery', [new Reference(\$id)]) with \$container->getDefinition(\$id)->addTag('plugin_manager_cache_clear')", + [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$container->getDefinition('plugin.cache_clearer') + ->addMethodCall('addCachedDiscovery', [new Reference('my.plugin.manager')]); +CODE_BEFORE, + <<<'CODE_AFTER' +$container->getDefinition('my.plugin.manager')->addTag('plugin_manager_cache_clear'); +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('11.1.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector.php b/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector.php new file mode 100644 index 000000000..88261509e --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector.php @@ -0,0 +1,101 @@ +> */ + public function getNodeTypes(): array + { + return [StaticCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof StaticCall); + if (!$this->isName($node->name, 'alphadecimalToInt')) { + return null; + } + if (!$this->isObjectType($node->class, new ObjectType('Drupal\Component\Utility\Number'))) { + return null; + } + if (count($node->args) !== 1) { + return null; + } + $arg = $node->args[0]; + if (!$arg instanceof Arg) { + return null; + } + $value = $arg->value; + + if ($value instanceof ConstFetch + && strtolower($this->getName($value->name)) === 'null' + ) { + return new LNumber(0); + } + + if ($value instanceof String_ && $value->value === '') { + return new LNumber(0); + } + + return null; + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector.php b/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector.php new file mode 100644 index 000000000..bf0a81ad2 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector.php @@ -0,0 +1,81 @@ +isName($node->name, 'getCountNewComments')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\comment\CommentManagerInterface'))) { + return null; + } + + $service = new Node\Expr\StaticCall( + new Node\Name\FullyQualified('Drupal'), + 'service', + [new Node\Arg(new Node\Expr\ClassConstFetch(new Node\Name\FullyQualified('Drupal\history\HistoryManager'), 'class'))] + ); + + return new Node\Expr\MethodCall($service, 'getCountNewComments', $node->args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated CommentManagerInterface::getCountNewComments() with HistoryManager service', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$this->commentManager->getCountNewComments($entity); +CODE_BEFORE, + <<<'CODE_AFTER' +\Drupal::service(\Drupal\history\HistoryManager::class)->getCountNewComments($entity); +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector.php b/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector.php new file mode 100644 index 000000000..bc05f1aaf --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector.php @@ -0,0 +1,131 @@ + 'Both', + 'START_DATE' => 'StartDate', + 'END_DATE' => 'EndDate', + ]; + + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace removed DateTimeRangeConstantsInterface constants and datetime_type_field_views_data_helper() with Drupal 12 equivalents', + [ + new ConfiguredCodeSample( + 'DateTimeRangeConstantsInterface::BOTH;', + '\\Drupal\\datetime_range\\DateTimeRangeDisplayOptions::Both->value;', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + new ConfiguredCodeSample( + 'datetime_type_field_views_data_helper($field_storage, $data, $column);', + "\\Drupal::service('datetime.views_helper')->buildViewsData(\$field_storage, \$data, \$column);", + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [ClassConstFetch::class, FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + if ($node instanceof ClassConstFetch) { + return $this->refactorClassConst($node); + } + if ($node instanceof FuncCall) { + return $this->refactorFuncCall($node); + } + + return null; + } + + private function refactorClassConst(ClassConstFetch $node): ?Node + { + if (!$node->name instanceof Identifier) { + return null; + } + $constName = $node->name->toString(); + if (!isset(self::CONST_MAP[$constName])) { + return null; + } + if (!$this->isName($node->class, self::CONSTANTS_INTERFACE)) { + return null; + } + $enumCaseFetch = new ClassConstFetch( + new FullyQualified(self::DISPLAY_OPTIONS_ENUM), + self::CONST_MAP[$constName] + ); + + return new PropertyFetch($enumCaseFetch, 'value'); + } + + private function refactorFuncCall(FuncCall $node): ?Node + { + if (!$node->name instanceof Name) { + return null; + } + if ($node->name->toString() !== 'datetime_type_field_views_data_helper') { + return null; + } + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new String_('datetime.views_helper'))] + ); + + return new MethodCall($serviceCall, 'buildViewsData', $node->args); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector.php b/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector.php new file mode 100644 index 000000000..57dba80c7 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector.php @@ -0,0 +1,75 @@ +getStorage('editor')->load(). + * + * Deprecated in drupal:11.2.0, removed in drupal:12.0.0. + * + * @see https://www.drupal.org/node/3447794 + * @see https://www.drupal.org/node/3509245 + */ +final class ReplaceEditorLoadRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$this->isName($node, 'editor_load')) { + return null; + } + + if (count($node->args) !== 1) { + return null; + } + + $entityTypeManager = $this->nodeFactory->createStaticCall('Drupal', 'entityTypeManager'); + $getStorage = $this->nodeFactory->createMethodCall($entityTypeManager, 'getStorage', [new String_('editor')]); + + return new MethodCall($getStorage, 'load', $node->args); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated editor_load($format_id) with entityTypeManager()->getStorage(\'editor\')->load() (drupal:11.2.0)', [ + new ConfiguredCodeSample( + '$editor = editor_load($format_id);', + '$editor = \Drupal::entityTypeManager()->getStorage(\'editor\')->load($format_id);', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector.php b/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector.php new file mode 100644 index 000000000..499cdd4db --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector.php @@ -0,0 +1,126 @@ +original magic property with getOriginal()/setOriginal(). + * + * Deprecated in drupal:11.2.0, removed in drupal:12.0.0. + * Skips $this->original to avoid false positives on non-entity classes. + * + * @see https://www.drupal.org/node/3295826 + */ +final class ReplaceEntityOriginalPropertyRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [PropertyFetch::class, NullsafePropertyFetch::class, Assign::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + // Step 1a: $entity->original → $entity->getOriginal() + // Skip when used as LHS of an Assign — the Assign handler below manages that + // so BC wrapping captures the original assignment expression correctly. + // (skip $this->original — non-entity classes have a legitimate $original property) + if ($node instanceof PropertyFetch) { + if ($this->isName($node->name, 'original') + && !$this->isThisVar($node->var) + && $this->isObjectType($node->var, new ObjectType('Drupal\Core\Entity\EntityInterface')) + ) { + if ($node->getAttribute(AttributeKey::IS_BEING_ASSIGNED)) { + return null; + } + + return new MethodCall($node->var, 'getOriginal'); + } + + return null; + } + + // Step 1b: $entity?->original → $entity?->getOriginal() + if ($node instanceof NullsafePropertyFetch) { + if ($this->isName($node->name, 'original') + && !$this->isThisVar($node->var) + && $this->isObjectType($node->var, new ObjectType('Drupal\Core\Entity\EntityInterface')) + ) { + return new NullsafeMethodCall($node->var, 'getOriginal'); + } + + return null; + } + + assert($node instanceof Assign); + + // Step 2: $entity->original = $x → $entity->setOriginal($x) + // Detect the original PropertyFetch form directly (before any inner transformation) + // so the BC "old callable" captures the original assignment correctly. + if ($node->var instanceof PropertyFetch + && $this->isName($node->var->name, 'original') + && !$this->isThisVar($node->var->var) + && $this->isObjectType($node->var->var, new ObjectType('Drupal\Core\Entity\EntityInterface')) + ) { + return new MethodCall( + $node->var->var, + 'setOriginal', + [new Arg($node->expr)] + ); + } + + return null; + } + + private function isThisVar(Node $node): bool + { + return $node instanceof Variable && $node->name === 'this'; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated $entity->original magic property with getOriginal()/setOriginal() method calls (drupal:11.2.0)', [ + new ConfiguredCodeSample( + '$original = $entity->original;', + '$original = $entity->getOriginal();', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + new ConfiguredCodeSample( + '$entity->original = $unchanged;', + '$entity->setOriginal($unchanged);', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector.php b/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector.php new file mode 100644 index 000000000..b3e717ea7 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector.php @@ -0,0 +1,77 @@ +isName($node->name, 'RECURSIVE_RENDER_LIMIT')) { + return null; + } + + foreach (self::TARGET_CLASSES as $class) { + if ($this->isName($node->class, $class)) { + return new Int_(20); + } + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT with literal 20 (drupal:11.4.0)', [ + new ConfiguredCodeSample( + 'if ($count > EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT) {}', + 'if ($count > 20) {}', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector.php b/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector.php new file mode 100644 index 000000000..306f90bec --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector.php @@ -0,0 +1,82 @@ + 'fieldgroup' with '#type' => 'fieldset' in render arrays. + * + * The Fieldgroup render element is deprecated in drupal:11.2.0 and removed in + * drupal:12.0.0. + * + * @see https://www.drupal.org/node/3512254 + * @see https://www.drupal.org/node/3515272 + */ +final class ReplaceFieldgroupToFieldsetRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + "Replace '#type' => 'fieldgroup' with '#type' => 'fieldset' in render arrays", + [ + new ConfiguredCodeSample( + "\$form['account'] = ['#type' => 'fieldgroup', '#title' => \$this->t('Account settings')];", + "\$form['account'] = ['#type' => 'fieldset', '#title' => \$this->t('Account settings')];", + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [Array_::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Array_); + $changed = false; + $cloned = clone $node; + foreach ($cloned->items as $index => $item) { + if (!$item->key instanceof String_ || $item->key->value !== '#type') { + continue; + } + if (!$item->value instanceof String_ || $item->value->value !== 'fieldgroup') { + continue; + } + $newItem = clone $item; + $newItem->value = new String_('fieldset'); + $cloned->items[$index] = $newItem; + $changed = true; + } + + return $changed ? $cloned : null; + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector.php b/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector.php new file mode 100644 index 000000000..d783e1f1f --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector.php @@ -0,0 +1,80 @@ + locale_config_batch_update_default_config_langcodes() + * - locale_config_batch_refresh_name() => locale_config_batch_update_config_translations() + * + * @see https://www.drupal.org/node/3475054 + */ +final class ReplaceLocaleConfigBatchFunctionsRector extends AbstractDrupalCoreRector +{ + private const RENAME_MAP = [ + 'locale_config_batch_set_config_langcodes' => 'locale_config_batch_update_default_config_langcodes', + 'locale_config_batch_refresh_name' => 'locale_config_batch_update_config_translations', + ]; + + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Node\Expr\FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Node\Expr\FuncCall); + + if (!$node->name instanceof Node\Name) { + return null; + } + + $name = $node->name->toString(); + if (!isset(self::RENAME_MAP[$name])) { + return null; + } + + $newNode = clone $node; + $newNode->name = new Node\Name(self::RENAME_MAP[$name]); + + return $newNode; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace removed locale config batch helper functions with their renamed successors (drupal:11.1.0)', [ + new ConfiguredCodeSample( + 'locale_config_batch_set_config_langcodes($context);', + 'locale_config_batch_update_default_config_langcodes($context);', + [new DrupalIntroducedVersionConfiguration('11.1.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector.php b/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector.php new file mode 100644 index 000000000..0271cb357 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector.php @@ -0,0 +1,116 @@ +isName($node, 'node_access_view_all_nodes')) { + return $this->buildCheckAllGrants($node); + } + + if ($this->isName($node, 'drupal_static_reset')) { + return $this->refactorStaticReset($node); + } + + return null; + } + + private function buildCheckAllGrants(FuncCall $node): MethodCall + { + $entityTypeManager = $this->nodeFactory->createStaticCall('Drupal', 'entityTypeManager'); + $getHandler = $this->nodeFactory->createMethodCall( + $entityTypeManager, + 'getAccessControlHandler', + [new String_('node')] + ); + + if (!empty($node->args) && $node->args[0] instanceof Arg) { + $accountArg = $node->args[0]->value; + } else { + $accountArg = $this->nodeFactory->createStaticCall('Drupal', 'currentUser'); + } + + return $this->nodeFactory->createMethodCall($getHandler, 'checkAllGrants', [$accountArg]); + } + + private function refactorStaticReset(FuncCall $node): ?MethodCall + { + if (empty($node->args) || !$node->args[0] instanceof Arg) { + return null; + } + + $firstArg = $node->args[0]->value; + if (!$firstArg instanceof String_ || $firstArg->value !== 'node_access_view_all_nodes') { + return null; + } + + $service = $this->nodeFactory->createStaticCall( + 'Drupal', + 'service', + [new String_('node.view_all_nodes_memory_cache')] + ); + + return $this->nodeFactory->createMethodCall($service, 'deleteAll'); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated node_access_view_all_nodes() with entityTypeManager()->getAccessControlHandler(\'node\')->checkAllGrants() (drupal:11.3.0)', [ + new ConfiguredCodeSample( + 'node_access_view_all_nodes();', + "\\Drupal::entityTypeManager()->getAccessControlHandler('node')->checkAllGrants(\\Drupal::currentUser());", + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + new ConfiguredCodeSample( + "drupal_static_reset('node_access_view_all_nodes');", + "\\Drupal::service('node.view_all_nodes_memory_cache')->deleteAll();", + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector.php b/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector.php new file mode 100644 index 000000000..388536d0c --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector.php @@ -0,0 +1,95 @@ +createBodyField() from BodyFieldCreationTrait. + * + * Deprecated in drupal:11.3.0, removed in drupal:12.0.0. + * + * @see https://www.drupal.org/node/3489266 + * @see https://www.drupal.org/node/3516778 + */ +final class ReplaceNodeAddBodyFieldRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$this->isName($node, 'node_add_body_field')) { + return null; + } + + if (empty($node->args)) { + return null; + } + + $typeArg = $node->args[0] instanceof Arg ? $node->args[0]->value : null; + if ($typeArg === null) { + return null; + } + + $idCall = new MethodCall($typeArg, 'id'); + + $newArgs = [ + new Arg(new String_('node')), + new Arg($idCall), + ]; + + if (isset($node->args[1]) && $node->args[1] instanceof Arg) { + $newArgs[] = new Arg(new String_('body')); + $newArgs[] = new Arg($node->args[1]->value); + } + + return new MethodCall( + new Variable('this'), + 'createBodyField', + $newArgs + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated node_add_body_field() with $this->createBodyField() (drupal:11.3.0)', [ + new ConfiguredCodeSample( + 'node_add_body_field($nodeType, \'My Body\');', + '$this->createBodyField(\'node\', $nodeType->id(), \'body\', \'My Body\');', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector.php b/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector.php new file mode 100644 index 000000000..a8495e706 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector.php @@ -0,0 +1,109 @@ +getBundleLabels('node');", + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + new ConfiguredCodeSample( + 'node_get_type_label($node);', + '$node->getBundleEntity()->label();', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + return match ($node->name->toString()) { + 'node_type_get_names' => $this->refactorNodeTypeGetNames(), + 'node_get_type_label' => $this->refactorNodeGetTypeLabel($node), + default => null, + }; + } + + private function refactorNodeTypeGetNames(): Node + { + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new String_('entity_type.bundle.info'))] + ); + + return new MethodCall($serviceCall, 'getBundleLabels', [new Arg(new String_('node'))]); + } + + private function refactorNodeGetTypeLabel(FuncCall $node): ?Node + { + if (count($node->args) < 1) { + return null; + } + $nodeArg = $node->args[0]->value; + + return new MethodCall( + new MethodCall($nodeArg, 'getBundleEntity'), + 'label' + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector.php b/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector.php new file mode 100644 index 000000000..92f8761b7 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector.php @@ -0,0 +1,113 @@ + 'Disabled', + 'DRUPAL_OPTIONAL' => 'Optional', + 'DRUPAL_REQUIRED' => 'Required', + ]; + + private const INT_TO_ENUM = [ + 0 => 'Disabled', + 1 => 'Optional', + 2 => 'Required', + ]; + + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Node\Expr\MethodCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Node\Expr\MethodCall); + + if (!$this->isName($node->name, 'setPreviewMode')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\node\NodeTypeInterface'))) { + return null; + } + + if (count($node->args) !== 1) { + return null; + } + + $argValue = $node->args[0]->value; + $enumCase = null; + + if ($argValue instanceof Node\Expr\ConstFetch) { + $constName = $this->getName($argValue); + if ($constName !== null && isset(self::CONST_TO_ENUM[$constName])) { + $enumCase = self::CONST_TO_ENUM[$constName]; + } + } + + if ($enumCase === null && $argValue instanceof Node\Scalar\LNumber) { + $enumCase = self::INT_TO_ENUM[$argValue->value] ?? null; + } + + if ($enumCase === null) { + return null; + } + + $cloned = clone $node; + $cloned->args[0] = new Node\Arg( + new Node\Expr\ClassConstFetch( + new Node\Name\FullyQualified('Drupal\node\NodePreviewMode'), + $enumCase + ) + ); + + return $cloned; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated DRUPAL_DISABLED/OPTIONAL/REQUIRED constants and integer literals in setPreviewMode() with NodePreviewMode enum cases (drupal:11.3.0)', [ + new ConfiguredCodeSample( + '$nodeType->setPreviewMode(DRUPAL_DISABLED);', + '$nodeType->setPreviewMode(\\Drupal\\node\\NodePreviewMode::Disabled);', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector.php b/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector.php new file mode 100644 index 000000000..1b4181aec --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector.php @@ -0,0 +1,203 @@ + 'Object', + 'FETCH_ASSOC' => 'Associative', + 'FETCH_NUM' => 'List', + 'FETCH_COLUMN' => 'Column', + 'FETCH_CLASS' => 'ClassObject', + ]; + + private const DRUPAL_FETCH_METHODS = [ + 'setFetchMode' => 0, + 'fetch' => 0, + 'fetchAll' => 0, + 'fetchAllAssoc' => 1, + ]; + + private const PDO_RETURN_METHODS = ['getClientStatement', 'getClientConnection']; + + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace PDO::FETCH_* constants with FetchAs enum cases in Drupal Database API calls', + [ + new ConfiguredCodeSample( + '$statement->setFetchMode(\\PDO::FETCH_ASSOC);', + '$statement->setFetchMode(\\Drupal\\Core\\Database\\Statement\\FetchAs::Associative);', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class, ArrayItem::class]; + } + + public function refactor(Node $node): ?Node + { + if ($node instanceof ArrayItem) { + foreach ($this->configuration as $configuration) { + if (!$this->rectorShouldApplyToDrupalVersion($configuration)) { + continue; + } + if ($this->isInBackwardsCompatibleCall($node)) { + continue; + } + + $result = $this->refactorArrayItem($node); + if ($result === null) { + return null; + } + + if ($this->supportBackwardsCompatibility($configuration)) { + $cloned = clone $result; + $cloned->value = $this->createBcCallOnExpr( + $node->value, + $result->value, + $configuration->getIntroducedVersion() + ); + + return $cloned; + } + + return $result; + } + + return null; + } + + return parent::refactor($node); + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + if ($node instanceof MethodCall) { + return $this->refactorMethodCall($node); + } + + return null; + } + + private function refactorMethodCall(MethodCall $node): ?MethodCall + { + $methodName = $this->getName($node->name); + if ($methodName === null || !array_key_exists($methodName, self::DRUPAL_FETCH_METHODS)) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Database\StatementInterface'))) { + return null; + } + + if ($node->var instanceof MethodCall) { + $calleeName = $this->getName($node->var->name); + if ($calleeName !== null && in_array($calleeName, self::PDO_RETURN_METHODS, true)) { + return null; + } + } + + $fetchArgIndex = self::DRUPAL_FETCH_METHODS[$methodName]; + $changed = false; + $cloned = clone $node; + + foreach ($cloned->args as $index => $arg) { + if (!$arg instanceof Arg || $index !== $fetchArgIndex) { + continue; + } + $replacement = $this->replacePdoFetchConst($arg->value); + if ($replacement !== null) { + $newArg = clone $arg; + $newArg->value = $replacement; + $cloned->args[$index] = $newArg; + $changed = true; + } + } + + return $changed ? $cloned : null; + } + + private function refactorArrayItem(ArrayItem $node): ?ArrayItem + { + if ($node->key === null) { + return null; + } + if (!($node->key instanceof String_) || $node->key->value !== 'fetch') { + return null; + } + $replacement = $this->replacePdoFetchConst($node->value); + if ($replacement === null) { + return null; + } + $cloned = clone $node; + $cloned->value = $replacement; + + return $cloned; + } + + private function replacePdoFetchConst(Node $node): ?ClassConstFetch + { + if (!$node instanceof ClassConstFetch) { + return null; + } + $className = $this->getName($node->class); + if ($className !== 'PDO') { + return null; + } + $constName = $this->getName($node->name); + if ($constName === null || !isset(self::FETCH_MAP[$constName])) { + return null; + } + + return new ClassConstFetch( + new FullyQualified('Drupal\Core\Database\Statement\FetchAs'), + self::FETCH_MAP[$constName] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector.php b/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector.php new file mode 100644 index 000000000..fc4a682cd --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector.php @@ -0,0 +1,98 @@ +> */ + public function getNodeTypes(): array + { + return [StaticCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof StaticCall); + if (!$this->isName($node->name, 'installModule')) { + return null; + } + if (!$node->class instanceof Name) { + return null; + } + $className = $this->getName($node->class); + if (!in_array($className, [ + 'Drupal\Core\Recipe\RecipeRunner', + 'RecipeRunner', + 'static', + 'self', + ], true)) { + return null; + } + if (empty($node->args)) { + return null; + } + $firstArg = $node->args[0]; + if (!$firstArg instanceof Arg) { + return null; + } + $wrappedArray = new Array_([new ArrayItem($firstArg->value)]); + $newNode = clone $node; + $newNode->name = new Identifier('installModules'); + $newNode->args = array_merge([new Arg($wrappedArray)], array_slice($node->args, 1)); + + return $newNode; + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector.php b/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector.php new file mode 100644 index 000000000..ea9e1152f --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector.php @@ -0,0 +1,109 @@ +isName($node->name, 'delete')) { + return null; + } + + if (count($node->args) !== 1) { + return null; + } + + if (!$this->isSessionManagerType($node->var)) { + return null; + } + + $service = new Node\Expr\StaticCall( + new Node\Name\FullyQualified('Drupal'), + 'service', + [new Node\Arg(new Node\Expr\ClassConstFetch(new Node\Name\FullyQualified('Drupal\Core\Session\UserSessionRepositoryInterface'), 'class'))] + ); + + return new Node\Expr\MethodCall($service, 'deleteAll', [$node->args[0]]); + } + + private function isSessionManagerType(Node\Expr $node): bool + { + // Normal case: property typed as the interface or concrete class (with leading \). + if ($this->isObjectType($node, new ObjectType('Drupal\Core\Session\SessionManagerInterface'))) { + return true; + } + if ($this->isObjectType($node, new ObjectType('Drupal\Core\Session\SessionManager'))) { + return true; + } + + // Older Drupal contrib code often omits the leading \ in @var annotations: + // @var Drupal\Core\Session\SessionManager (no leading \) + // PHPStan resolves this relative to the current namespace, producing a wrong + // class name like "Vendor\Module\Drupal\Core\Session\SessionManager". We check + // the exact suffix so we still match the same two Drupal classes and nothing else. + foreach ($this->getType($node)->getObjectClassNames() as $className) { + if (str_ends_with($className, '\\Drupal\\Core\\Session\\SessionManagerInterface') + || str_ends_with($className, '\\Drupal\\Core\\Session\\SessionManager')) { + return true; + } + } + + return false; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated SessionManager::delete($uid) with UserSessionRepositoryInterface service', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$this->sessionManager->delete($uid); +CODE_BEFORE, + <<<'CODE_AFTER' +\Drupal::service(\Drupal\Core\Session\UserSessionRepositoryInterface::class)->deleteAll($uid); +CODE_AFTER, + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector.php b/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector.php new file mode 100644 index 000000000..fb87a6a6d --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector.php @@ -0,0 +1,101 @@ +getSession()->set(). + * + * Deprecated in drupal:11.2.0. + * + * @see https://www.drupal.org/node/3518914 + * @see https://www.drupal.org/node/3518527 + */ +final class ReplaceSessionWritesWithRequestSessionRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Assign::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Assign); + + if (!$node->var instanceof ArrayDimFetch) { + return null; + } + + $arrayDimFetch = $node->var; + + if (!$arrayDimFetch->var instanceof Variable) { + return null; + } + + if ($this->getName($arrayDimFetch->var) !== '_SESSION') { + return null; + } + + if ($arrayDimFetch->dim === null) { + return null; + } + + $drupalRequest = new StaticCall( + new FullyQualified('Drupal'), + 'request', + [] + ); + + $getSession = new MethodCall($drupalRequest, 'getSession', []); + + return new MethodCall( + $getSession, + 'set', + [ + new Arg($arrayDimFetch->dim), + new Arg($node->expr), + ] + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated $_SESSION writes with \\Drupal::request()->getSession()->set() (drupal:11.2.0)', [ + new ConfiguredCodeSample( + '$_SESSION[\'my_key\'] = $value;', + '\\Drupal::request()->getSession()->set(\'my_key\', $value);', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector.php b/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector.php new file mode 100644 index 000000000..15197d266 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector.php @@ -0,0 +1,122 @@ +get('css.gzip');", + "\\Drupal::config('system.performance')->get('css.compress');", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof MethodCall); + if (!$this->isNames($node->name, ['get', 'set'])) { + return null; + } + if (empty($node->args)) { + return null; + } + $firstArg = $node->args[0]; + if (!$firstArg instanceof Arg) { + return null; + } + $keyExpr = $firstArg->value; + if (!$keyExpr instanceof String_) { + return null; + } + $key = $keyExpr->value; + if ($key !== 'css.gzip' && $key !== 'js.gzip') { + return null; + } + if (!$this->isSystemPerformanceConfigReceiver($node->var)) { + return null; + } + $newKey = ($key === 'css.gzip') ? 'css.compress' : 'js.compress'; + $cloned = clone $node; + $cloned->args[0] = new Arg(new String_($newKey)); + + return $cloned; + } + + private function isSystemPerformanceConfigReceiver(Node $receiver): bool + { + $current = $receiver; + while ($current instanceof MethodCall) { + if ($this->isNames($current->name, self::CONFIG_ACCESSOR_METHODS)) { + if (!empty($current->args) && $current->args[0] instanceof Arg) { + $arg = $current->args[0]->value; + if ($arg instanceof String_ && $arg->value === 'system.performance') { + return true; + } + } + } + $current = $current->var; + } + if ($current instanceof StaticCall) { + if ($this->isName($current->name, 'config') && !empty($current->args)) { + $arg = $current->args[0]; + if ($arg instanceof Arg && $arg->value instanceof String_) { + return $arg->value->value === 'system.performance'; + } + } + } + + return false; + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector.php b/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector.php new file mode 100644 index 000000000..5396a061b --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector.php @@ -0,0 +1,103 @@ +getSetting('logo.url');", + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + new ConfiguredCodeSample( + '_system_default_theme_features();', + '\\Drupal\\Core\\Extension\\ThemeSettingsProvider::DEFAULT_THEME_FEATURES;', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + if (!$node->name instanceof Name) { + return null; + } + + $funcName = $node->name->toString(); + + if ($funcName === 'theme_get_setting') { + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new ClassConstFetch( + new FullyQualified(self::THEME_SETTINGS_PROVIDER), + 'class' + ))] + ); + + return new MethodCall($serviceCall, 'getSetting', $node->args); + } + + if ($funcName === '_system_default_theme_features') { + return new ClassConstFetch( + new FullyQualified(self::THEME_SETTINGS_PROVIDER), + 'DEFAULT_THEME_FEATURES' + ); + } + + return null; + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector.php b/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector.php new file mode 100644 index 000000000..85d6083b2 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector.php @@ -0,0 +1,68 @@ +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if ($this->getName($node) === 'twig_extension') { + return new String_('.html.twig'); + } + + return null; + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector.php b/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector.php new file mode 100644 index 000000000..ce4e777f0 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector.php @@ -0,0 +1,78 @@ +name property read with $userSession->getAccountName(). + * + * Deprecated in drupal:11.3.0, removed in drupal:12.0.0. + * + * @see https://www.drupal.org/node/3513877 + * @see https://www.drupal.org/node/3513856 + */ +final class ReplaceUserSessionNamePropertyRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Node\Expr\PropertyFetch::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Node\Expr\PropertyFetch); + + if (!$this->isName($node->name, 'name')) { + return null; + } + + // Skip $this->name: protected property access within UserSession is + // not deprecated and must not be rewritten to avoid infinite recursion + // (UserSession::getAccountName() itself reads $this->name). + if ($node->var instanceof Variable && $this->getName($node->var) === 'this') { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Session\UserSession'))) { + return null; + } + + return new Node\Expr\MethodCall($node->var, new Node\Identifier('getAccountName')); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated $userSession->name property read with $userSession->getAccountName() (drupal:11.3.0)', [ + new ConfiguredCodeSample( + '$name = $userSession->name;', + '$name = $userSession->getAccountName();', + [new DrupalIntroducedVersionConfiguration('11.3.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector.php b/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector.php new file mode 100644 index 000000000..2106d0580 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector.php @@ -0,0 +1,127 @@ + $view->status() + * - views_view_is_disabled($view) => !$view->status() + * - views_enable_view($view) => $view->enable()->save() + * - views_disable_view($view) => $view->disable()->save() + * - views_get_view_result(...) => \Drupal\views\Views::getViewResult(...) + * + * @see https://www.drupal.org/node/3572243 + * @see https://www.drupal.org/node/3572594 + */ +final class ReplaceViewsProceduralFunctionsRector extends AbstractDrupalCoreRector +{ + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Node\Expr\FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof Node\Expr\FuncCall); + + if (!$node->name instanceof Node\Name) { + return null; + } + + return match ($node->name->toString()) { + 'views_view_is_enabled' => $this->statusCall($node), + 'views_view_is_disabled' => $this->negatedStatusCall($node), + 'views_enable_view' => $this->enableSaveCall($node), + 'views_disable_view' => $this->disableSaveCall($node), + 'views_get_view_result' => $this->staticGetViewResult($node), + default => null, + }; + } + + private function statusCall(Node\Expr\FuncCall $node): ?Node + { + if (count($node->args) < 1) { + return null; + } + + return new Node\Expr\MethodCall($node->args[0]->value, new Node\Identifier('status')); + } + + private function negatedStatusCall(Node\Expr\FuncCall $node): ?Node + { + if (count($node->args) < 1) { + return null; + } + + return new Node\Expr\BooleanNot( + new Node\Expr\MethodCall($node->args[0]->value, new Node\Identifier('status')) + ); + } + + private function enableSaveCall(Node\Expr\FuncCall $node): ?Node + { + if (count($node->args) < 1) { + return null; + } + $enable = new Node\Expr\MethodCall($node->args[0]->value, new Node\Identifier('enable')); + + return new Node\Expr\MethodCall($enable, new Node\Identifier('save')); + } + + private function disableSaveCall(Node\Expr\FuncCall $node): ?Node + { + if (count($node->args) < 1) { + return null; + } + $disable = new Node\Expr\MethodCall($node->args[0]->value, new Node\Identifier('disable')); + + return new Node\Expr\MethodCall($disable, new Node\Identifier('save')); + } + + private function staticGetViewResult(Node\Expr\FuncCall $node): Node\Expr\StaticCall + { + return new Node\Expr\StaticCall( + new Node\Name\FullyQualified('Drupal\views\Views'), + new Node\Identifier('getViewResult'), + $node->args + ); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated Views procedural functions with OO equivalents (drupal:11.4.0)', [ + new ConfiguredCodeSample( + 'views_enable_view($view);', + '$view->enable()->save();', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector.php b/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector.php new file mode 100644 index 000000000..b234ced9d --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector.php @@ -0,0 +1,75 @@ +isName($node->name, 'fetchColumn')) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\Core\Database\StatementPrefetchIterator'))) { + return null; + } + + $newNode = clone $node; + $newNode->name = new Node\Identifier('fetchField'); + + return $newNode; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated StatementPrefetchIterator::fetchColumn() with fetchField()', [ + new ConfiguredCodeSample( + '$result = $statement->fetchColumn(0);', + '$result = $statement->fetchField(0);', + [new DrupalIntroducedVersionConfiguration('11.2.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector.php b/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector.php new file mode 100644 index 000000000..d20bf7944 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector.php @@ -0,0 +1,77 @@ +isName($node->name, 'getMigrationDependencies')) { + return null; + } + + if (count($node->args) === 0) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType('Drupal\migrate\Plugin\MigrationInterface'))) { + return null; + } + + $cloned = clone $node; + $cloned->args = []; + + return $cloned; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Strip removed $expand argument from getMigrationDependencies() calls on MigrationInterface (drupal:11.0.0)', [ + new ConfiguredCodeSample( + '$deps = $migration->getMigrationDependencies(TRUE);', + '$deps = $migration->getMigrationDependencies();', + [new DrupalIntroducedVersionConfiguration('11.0.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector.php b/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector.php new file mode 100644 index 000000000..00a4f8709 --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector.php @@ -0,0 +1,135 @@ +> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + assert($node instanceof FuncCall); + + if (!$node->name instanceof Name) { + return null; + } + + return match ($node->name->toString()) { + 'system_region_list' => $this->refactorSystemRegionList($node), + 'system_default_region' => $this->refactorSystemDefaultRegion($node), + default => null, + }; + } + + private function refactorSystemRegionList(FuncCall $node): ?MethodCall + { + if (empty($node->args) || !$node->args[0] instanceof Arg) { + return null; + } + + $themeExpr = $node->args[0]->value; + $method = 'listAllRegions'; + + if (isset($node->args[1]) && $node->args[1] instanceof Arg) { + $showArg = $node->args[1]->value; + if ( + ($showArg instanceof ConstFetch && in_array($this->getName($showArg), ['REGIONS_VISIBLE', 'visible'], true)) + || ($showArg instanceof String_ && $showArg->value === 'visible') + || ($showArg instanceof ClassConstFetch && $this->isName($showArg->name, 'REGIONS_VISIBLE')) + ) { + $method = 'listVisibleRegions'; + } + } + + return $this->buildThemeChainCall($themeExpr, $method); + } + + private function refactorSystemDefaultRegion(FuncCall $node): ?MethodCall + { + if (empty($node->args) || !$node->args[0] instanceof Arg) { + return null; + } + + return $this->buildThemeChainCall($node->args[0]->value, 'getDefaultRegion'); + } + + private function buildThemeChainCall(Expr $themeExpr, string $method): MethodCall + { + $serviceCall = new StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Arg(new String_('theme_handler'))] + ); + $getTheme = new MethodCall($serviceCall, 'getTheme', [new Arg($themeExpr)]); + + return new MethodCall($getTheme, $method, []); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Replace deprecated system_region_list() and system_default_region() with Theme object methods via the theme_handler service.', + [ + new ConfiguredCodeSample( + 'system_region_list($theme);', + "\Drupal::service('theme_handler')->getTheme(\$theme)->listAllRegions();", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'system_region_list($theme, REGIONS_VISIBLE);', + "\Drupal::service('theme_handler')->getTheme(\$theme)->listVisibleRegions();", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + new ConfiguredCodeSample( + 'system_default_region($theme);', + "\Drupal::service('theme_handler')->getTheme(\$theme)->getDefaultRegion();", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ] + ); + } +} diff --git a/src/Drupal11/Rector/Deprecation/SystemSortThemesRector.php b/src/Drupal11/Rector/Deprecation/SystemSortThemesRector.php new file mode 100644 index 000000000..7fdc9804f --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/SystemSortThemesRector.php @@ -0,0 +1,118 @@ +is_default) { + return -1; + } + if ($b->is_default) { + return 1; + } + return strcasecmp($a->info['name'], $b->info['name']); +}); +CODE_AFTER + ), + ] + ); + } + + /** @return array> */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** @param FuncCall $node */ + public function refactor(Node $node): ?Node + { + if (!$this->isName($node, 'uasort')) { + return null; + } + + if (count($node->args) < 2) { + return null; + } + + $secondArg = $node->args[1]; + if (!$secondArg instanceof Arg) { + return null; + } + + $callbackValue = $secondArg->value; + if (!$callbackValue instanceof String_ || $callbackValue->value !== 'system_sort_themes') { + return null; + } + + $varA = new Variable('a'); + $varB = new Variable('b'); + + $ifADefault = new If_( + new PropertyFetch($varA, 'is_default'), + ['stmts' => [new Return_(new UnaryMinus(new Int_(1)))]] + ); + + $ifBDefault = new If_( + new PropertyFetch($varB, 'is_default'), + ['stmts' => [new Return_(new Int_(1))]] + ); + + $returnStmt = new Return_( + new FuncCall( + new Node\Name('strcasecmp'), + [ + new Arg(new ArrayDimFetch(new PropertyFetch($varA, 'info'), new String_('name'))), + new Arg(new ArrayDimFetch(new PropertyFetch($varB, 'info'), new String_('name'))), + ] + ) + ); + + $closure = new Closure([ + 'static' => true, + 'params' => [new Param($varA), new Param($varB)], + 'stmts' => [$ifADefault, $ifBDefault, $returnStmt], + ]); + + $node->args[1] = new Arg($closure); + + return $node; + } +} diff --git a/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector.php b/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector.php new file mode 100644 index 000000000..7665e94da --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector.php @@ -0,0 +1,147 @@ +getEntityTypeIdKeyType($entityType) === 'integer' => $entityType->hasIntegerId() + * - $this->entityTypeSupportsComments($entityType) => $entityType->hasIntegerId() + * - $this->hasIntegerId($entityType) => $entityType->hasIntegerId() + * + * @see https://www.drupal.org/node/3566801 + * @see https://www.drupal.org/node/3566814 + */ +final class UseEntityTypeHasIntegerIdRector extends AbstractDrupalCoreRector +{ + private const METHOD_OWNER_CLASS = [ + 'entityTypeSupportsComments' => 'Drupal\comment\CommentTypeForm', + 'hasIntegerId' => 'Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage', + ]; + + private const GET_ENTITY_TYPE_ID_KEY_TYPE_CLASS = 'Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider'; + + /** + * @var array|DrupalIntroducedVersionConfiguration[] + */ + protected array $configuration; + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!$value instanceof DrupalIntroducedVersionConfiguration) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', DrupalIntroducedVersionConfiguration::class)); + } + } + parent::configure($configuration); + } + + public function getNodeTypes(): array + { + return [Node\Expr\BinaryOp\Identical::class, Node\Expr\MethodCall::class]; + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node + { + if ($node instanceof Node\Expr\BinaryOp\Identical) { + return $this->refactorIdentical($node); + } + + assert($node instanceof Node\Expr\MethodCall); + + return $this->refactorMethodCall($node); + } + + private function refactorIdentical(Node\Expr\BinaryOp\Identical $node): ?Node + { + [$methodCall, $string] = $this->extractPair($node); + + if ($methodCall === null || $string === null) { + return null; + } + + if (!$this->isThisCall($methodCall, 'getEntityTypeIdKeyType')) { + return null; + } + + if (!$this->isObjectType($methodCall->var, new ObjectType(self::GET_ENTITY_TYPE_ID_KEY_TYPE_CLASS))) { + return null; + } + + if ($string->value !== 'integer' || count($methodCall->args) !== 1) { + return null; + } + + return new Node\Expr\MethodCall($methodCall->args[0]->value, new Node\Identifier('hasIntegerId')); + } + + private function refactorMethodCall(Node\Expr\MethodCall $node): ?Node + { + if (!$node->var instanceof Node\Expr\Variable || $node->var->name !== 'this') { + return null; + } + + $name = $this->getName($node->name); + if ($name === null || !isset(self::METHOD_OWNER_CLASS[$name])) { + return null; + } + + if (!$this->isObjectType($node->var, new ObjectType(self::METHOD_OWNER_CLASS[$name]))) { + return null; + } + + if (count($node->args) !== 1) { + return null; + } + + return new Node\Expr\MethodCall($node->args[0]->value, new Node\Identifier('hasIntegerId')); + } + + /** + * @return array{Node\Expr\MethodCall|null, Node\Scalar\String_|null} + */ + private function extractPair(Node\Expr\BinaryOp\Identical $node): array + { + if ($node->left instanceof Node\Expr\MethodCall && $node->right instanceof Node\Scalar\String_) { + return [$node->left, $node->right]; + } + if ($node->right instanceof Node\Expr\MethodCall && $node->left instanceof Node\Scalar\String_) { + return [$node->right, $node->left]; + } + + return [null, null]; + } + + private function isThisCall(Node\Expr\MethodCall $node, string $methodName): bool + { + if (!$node->var instanceof Node\Expr\Variable || $node->var->name !== 'this') { + return false; + } + + return $this->isName($node->name, $methodName); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replace deprecated entity-type integer-ID helper methods with EntityTypeInterface::hasIntegerId() (drupal:11.4.0)', [ + new ConfiguredCodeSample( + "\$this->getEntityTypeIdKeyType(\$entity_type) === 'integer'", + '$entity_type->hasIntegerId()', + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ]); + } +} diff --git a/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector.php b/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector.php new file mode 100644 index 000000000..8fe600ecf --- /dev/null +++ b/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector.php @@ -0,0 +1,98 @@ +isName($node->class, 'Drupal\views\Views')) { + return null; + } + + $methodName = $this->getName($node->name); + if ($methodName !== 'pluginManager' && $methodName !== 'handlerManager') { + return null; + } + + if (count($node->args) === 0) { + return null; + } + + $arg = $node->args[0]; + if (!$arg instanceof Node\Arg) { + return null; + } + + $typeExpr = $arg->value; + $drupalClass = new Node\Name\FullyQualified('Drupal'); + + if ($typeExpr instanceof Node\Scalar\String_) { + return new Node\Expr\StaticCall( + $drupalClass, + 'service', + [new Node\Arg(new Node\Scalar\String_('plugin.manager.views.'.$typeExpr->value))] + ); + } + + $serviceLocatorCall = new Node\Expr\StaticCall( + $drupalClass, + 'service', + [new Node\Arg(new Node\Scalar\String_('views.plugin_managers'))] + ); + + return new Node\Expr\MethodCall($serviceLocatorCall, 'get', [new Node\Arg($typeExpr)]); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces deprecated Views::pluginManager() and Views::handlerManager() with \\Drupal::service() equivalents', [ + new ConfiguredCodeSample( + "Views::handlerManager('filter');\nViews::pluginManager(\$type);", + "\\Drupal::service('plugin.manager.views.filter');\n\\Drupal::service('views.plugin_managers')->get(\$type);", + [new DrupalIntroducedVersionConfiguration('11.4.0')] + ), + ]); + } +} diff --git a/src/Drupal8/Rector/Deprecation/DrupalServiceRenameRector.php b/src/Drupal8/Rector/Deprecation/DrupalServiceRenameRector.php index f530b832f..99ec38382 100644 --- a/src/Drupal8/Rector/Deprecation/DrupalServiceRenameRector.php +++ b/src/Drupal8/Rector/Deprecation/DrupalServiceRenameRector.php @@ -5,19 +5,9 @@ namespace DrupalRector\Drupal8\Rector\Deprecation; use DrupalRector\Drupal8\Rector\ValueObject\DrupalServiceRenameConfiguration; -use PhpParser\Node; -use Rector\Contract\Rector\ConfigurableRectorInterface; -use Rector\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; -use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -class DrupalServiceRenameRector extends AbstractRector implements ConfigurableRectorInterface +class DrupalServiceRenameRector extends \DrupalRector\Rector\Deprecation\DrupalServiceRenameRector { - /** - * @var DrupalServiceRenameConfiguration[] - */ - protected array $staticArgumentRenameConfigs = []; - public function configure(array $configuration): void { foreach ($configuration as $value) { @@ -26,55 +16,6 @@ public function configure(array $configuration): void } } - $this->staticArgumentRenameConfigs = $configuration; - } - - public function getNodeTypes(): array - { - return [ - Node\Expr\StaticCall::class, - ]; - } - - public function refactor(Node $node) - { - if ($node instanceof Node\Expr\StaticCall) { - foreach ($this->staticArgumentRenameConfigs as $configuration) { - if ($this->getName($node->name) === 'service' && (string) $node->class === 'Drupal') { - if (count($node->args) === 1) { - /* @var Node\Arg $argument */ - $argument = $node->args[0]; - - if ($argument->value instanceof Node\Scalar\String_ && $argument->value->value === $configuration->getDeprecatedService()) { - $node->args[0] = new Node\Arg(new Node\Scalar\String_($configuration->getNewService())); - - return $node; - } - } - } - } - } - - return null; - } - - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Renames the IDs in Drupal::service() calls', [ - new ConfiguredCodeSample( - <<<'CODE_BEFORE' -\Drupal::service('old')->foo(); -CODE_BEFORE, - <<<'CODE_AFTER' -\Drupal::service('bar')->foo(); -CODE_AFTER, - [ - new DrupalServiceRenameConfiguration( - 'old', - 'bar', - ), - ] - ), - ]); + parent::configure($configuration); } } diff --git a/src/Drupal8/Rector/Deprecation/EntityInterfaceLinkRector.php b/src/Drupal8/Rector/Deprecation/EntityInterfaceLinkRector.php index 78ca41d91..017c2ce76 100644 --- a/src/Drupal8/Rector/Deprecation/EntityInterfaceLinkRector.php +++ b/src/Drupal8/Rector/Deprecation/EntityInterfaceLinkRector.php @@ -67,26 +67,20 @@ public function refactor(Node $node): ?Node { assert($node instanceof Node\Stmt\Expression); - if (!($node->expr instanceof Node\Expr\MethodCall) && !($node->expr instanceof Node\Expr\Assign && $node->expr->expr instanceof Node\Expr\MethodCall)) { - return null; - } - - if ($node->expr instanceof Node\Expr\MethodCall && $this->getName($node->expr->name) !== 'link') { - return null; - } - - if (($node->expr instanceof Node\Expr\Assign && $node->expr->expr instanceof Node\Expr\MethodCall) && $this->getName($node->expr->expr->name) !== 'link') { - return null; - } - if ($node->expr instanceof Node\Expr\MethodCall) { + if ($this->getName($node->expr->name) !== 'link') { + return null; + } $methodCall = $this->getMethodCall($node->expr, $node); $node->expr = $methodCall; return $node; } - if ($node->expr->expr instanceof Node\Expr\MethodCall) { + if ($node->expr instanceof Node\Expr\Assign && $node->expr->expr instanceof Node\Expr\MethodCall) { + if ($this->getName($node->expr->expr->name) !== 'link') { + return null; + } $methodCall = $this->getMethodCall($node->expr->expr, $node); $node->expr->expr = $methodCall; diff --git a/src/Drupal8/Rector/ValueObject/DrupalServiceRenameConfiguration.php b/src/Drupal8/Rector/ValueObject/DrupalServiceRenameConfiguration.php index d24d59335..d2e5e2915 100644 --- a/src/Drupal8/Rector/ValueObject/DrupalServiceRenameConfiguration.php +++ b/src/Drupal8/Rector/ValueObject/DrupalServiceRenameConfiguration.php @@ -4,25 +4,12 @@ namespace DrupalRector\Drupal8\Rector\ValueObject; -class DrupalServiceRenameConfiguration -{ - protected string $newService; - - protected string $deprecatedService; +use DrupalRector\Rector\ValueObject\DrupalServiceRenameConfiguration as GenericDrupalServiceRenameConfiguration; +class DrupalServiceRenameConfiguration extends GenericDrupalServiceRenameConfiguration +{ public function __construct(string $deprecatedService, string $newService) { - $this->deprecatedService = $deprecatedService; - $this->newService = $newService; - } - - public function getNewService(): string - { - return $this->newService; - } - - public function getDeprecatedService(): string - { - return $this->deprecatedService; + parent::__construct('', $deprecatedService, $newService); } } diff --git a/src/Drupal9/Rector/Deprecation/FunctionToFirstArgMethodRector.php b/src/Drupal9/Rector/Deprecation/FunctionToFirstArgMethodRector.php index 769d014d9..30146d3f6 100644 --- a/src/Drupal9/Rector/Deprecation/FunctionToFirstArgMethodRector.php +++ b/src/Drupal9/Rector/Deprecation/FunctionToFirstArgMethodRector.php @@ -5,29 +5,9 @@ namespace DrupalRector\Drupal9\Rector\Deprecation; use DrupalRector\Drupal9\Rector\ValueObject\FunctionToFirstArgMethodConfiguration; -use PhpParser\Node; -use Rector\Contract\Rector\ConfigurableRectorInterface; -use Rector\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; -use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class FunctionToFirstArgMethodRector extends AbstractRector implements ConfigurableRectorInterface +final class FunctionToFirstArgMethodRector extends \DrupalRector\Rector\Deprecation\FunctionToFirstArgMethodRector { - /** - * @var FunctionToFirstArgMethodConfiguration[] - */ - private array $configuration; - - /** - * {@inheritdoc} - */ - public function getNodeTypes(): array - { - return [ - Node\Expr\FuncCall::class, - ]; - } - public function configure(array $configuration): void { foreach ($configuration as $value) { @@ -36,51 +16,6 @@ public function configure(array $configuration): void } } - $this->configuration = $configuration; - } - - /** - * {@inheritdoc} - */ - public function refactor(Node $node): ?Node - { - assert($node instanceof Node\Expr\FuncCall); - - foreach ($this->configuration as $configuration) { - if ($this->getName($node) !== $configuration->getDeprecatedFunctionName()) { - continue; - } - $args = $node->getArgs(); - if (count($args) !== 1) { - continue; - } - - return $this->nodeFactory->createMethodCall($args[0]->value, $configuration->getMethodName()); - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function getRuleDefinition(): RuleDefinition - { - return new RuleDefinition('Fixes deprecated taxonomy_implode_tags() calls', [ - new ConfiguredCodeSample( - <<<'CODE_BEFORE' -$url = taxonomy_term_uri($term); -$name = taxonomy_term_title($term); -CODE_BEFORE, - <<<'CODE_AFTER' -$url = $term->toUrl(); -$name = $term->label(); -CODE_AFTER, - [ - new FunctionToFirstArgMethodConfiguration('taxonomy_term_uri', 'toUrl'), - new FunctionToFirstArgMethodConfiguration('taxonomy_term_title', 'label'), - ] - ), - ]); + parent::configure($configuration); } } diff --git a/src/Drupal9/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php b/src/Drupal9/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php index 86299e61f..4e1c1f26b 100644 --- a/src/Drupal9/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php +++ b/src/Drupal9/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php @@ -4,28 +4,12 @@ namespace DrupalRector\Drupal9\Rector\ValueObject; -class FunctionToFirstArgMethodConfiguration -{ - private string $deprecatedFunctionName; - private string $methodName; +use DrupalRector\Rector\ValueObject\FunctionToFirstArgMethodConfiguration as GenericFunctionToFirstArgMethodConfiguration; - /** - * @param string $deprecatedFunctionName - * @param string $methodName - */ +class FunctionToFirstArgMethodConfiguration extends GenericFunctionToFirstArgMethodConfiguration +{ public function __construct(string $deprecatedFunctionName, string $methodName) { - $this->deprecatedFunctionName = $deprecatedFunctionName; - $this->methodName = $methodName; - } - - public function getDeprecatedFunctionName(): string - { - return $this->deprecatedFunctionName; - } - - public function getMethodName(): string - { - return $this->methodName; + parent::__construct('', $deprecatedFunctionName, $methodName); } } diff --git a/src/Rector/AbstractDrupalCoreRector.php b/src/Rector/AbstractDrupalCoreRector.php index b3d6929ce..f5d5f3367 100644 --- a/src/Rector/AbstractDrupalCoreRector.php +++ b/src/Rector/AbstractDrupalCoreRector.php @@ -6,6 +6,7 @@ use Drupal\Component\Utility\DeprecationHelper; use DrupalRector\Contract\VersionedConfigurationInterface; +use DrupalRector\Services\DrupalRectorSettings; use PhpParser\Node; use PhpParser\Node\Expr\ArrowFunction; use PHPStan\Reflection\MethodReflection; @@ -20,6 +21,10 @@ abstract class AbstractDrupalCoreRector extends AbstractRector implements Config */ protected array $configuration = []; + public function __construct(private readonly DrupalRectorSettings $drupalRectorSettings) + { + } + public function configure(array $configuration): void { foreach ($configuration as $value) { @@ -39,21 +44,21 @@ protected function isInBackwardsCompatibleCall(Node $node): bool $scope = $node->getAttribute(AttributeKey::SCOPE); - $callStack = $scope->getFunctionCallStackWithParameters(); - if (count($callStack) === 0) { - return false; - } - [$function, $parameter] = $callStack[0]; - if (!$function instanceof MethodReflection) { - return false; - } - if ($function->getName() !== 'backwardsCompatibleCall' - || $function->getDeclaringClass()->getName() !== DeprecationHelper::class - ) { - return false; + foreach ($scope->getFunctionCallStackWithParameters() as [$function, $parameter]) { + if (!$function instanceof MethodReflection) { + continue; + } + if ($function->getName() !== 'backwardsCompatibleCall' + || $function->getDeclaringClass()->getName() !== DeprecationHelper::class + ) { + continue; + } + if ($parameter !== null && $parameter->getName() === 'deprecatedCallable') { + return true; + } } - return $parameter !== null && $parameter->getName() === 'deprecatedCallable'; + return false; } public function refactor(Node $node) @@ -84,9 +89,8 @@ public function refactor(Node $node) return $result; } - // Create a backwards compatible call if the node is a call-like expression. - if ($node instanceof Node\Expr\CallLike && $result instanceof Node\Expr\CallLike) { - return $this->createBcCallOnCallLike($node, $result, $configuration->getIntroducedVersion()); + if ($node instanceof Node\Expr && $result instanceof Node\Expr) { + return $this->createBcCallOnExpr($node, $result, $configuration->getIntroducedVersion()); } return $result; @@ -102,7 +106,7 @@ public function refactor(Node $node) */ abstract protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration); - private function createBcCallOnCallLike(Node\Expr\CallLike $node, Node\Expr\CallLike $result, string $introducedVersion): Node\Expr\StaticCall + protected function createBcCallOnExpr(Node\Expr $node, Node\Expr $result, string $introducedVersion): Node\Expr\StaticCall { $clonedNode = clone $node; @@ -132,7 +136,7 @@ public function installedDrupalVersion(): string return str_replace([ '.x-dev', '-dev', - ], '.0', \Drupal::VERSION); + ], '.0', $this->drupalRectorSettings->getDrupalVersion() ?? \Drupal::VERSION); } /** @@ -149,6 +153,12 @@ public function installedDrupalVersion(): string */ public function supportBackwardsCompatibility(VersionedConfigurationInterface $configuration): bool { - return !(version_compare($this->installedDrupalVersion(), '10.1.0', '<') || version_compare($configuration->getIntroducedVersion(), '10.0.0', '<')); + if (!$this->drupalRectorSettings->isBackwardCompatibilityEnabled()) { + return false; + } + + $minimumVersion = $this->drupalRectorSettings->getMinimumCoreVersionSupported(); + + return !(version_compare($minimumVersion, '10.1.0', '<') || version_compare($configuration->getIntroducedVersion(), '10.0.0', '<') || version_compare($minimumVersion, $configuration->getIntroducedVersion(), '>=')); } } diff --git a/src/Rector/Convert/HookConvertRector.php b/src/Rector/Convert/HookConvertRector.php index 786298743..81eed542f 100644 --- a/src/Rector/Convert/HookConvertRector.php +++ b/src/Rector/Convert/HookConvertRector.php @@ -131,7 +131,7 @@ public function refactor(Node $node): Node|int|null ? $this->getFile()->getFilePath() : $this->file->getFilePath(); $ext = pathinfo($filePath, \PATHINFO_EXTENSION); - if (!in_array($ext, ['inc', 'module'])) { + if (!in_array($ext, ['inc', 'module', 'theme'])) { return null; } if ($filePath !== $this->inputFilename) { @@ -366,7 +366,8 @@ protected function getMethodName(Function_ $node): string public function getLegacyHookFunction(Function_ $node): Function_ { $methodCall = new Node\Expr\MethodCall($this->drupalServiceCall, $this->getMethodName($node), self::convertParamsToArgs($node)); - $hasReturn = (new NodeFinder())->findFirstInstanceOf([$node], Node\Stmt\Return_::class); + $isVoid = $node->returnType instanceof Node\Identifier && $node->returnType->toString() === 'void'; + $hasReturn = !$isVoid && (new NodeFinder())->findFirst([$node], fn (Node $n) => $n instanceof Node\Stmt\Return_ && $n->expr !== null); $node->stmts = [$hasReturn ? new Node\Stmt\Return_($methodCall) : new Node\Stmt\Expression($methodCall)]; // Mark this function as a legacy hook. $node->attrGroups[] = new Node\AttributeGroup([new Node\Attribute(new FullyQualified('Drupal\Core\Hook\Attribute\LegacyHook'))]); diff --git a/src/Rector/Deprecation/ClassConstantToClassConstantRector.php b/src/Rector/Deprecation/ClassConstantToClassConstantRector.php index 64eed5b6d..2ce0a62ae 100644 --- a/src/Rector/Deprecation/ClassConstantToClassConstantRector.php +++ b/src/Rector/Deprecation/ClassConstantToClassConstantRector.php @@ -4,11 +4,10 @@ namespace DrupalRector\Rector\Deprecation; +use DrupalRector\Contract\VersionedConfigurationInterface; +use DrupalRector\Rector\AbstractDrupalCoreRector; use DrupalRector\Rector\ValueObject\ClassConstantToClassConstantConfiguration; -use DrupalRector\Rector\ValueObject\ConstantToClassConfiguration; use PhpParser\Node; -use Rector\Contract\Rector\ConfigurableRectorInterface; -use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,22 +17,17 @@ * What is covered: * - Replacement with a use statement. */ -class ClassConstantToClassConstantRector extends AbstractRector implements ConfigurableRectorInterface +class ClassConstantToClassConstantRector extends AbstractDrupalCoreRector { - /** - * @var ClassConstantToClassConstantConfiguration[] - */ - private array $constantToClassRenames; - public function configure(array $configuration): void { foreach ($configuration as $value) { if (!$value instanceof ClassConstantToClassConstantConfiguration) { - throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', ConstantToClassConfiguration::class)); + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', ClassConstantToClassConstantConfiguration::class)); } } - $this->constantToClassRenames = $configuration; + parent::configure($configuration); } /** @@ -59,18 +53,21 @@ public function getRuleDefinition(): RuleDefinition 'ROUTE_NAME', 'Drupal\Core\Routing\RouteObjectInterface', 'ROUTE_NAME', + '9.1.0', ), new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'ROUTE_OBJECT', 'Drupal\Core\Routing\RouteObjectInterface', 'ROUTE_OBJECT', + '9.1.0', ), new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'CONTROLLER_NAME', 'Drupal\Core\Routing\RouteObjectInterface', 'CONTROLLER_NAME', + '9.1.0', ), ] ), @@ -87,24 +84,19 @@ public function getNodeTypes(): array ]; } - /** - * {@inheritdoc} - */ - public function refactor(Node $node): ?Node + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node { assert($node instanceof Node\Expr\ClassConstFetch); + assert($configuration instanceof ClassConstantToClassConstantConfiguration); - foreach ($this->constantToClassRenames as $constantToClassRename) { - if ($this->getName($node->name) === $constantToClassRename->getDeprecated() && $this->getName($node->class) === $constantToClassRename->getDeprecatedClass()) { - // We add a fully qualified class name and the parameters in `rector.php` adds the use statement. - $fully_qualified_class = new Node\Name\FullyQualified($constantToClassRename->getClass()); - - $name = new Node\Identifier($constantToClassRename->getConstant()); - - return new Node\Expr\ClassConstFetch($fully_qualified_class, $name); - } + if ($this->getName($node->name) !== $configuration->getDeprecated() || $this->getName($node->class) !== $configuration->getDeprecatedClass()) { + return null; } - return null; + // We add a fully qualified class name and the parameters in `rector.php` adds the use statement. + return new Node\Expr\ClassConstFetch( + new Node\Name\FullyQualified($configuration->getClass()), + new Node\Identifier($configuration->getConstant()) + ); } } diff --git a/src/Rector/Deprecation/ConstantToClassConstantRector.php b/src/Rector/Deprecation/ConstantToClassConstantRector.php index 0ac8b670e..61b93e0e7 100644 --- a/src/Rector/Deprecation/ConstantToClassConstantRector.php +++ b/src/Rector/Deprecation/ConstantToClassConstantRector.php @@ -4,10 +4,10 @@ namespace DrupalRector\Rector\Deprecation; +use DrupalRector\Contract\VersionedConfigurationInterface; +use DrupalRector\Rector\AbstractDrupalCoreRector; use DrupalRector\Rector\ValueObject\ConstantToClassConfiguration; use PhpParser\Node; -use Rector\Contract\Rector\ConfigurableRectorInterface; -use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -17,13 +17,8 @@ * What is covered: * - Replacement with a use statement. */ -class ConstantToClassConstantRector extends AbstractRector implements ConfigurableRectorInterface +class ConstantToClassConstantRector extends AbstractDrupalCoreRector { - /** - * @var ConstantToClassConfiguration[] - */ - private array $constantToClassRenames; - public function configure(array $configuration): void { foreach ($configuration as $value) { @@ -32,61 +27,37 @@ public function configure(array $configuration): void } } - $this->constantToClassRenames = $configuration; + parent::configure($configuration); } - /** - * {@inheritdoc} - */ public function getRuleDefinition(): RuleDefinition { - return new RuleDefinition('Fixes deprecated contant use, used in Drupal 8 and 9 deprecations', [ + return new RuleDefinition('Fixes deprecated constant use, used in Drupal 8 and later deprecations', [ new ConfiguredCodeSample( - <<<'CODE_BEFORE' -$result = file_unmanaged_copy($source, $destination, DEPRECATED_CONSTANT); -CODE_BEFORE, - <<<'CODE_AFTER' -$result = file_unmanaged_copy($source, $destination, \Drupal\MyClass::CONSTANT); -CODE_AFTER, - [ - new ConstantToClassConfiguration( - 'DEPRECATED_CONSTANT', - 'Drupal\MyClass', - 'CONSTANT' - ), - ] + '$result = file_unmanaged_copy($source, $destination, DEPRECATED_CONSTANT);', + '$result = file_unmanaged_copy($source, $destination, \Drupal\MyClass::CONSTANT);', + [new ConstantToClassConfiguration('DEPRECATED_CONSTANT', 'Drupal\MyClass', 'CONSTANT', '8.0.0')] ), ]); } - /** - * {@inheritdoc} - */ public function getNodeTypes(): array { - return [ - Node\Expr\ConstFetch::class, - ]; + return [Node\Expr\ConstFetch::class]; } - /** - * {@inheritdoc} - */ - public function refactor(Node $node): ?Node + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node { assert($node instanceof Node\Expr\ConstFetch); + assert($configuration instanceof ConstantToClassConfiguration); - foreach ($this->constantToClassRenames as $constantToClassRename) { - if ($this->getName($node->name) === $constantToClassRename->getDeprecated()) { - // We add a fully qualified class name and the parameters in `rector.php` adds the use statement. - $fully_qualified_class = new Node\Name\FullyQualified($constantToClassRename->getClass()); - - $name = new Node\Identifier($constantToClassRename->getConstant()); - - return new Node\Expr\ClassConstFetch($fully_qualified_class, $name); - } + if ($this->getName($node->name) !== $configuration->getDeprecated()) { + return null; } - return null; + return new Node\Expr\ClassConstFetch( + new Node\Name\FullyQualified($configuration->getClass()), + new Node\Identifier($configuration->getConstant()) + ); } } diff --git a/src/Rector/Deprecation/DrupalServiceRenameRector.php b/src/Rector/Deprecation/DrupalServiceRenameRector.php new file mode 100644 index 000000000..cbf9a2497 --- /dev/null +++ b/src/Rector/Deprecation/DrupalServiceRenameRector.php @@ -0,0 +1,74 @@ +getName($node->name) !== 'service' || (string) $node->class !== 'Drupal') { + return null; + } + + if (count($node->args) !== 1) { + return null; + } + + $argument = $node->args[0]; + if (!$argument instanceof Node\Arg || !$argument->value instanceof Node\Scalar\String_) { + return null; + } + + if ($argument->value->value !== $configuration->getDeprecatedService()) { + return null; + } + + $newNode = clone $node; + $newNode->args[0] = new Node\Arg(new Node\Scalar\String_($configuration->getNewService())); + + return $newNode; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Renames the IDs in Drupal::service() calls', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +\Drupal::service('old')->foo(); +CODE_BEFORE, + <<<'CODE_AFTER' +\Drupal::service('bar')->foo(); +CODE_AFTER, + [new DrupalServiceRenameConfiguration('11.4.0', 'old', 'bar')] + ), + ]); + } +} diff --git a/src/Rector/Deprecation/FunctionCallRemovalRector.php b/src/Rector/Deprecation/FunctionCallRemovalRector.php new file mode 100644 index 000000000..0c7ab8d04 --- /dev/null +++ b/src/Rector/Deprecation/FunctionCallRemovalRector.php @@ -0,0 +1,73 @@ +configuration = array_merge($this->configuration, $configuration); + } + + public function getNodeTypes(): array + { + return [Node\Stmt\Expression::class]; + } + + public function refactor(Node $node): mixed + { + assert($node instanceof Node\Stmt\Expression); + + if (!$node->expr instanceof Node\Expr\FuncCall) { + return null; + } + + $name = $this->getName($node->expr); + if ($name === null) { + return null; + } + + foreach ($this->configuration as $configuration) { + if ($name === $configuration->getFunctionName()) { + return NodeVisitor::REMOVE_NODE; + } + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Removes deprecated function call statements that have no replacement', [ + new ConfiguredCodeSample( + 'deprecated_function();', + '', + [new FunctionCallRemovalConfiguration('deprecated_function')] + ), + ]); + } +} diff --git a/src/Rector/Deprecation/FunctionToFirstArgMethodRector.php b/src/Rector/Deprecation/FunctionToFirstArgMethodRector.php new file mode 100644 index 000000000..f6f4071cd --- /dev/null +++ b/src/Rector/Deprecation/FunctionToFirstArgMethodRector.php @@ -0,0 +1,63 @@ +getName($node) !== $configuration->getDeprecatedFunctionName()) { + return null; + } + + $args = $node->getArgs(); + if (count($args) !== 1) { + return null; + } + + return $this->nodeFactory->createMethodCall($args[0]->value, $configuration->getMethodName()); + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Replaces function calls where the first argument is an object with a method call on that object', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$url = taxonomy_term_uri($term); +CODE_BEFORE, + <<<'CODE_AFTER' +$url = $term->toUrl(); +CODE_AFTER, + [new FunctionToFirstArgMethodConfiguration('9.3.0', 'taxonomy_term_uri', 'toUrl')] + ), + ]); + } +} diff --git a/src/Rector/Deprecation/FunctionToServiceRector.php b/src/Rector/Deprecation/FunctionToServiceRector.php index 26f1813ff..48276be26 100644 --- a/src/Rector/Deprecation/FunctionToServiceRector.php +++ b/src/Rector/Deprecation/FunctionToServiceRector.php @@ -57,8 +57,11 @@ public function refactorWithConfiguration(Node $node, VersionedConfigurationInte assert($node instanceof Node\Expr\FuncCall); if ($this->getName($node->name) === $configuration->getDeprecatedFunctionName()) { - // This creates a service call like `\Drupal::service('file_system'). - $service = new Node\Expr\StaticCall(new Node\Name\FullyQualified('Drupal'), 'service', [new Node\Arg(new Node\Scalar\String_($configuration->getServiceName()))]); + $serviceArg = $configuration->useClassSyntax() + ? new Node\Arg(new Node\Expr\ClassConstFetch(new Node\Name\FullyQualified($configuration->getServiceName()), 'class')) + : new Node\Arg(new Node\Scalar\String_($configuration->getServiceName())); + + $service = new Node\Expr\StaticCall(new Node\Name\FullyQualified('Drupal'), 'service', [$serviceArg]); $method_name = new Node\Identifier($configuration->getServiceMethodName()); diff --git a/src/Rector/Deprecation/MethodToMethodWithCheckRector.php b/src/Rector/Deprecation/MethodToMethodWithCheckRector.php index 5d8f237cc..588c78a4c 100644 --- a/src/Rector/Deprecation/MethodToMethodWithCheckRector.php +++ b/src/Rector/Deprecation/MethodToMethodWithCheckRector.php @@ -4,13 +4,11 @@ namespace DrupalRector\Rector\Deprecation; +use DrupalRector\Contract\VersionedConfigurationInterface; +use DrupalRector\Rector\AbstractDrupalCoreRector; use DrupalRector\Rector\ValueObject\MethodToMethodWithCheckConfiguration; -use DrupalRector\Services\AddCommentService; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Contract\Rector\ConfigurableRectorInterface; -use Rector\Exception\ShouldNotHappenException; -use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -18,28 +16,12 @@ * Replaces deprecated method calls with a new method. * * What is covered: - * - Changes the name of the method. - * - * Improvement opportunities: - * - Checks the variable has a certain class. + * - Changes the name of the method when the receiver type can be inferred. + * - Wraps in DeprecationHelper::backwardsCompatibleCall when the introduced + * version warrants BC support. */ -class MethodToMethodWithCheckRector extends AbstractRector implements ConfigurableRectorInterface +class MethodToMethodWithCheckRector extends AbstractDrupalCoreRector { - /** - * @var MethodToMethodWithCheckConfiguration[] - */ - private array $configuration; - - /** - * @var AddCommentService - */ - private AddCommentService $commentService; - - public function __construct(AddCommentService $commentService) - { - $this->commentService = $commentService; - } - public function configure(array $configuration): void { foreach ($configuration as $value) { @@ -48,7 +30,7 @@ public function configure(array $configuration): void } } - $this->configuration = $configuration; + parent::configure($configuration); } /** @@ -56,101 +38,30 @@ public function configure(array $configuration): void */ public function getNodeTypes(): array { - return [ - Node\Stmt\Expression::class, - Node\Expr\MethodCall::class, - ]; + return [Node\Expr\MethodCall::class]; } - /** - * {@inheritdoc} - */ - public function refactor(Node $node): ?Node + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration): ?Node { - assert($node instanceof Node\Stmt\Expression || $node instanceof Node\Expr\MethodCall); + assert($node instanceof Node\Expr\MethodCall); + assert($configuration instanceof MethodToMethodWithCheckConfiguration); - if (!$node instanceof Node\Expr\MethodCall && !$node->expr instanceof Node\Expr\MethodCall && !($node->expr instanceof Node\Expr\Assign && $node->expr->expr instanceof Node\Expr\MethodCall)) { + if ($this->getName($node->name) !== $configuration->getDeprecatedMethodName()) { return null; } - foreach ($this->configuration as $configuration) { - if ($node instanceof Node\Expr\MethodCall && $this->getName($node->name) !== $configuration->getDeprecatedMethodName()) { - continue; - } - - if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\MethodCall && $this->getName($node->expr->name) !== $configuration->getDeprecatedMethodName()) { - continue; - } - - if ($node instanceof Node\Stmt\Expression && $node->expr instanceof Node\Expr\Assign && $node->expr->expr instanceof Node\Expr\MethodCall && $this->getName($node->expr->expr->name) !== $configuration->getDeprecatedMethodName()) { - continue; - } - - if ($node instanceof Node\Expr\MethodCall) { - $methodNode = $this->refactorNode($node, null, $configuration); - if (is_null($methodNode)) { - continue; - } - - return $methodNode; - } - - if ($node->expr instanceof Node\Expr\MethodCall) { - $methodNode = $this->refactorNode($node->expr, $node, $configuration); - if (is_null($methodNode)) { - continue; - } - $node->expr = $methodNode; - } elseif ($node->expr instanceof Node\Expr\Assign && $node->expr->expr instanceof Node\Expr\MethodCall) { - $methodNode = $this->refactorNode($node->expr->expr, $node, $configuration); - if (is_null($methodNode)) { - continue; - } - $node->expr->expr = $methodNode; - } - - return $node; - } - - return null; - } - - public function refactorNode(Node\Expr\MethodCall $node, ?Node\Stmt\Expression $statement, MethodToMethodWithCheckConfiguration $configuration): ?Node\Expr\MethodCall - { $callerType = $this->nodeTypeResolver->getType($node->var); $expectedType = new ObjectType($configuration->getClassName()); $isSuperOf = $expectedType->isSuperTypeOf($callerType); - if ($isSuperOf->yes()) { - $node->name = new Node\Identifier($configuration->getMethodName()); - - return $node; + if (!$isSuperOf->yes() && !$isSuperOf->maybe()) { + return null; } - if ($isSuperOf->maybe()) { - if ($node->var instanceof Node\Expr\Variable) { - $node_var = $node->var->name; - $node_var = "$$node_var"; - } elseif ($node->var instanceof Node\Expr\MethodCall) { - $node_var = $node->var->name; - $node_var = "$node_var()"; - } else { - throw new ShouldNotHappenException('Unexpected node type: '.get_class($node->var)); - } - $className = $configuration->getClassName(); - - if (!is_null($statement)) { - $this->commentService->addDrupalRectorComment( - $statement, - "Please confirm that `$node_var` is an instance of `$className`. Only the method name and not the class name was checked for this replacement, so this may be a false positive." - ); - } - $node->name = new Node\Identifier($configuration->getMethodName()); - - return $node; - } + $newNode = clone $node; + $newNode->name = new Node\Identifier($configuration->getMethodName()); - return null; + return $newNode; } public function getRuleDefinition(): RuleDefinition @@ -169,7 +80,8 @@ public function getRuleDefinition(): RuleDefinition new MethodToMethodWithCheckConfiguration( 'Drupal\Core\Session\MetadataBag', 'clearCsrfTokenSeed', - 'stampNew' + 'stampNew', + '9.2.0', ), ] ), @@ -181,7 +93,7 @@ public function getRuleDefinition(): RuleDefinition $url = $entity->toUrl(); CODE_AFTER, [ - new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityInterface', 'urlInfo', 'toUrl'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityInterface', 'urlInfo', 'toUrl', '8.0.0'), ] ), new ConfiguredCodeSample( @@ -198,7 +110,7 @@ public function getRuleDefinition(): RuleDefinition $entity_type->getSingularLabel(); CODE_AFTER, [ - new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityTypeInterface', 'getLowercaseLabel', 'getSingularLabel'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityTypeInterface', 'getLowercaseLabel', 'getSingularLabel', '8.8.0'), ] ), ]); diff --git a/src/Rector/ValueObject/ClassConstantToClassConstantConfiguration.php b/src/Rector/ValueObject/ClassConstantToClassConstantConfiguration.php index 24a3d5745..19f26569f 100644 --- a/src/Rector/ValueObject/ClassConstantToClassConstantConfiguration.php +++ b/src/Rector/ValueObject/ClassConstantToClassConstantConfiguration.php @@ -4,9 +4,10 @@ namespace DrupalRector\Rector\ValueObject; +use DrupalRector\Contract\VersionedConfigurationInterface; use Rector\Validation\RectorAssert; -final class ClassConstantToClassConstantConfiguration +final class ClassConstantToClassConstantConfiguration implements VersionedConfigurationInterface { private string $deprecated; private string $class; @@ -14,12 +15,15 @@ final class ClassConstantToClassConstantConfiguration private string $deprecatedClass; - public function __construct(string $deprecatedClass, string $deprecated, string $class, string $constant) + private string $introducedVersion; + + public function __construct(string $deprecatedClass, string $deprecated, string $class, string $constant, string $introducedVersion) { $this->deprecatedClass = $deprecatedClass; $this->deprecated = $deprecated; $this->class = $class; $this->constant = $constant; + $this->introducedVersion = $introducedVersion; RectorAssert::className($deprecatedClass); RectorAssert::className($class); @@ -46,4 +50,9 @@ public function getDeprecatedClass(): string { return $this->deprecatedClass; } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } } diff --git a/src/Rector/ValueObject/ConstantToClassConfiguration.php b/src/Rector/ValueObject/ConstantToClassConfiguration.php index 98a5c16d4..8d505d83d 100644 --- a/src/Rector/ValueObject/ConstantToClassConfiguration.php +++ b/src/Rector/ValueObject/ConstantToClassConfiguration.php @@ -4,19 +4,22 @@ namespace DrupalRector\Rector\ValueObject; +use DrupalRector\Contract\VersionedConfigurationInterface; use Rector\Validation\RectorAssert; -final class ConstantToClassConfiguration +final class ConstantToClassConfiguration implements VersionedConfigurationInterface { private string $deprecated; private string $class; private string $constant; + private string $introducedVersion; - public function __construct(string $deprecated, string $class, string $constant) + public function __construct(string $deprecated, string $class, string $constant, string $introducedVersion) { $this->deprecated = $deprecated; $this->class = $class; $this->constant = $constant; + $this->introducedVersion = $introducedVersion; RectorAssert::className($class); RectorAssert::constantName($deprecated); @@ -37,4 +40,9 @@ public function getConstant(): string { return $this->constant; } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } } diff --git a/src/Rector/ValueObject/DrupalServiceRenameConfiguration.php b/src/Rector/ValueObject/DrupalServiceRenameConfiguration.php new file mode 100644 index 000000000..790339a07 --- /dev/null +++ b/src/Rector/ValueObject/DrupalServiceRenameConfiguration.php @@ -0,0 +1,38 @@ +introducedVersion = $introducedVersion; + $this->deprecatedService = $deprecatedService; + $this->newService = $newService; + } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } + + public function getDeprecatedService(): string + { + return $this->deprecatedService; + } + + public function getNewService(): string + { + return $this->newService; + } +} diff --git a/src/Rector/ValueObject/FunctionCallRemovalConfiguration.php b/src/Rector/ValueObject/FunctionCallRemovalConfiguration.php new file mode 100644 index 000000000..ffbbd9510 --- /dev/null +++ b/src/Rector/ValueObject/FunctionCallRemovalConfiguration.php @@ -0,0 +1,17 @@ +functionName; + } +} diff --git a/src/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php b/src/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php new file mode 100644 index 000000000..8856ef993 --- /dev/null +++ b/src/Rector/ValueObject/FunctionToFirstArgMethodConfiguration.php @@ -0,0 +1,38 @@ +introducedVersion = $introducedVersion; + $this->deprecatedFunctionName = $deprecatedFunctionName; + $this->methodName = $methodName; + } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } + + public function getDeprecatedFunctionName(): string + { + return $this->deprecatedFunctionName; + } + + public function getMethodName(): string + { + return $this->methodName; + } +} diff --git a/src/Rector/ValueObject/FunctionToServiceConfiguration.php b/src/Rector/ValueObject/FunctionToServiceConfiguration.php index 737aaeb1a..9cfb6a02d 100644 --- a/src/Rector/ValueObject/FunctionToServiceConfiguration.php +++ b/src/Rector/ValueObject/FunctionToServiceConfiguration.php @@ -25,12 +25,15 @@ class FunctionToServiceConfiguration implements VersionedConfigurationInterface protected string $introducedVersion; - public function __construct(string $introducedVersion, string $deprecatedFunctionName, string $serviceName, string $serviceMethodName) + protected bool $useClassSyntax; + + public function __construct(string $introducedVersion, string $deprecatedFunctionName, string $serviceName, string $serviceMethodName, bool $useClassSyntax = false) { $this->deprecatedFunctionName = $deprecatedFunctionName; $this->serviceName = $serviceName; $this->serviceMethodName = $serviceMethodName; $this->introducedVersion = $introducedVersion; + $this->useClassSyntax = $useClassSyntax; } public function getDeprecatedFunctionName(): string @@ -52,4 +55,9 @@ public function getIntroducedVersion(): string { return $this->introducedVersion; } + + public function useClassSyntax(): bool + { + return $this->useClassSyntax; + } } diff --git a/src/Rector/ValueObject/MethodToMethodWithCheckConfiguration.php b/src/Rector/ValueObject/MethodToMethodWithCheckConfiguration.php index 4adb280d3..d5fa4b0f0 100644 --- a/src/Rector/ValueObject/MethodToMethodWithCheckConfiguration.php +++ b/src/Rector/ValueObject/MethodToMethodWithCheckConfiguration.php @@ -4,7 +4,9 @@ namespace DrupalRector\Rector\ValueObject; -class MethodToMethodWithCheckConfiguration +use DrupalRector\Contract\VersionedConfigurationInterface; + +class MethodToMethodWithCheckConfiguration implements VersionedConfigurationInterface { protected string $deprecatedMethodName; @@ -12,11 +14,14 @@ class MethodToMethodWithCheckConfiguration protected string $className; - public function __construct(string $className, string $deprecatedMethodName, string $methodName) + protected string $introducedVersion; + + public function __construct(string $className, string $deprecatedMethodName, string $methodName, string $introducedVersion) { $this->className = $className; $this->deprecatedMethodName = $deprecatedMethodName; $this->methodName = $methodName; + $this->introducedVersion = $introducedVersion; } public function getDeprecatedMethodName(): string @@ -33,4 +38,9 @@ public function getClassName(): string { return $this->className; } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } } diff --git a/src/Services/DrupalRectorSettings.php b/src/Services/DrupalRectorSettings.php new file mode 100644 index 000000000..bf6c819ed --- /dev/null +++ b/src/Services/DrupalRectorSettings.php @@ -0,0 +1,61 @@ +backwardCompatibilityEnabled = true; + + return $this; + } + + public function disableBackwardCompatibility(): static + { + $this->backwardCompatibilityEnabled = false; + + return $this; + } + + public function isBackwardCompatibilityEnabled(): bool + { + return $this->backwardCompatibilityEnabled; + } + + public function setMinimumCoreVersionSupported(string $version): static + { + if ($version === '') { + throw new \InvalidArgumentException('Minimum core version supported cannot be empty.'); + } + + $this->minimumCoreVersionSupported = $version; + + return $this; + } + + public function getMinimumCoreVersionSupported(): string + { + return $this->minimumCoreVersionSupported; + } + + public function setDrupalVersion(?string $version): static + { + $this->drupalVersion = $version; + + return $this; + } + + public function getDrupalVersion(): ?string + { + return $this->drupalVersion; + } +} diff --git a/src/Set/Drupal11SetList.php b/src/Set/Drupal11SetList.php new file mode 100644 index 000000000..d01d169b4 --- /dev/null +++ b/src/Set/Drupal11SetList.php @@ -0,0 +1,15 @@ + 'red', + 'green' => 'green', + 'blue' => 'blue', + ]; +} + + +/** + * Implements hook_user_add(). + */ +#[LegacyHook] +function hookconvertrector_theme_suggestions_form_alter(array &$attachments): void { + $red = 'red'; + $method = [ + 'red', + 'green', + 'blue', + ]; + $edit = [ + 'red' => 'red', + 'green' => 'green', + 'blue' => 'blue', + ]; +} + +/** + * Implements hook_page_attachments_alter(). + */ +function hookconvertrector_page_attachments_alter(array &$page) { + // Routes that don't use BigPipe also don't need no-JS detection. + if (\Drupal::routeMatch()->getRouteObject()->getOption('_no_big_pipe')) { + return; + } + + $request = \Drupal::request(); + // BigPipe is only used when there is an actual session, so only add the no-JS + // detection when there actually is a session. + // @see \Drupal\big_pipe\Render\Placeholder\BigPipeStrategy. + $session_exists = \Drupal::service('session_configuration')->hasSession($request); + $page['#cache']['contexts'][] = 'session.exists'; + // Only do the no-JS detection while we don't know if there's no JS support: + // avoid endless redirect loops. + $has_big_pipe_nojs_cookie = $request->cookies->has(BigPipeStrategy::NOJS_COOKIE); + $page['#cache']['contexts'][] = 'cookies:' . BigPipeStrategy::NOJS_COOKIE; + if ($session_exists) { + if (!$has_big_pipe_nojs_cookie) { + // Let server set the BigPipe no-JS cookie. + $page['#attached']['html_head'][] = [ + [ + // Redirect through a 'Refresh' meta tag if JavaScript is disabled. + '#tag' => 'meta', + '#noscript' => TRUE, + '#attributes' => [ + 'http-equiv' => 'Refresh', + 'content' => '0; URL=' . Url::fromRoute('big_pipe.nojs', [], ['query' => \Drupal::service('redirect.destination')->getAsArray()])->toString(), + ], + ], + 'big_pipe_detect_nojs', + ]; + } + else { + // Let client delete the BigPipe no-JS cookie. + $page['#attached']['html_head'][] = [ + [ + '#tag' => 'script', + '#value' => 'document.cookie = "' . BigPipeStrategy::NOJS_COOKIE . '=1; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"', + ], + 'big_pipe_detect_js', + ]; + } + } +} diff --git a/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.module b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.module index 573e647bd..5cc35874e 100644 --- a/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.module +++ b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.module @@ -36,5 +36,5 @@ function hookconvertrector_user_add($edit, UserInterface $account, $method) { #[LegacyHook] function hookconvertrector_page_attachments(array &$page) { - return \Drupal::service(HookconvertrectorHooks::class)->pageAttachments($page); + \Drupal::service(HookconvertrectorHooks::class)->pageAttachments($page); } diff --git a/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.services.yml b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.services.yml index 3be2c7590..366f49558 100644 --- a/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.services.yml +++ b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.services.yml @@ -3,3 +3,7 @@ services: Drupal\hookconvertrector\Hook\HookconvertrectorHooks: class: Drupal\hookconvertrector\Hook\HookconvertrectorHooks autowire: true + + Drupal\hookconvertrector\Hook\HookconvertrectorHooks1: + class: Drupal\hookconvertrector\Hook\HookconvertrectorHooks1 + autowire: true diff --git a/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.theme b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.theme new file mode 100644 index 000000000..5a1be5fa9 --- /dev/null +++ b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/hookconvertrector.theme @@ -0,0 +1,41 @@ +themeSuggestionsFormElementAlter($suggestions, $variables); +} + + +/** + * Implements hook_user_add(). + */ +#[LegacyHook] +function hookconvertrector_theme_suggestions_form_alter(array &$attachments): void { + $red = 'red'; + $method = [ + 'red', + 'green', + 'blue', + ]; + $edit = [ + 'red' => 'red', + 'green' => 'green', + 'blue' => 'blue', + ]; +} + +/** + * Implements hook_page_attachments_alter(). + */ +#[LegacyHook] +function hookconvertrector_page_attachments_alter(array &$page) +{ + \Drupal::service(HookconvertrectorHooks1::class)->pageAttachmentsAlter($page); +} diff --git a/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/src/Hook/HookconvertrectorHooks1.php b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/src/Hook/HookconvertrectorHooks1.php new file mode 100644 index 000000000..aa0262045 --- /dev/null +++ b/tests/functional/hookconvertrector/fixture/hookconvertrector_updated/src/Hook/HookconvertrectorHooks1.php @@ -0,0 +1,81 @@ + 'red', + 'green' => 'green', + 'blue' => 'blue', + ]; + } + + /** + * Implements hook_page_attachments_alter(). + */ + #[Hook('page_attachments_alter')] + public function pageAttachmentsAlter(array &$page) + { + // Routes that don't use BigPipe also don't need no-JS detection. + if (\Drupal::routeMatch()->getRouteObject()->getOption('_no_big_pipe')) { + return; + } + $request = \Drupal::request(); + // BigPipe is only used when there is an actual session, so only add the no-JS + // detection when there actually is a session. + // @see \Drupal\big_pipe\Render\Placeholder\BigPipeStrategy. + $session_exists = \Drupal::service('session_configuration')->hasSession($request); + $page['#cache']['contexts'][] = 'session.exists'; + // Only do the no-JS detection while we don't know if there's no JS support: + // avoid endless redirect loops. + $has_big_pipe_nojs_cookie = $request->cookies->has(\BigPipeStrategy::NOJS_COOKIE); + $page['#cache']['contexts'][] = 'cookies:' . \BigPipeStrategy::NOJS_COOKIE; + if ($session_exists) { + if (!$has_big_pipe_nojs_cookie) { + // Let server set the BigPipe no-JS cookie. + $page['#attached']['html_head'][] = [ + [ + // Redirect through a 'Refresh' meta tag if JavaScript is disabled. + '#tag' => 'meta', + '#noscript' => TRUE, + '#attributes' => [ + 'http-equiv' => 'Refresh', + 'content' => '0; URL=' . \Url::fromRoute('big_pipe.nojs', [ + ], [ + 'query' => \Drupal::service('redirect.destination')->getAsArray(), + ])->toString(), + ], + ], + 'big_pipe_detect_nojs', + ]; + } else { + // Let client delete the BigPipe no-JS cookie. + $page['#attached']['html_head'][] = [ + [ + '#tag' => 'script', + '#value' => 'document.cookie = "' . \BigPipeStrategy::NOJS_COOKIE . '=1; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"', + ], + 'big_pipe_detect_js', + ]; + } + } + } +} diff --git a/tests/src/AbstractDrupalRectorTestCase.php b/tests/src/AbstractDrupalRectorTestCase.php new file mode 100644 index 000000000..2f9a9c549 --- /dev/null +++ b/tests/src/AbstractDrupalRectorTestCase.php @@ -0,0 +1,24 @@ +make(DrupalRectorSettings::class) + ->setDrupalVersion(null) + ->enableBackwardCompatibility() + ->setMinimumCoreVersionSupported('10.1.0'); + + parent::tearDown(); + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php index f29f94cff..037c3ef68 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/ActionAnnotationToAttributeRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class ActionAnnotationToAttributeRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class ActionAnnotationToAttributeRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php index 194f05a8a..8ed8483a2 100644 --- a/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php +++ b/tests/src/Drupal10/Rector/Deprecation/ActionAnnotationToAttributeRector/BackwardsCompatibilityActionAnnotationToAttributeRectorTest.php @@ -4,21 +4,18 @@ namespace DrupalRector\Tests\Drupal10\Rector\Deprecation\ActionAnnotationToAttributeRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; class Drupal { public const VERSION = '11.0.x-dev'; } -class BackwardsCompatibilityActionAnnotationToAttributeRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class BackwardsCompatibilityActionAnnotationToAttributeRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/ReplaceModuleHandlerGetNameRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/ReplaceModuleHandlerGetNameRectorTest.php new file mode 100644 index 000000000..822588999 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/ReplaceModuleHandlerGetNameRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/config/configured_rule.php new file mode 100644 index 000000000..9f4530a88 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getName('mymodule'); +?> +----- +getName('mymodule'); +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/basic.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/basic.php.inc new file mode 100644 index 000000000..16f179c96 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/basic.php.inc @@ -0,0 +1,11 @@ +getName('mymodule'); +?> +----- + \Drupal::service('extension.list.module')->getName('mymodule'), fn() => $module_handler->getName('mymodule')); +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/class_property.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/class_property.php.inc new file mode 100644 index 000000000..4a9dc5349 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/class_property.php.inc @@ -0,0 +1,29 @@ +moduleHandler->getName($module); + } +} +?> +----- + \Drupal::service('extension.list.module')->getName($module), fn() => $this->moduleHandler->getName($module)); + } +} +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..d1cc0d0ad --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceModuleHandlerGetNameRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,12 @@ +getName('mymodule'); diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/ReplaceRebuildThemeDataRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/ReplaceRebuildThemeDataRectorTest.php new file mode 100644 index 000000000..341bf92be --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/ReplaceRebuildThemeDataRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/config/configured_rule.php new file mode 100644 index 000000000..e861c7f4a --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/config/configured_rule.php @@ -0,0 +1,14 @@ +rebuildThemeData(); +?> +----- +rebuildThemeData(); +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/basic.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/basic.php.inc new file mode 100644 index 000000000..a6e42af7a --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/basic.php.inc @@ -0,0 +1,11 @@ +rebuildThemeData(); +?> +----- + \Drupal::service('extension.list.theme')->reset()->getList(), fn() => $theme_handler->rebuildThemeData()); +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/class_property.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/class_property.php.inc new file mode 100644 index 000000000..d752aaa46 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/class_property.php.inc @@ -0,0 +1,29 @@ +themeHandler->rebuildThemeData(); + } +} +?> +----- + \Drupal::service('extension.list.theme')->reset()->getList(), fn() => $this->themeHandler->rebuildThemeData()); + } +} +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_concrete_class.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_concrete_class.php.inc new file mode 100644 index 000000000..ce104de10 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_concrete_class.php.inc @@ -0,0 +1,21 @@ +themeHandler = $themeHandler; + } + + public function rebuild(): array { + return $this->themeHandler->rebuildThemeData(); + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_untyped.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_untyped.php.inc new file mode 100644 index 000000000..ebd01cfda --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_untyped.php.inc @@ -0,0 +1,4 @@ +rebuildThemeData(); diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_with_args.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_with_args.php.inc new file mode 100644 index 000000000..fbd5fa843 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRebuildThemeDataRector/fixture/no_change_with_args.php.inc @@ -0,0 +1,5 @@ +args), must not change. +/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */ +$themes = $theme_handler->rebuildThemeData($extra); diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/ReplaceRequestTimeConstantRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/ReplaceRequestTimeConstantRectorTest.php new file mode 100644 index 000000000..a154da2df --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/ReplaceRequestTimeConstantRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/config/configured_rule.php new file mode 100644 index 000000000..47f93fc4c --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture-below-version/in_function_call.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture-below-version/in_function_call.php.inc new file mode 100644 index 000000000..0b7ecee32 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture-below-version/in_function_call.php.inc @@ -0,0 +1,25 @@ + REQUEST_TIME]; + +// REQUEST_TIME in string concatenation (still a ConstFetch, so it IS transformed). +$label = 'Timestamp: ' . REQUEST_TIME; + +?> +----- + REQUEST_TIME]; + +// REQUEST_TIME in string concatenation (still a ConstFetch, so it IS transformed). +$label = 'Timestamp: ' . REQUEST_TIME; + +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/basic.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/basic.php.inc new file mode 100644 index 000000000..ca2b0053b --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/basic.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::time()->getRequestTime(), fn() => REQUEST_TIME) - $lifespan; +$other = time(); + +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/in_function_call.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/in_function_call.php.inc new file mode 100644 index 000000000..dcbe96c31 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/in_function_call.php.inc @@ -0,0 +1,25 @@ + REQUEST_TIME]; + +// REQUEST_TIME in string concatenation (still a ConstFetch, so it IS transformed). +$label = 'Timestamp: ' . REQUEST_TIME; + +?> +----- + \Drupal::time()->getRequestTime(), fn() => REQUEST_TIME)); + +// REQUEST_TIME used as an array value. +$data = ['cutoff' => \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.0.0', fn() => \Drupal::time()->getRequestTime(), fn() => REQUEST_TIME)]; + +// REQUEST_TIME in string concatenation (still a ConstFetch, so it IS transformed). +$label = 'Timestamp: ' . \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.0.0', fn() => \Drupal::time()->getRequestTime(), fn() => REQUEST_TIME); + +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/no_change_string_literal.php.inc b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/no_change_string_literal.php.inc new file mode 100644 index 000000000..8d56937d9 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/ReplaceRequestTimeConstantRector/fixture/no_change_string_literal.php.inc @@ -0,0 +1,8 @@ + diff --git a/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/SystemTimeZonesRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/SystemTimeZonesRectorTest.php index 04a0f300c..af2892fea 100644 --- a/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/SystemTimeZonesRectorTest.php +++ b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/SystemTimeZonesRectorTest.php @@ -4,18 +4,17 @@ namespace DrupalRector\Tests\Drupal10\Rector\Deprecation\SystemTimeZonesRector; +use DrupalRector\Services\DrupalRectorSettings; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class SystemTimeZonesRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class SystemTimeZonesRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ - public function test(string $filePath): void + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] + public function testAboveVersion(string $filePath): void { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); $this->doTestFile($filePath); } @@ -27,6 +26,21 @@ public static function provideData(): \Iterator return self::yieldFilesFromDirectory(__DIR__.'/fixture'); } + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + public function provideConfigFilePath(): string { // must be implemented diff --git a/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture-below-version/system_time_zones.php.inc b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture-below-version/system_time_zones.php.inc new file mode 100644 index 000000000..0825c7b22 --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture-below-version/system_time_zones.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones.php.inc b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones.php.inc index ca8669f41..60a1fdd9f 100644 --- a/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones.php.inc @@ -15,12 +15,12 @@ function simple_example() { \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(), fn() => system_time_zones()); + \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(); - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsListByRegion(), fn() => system_time_zones(FALSE, TRUE)); + \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsListByRegion(); - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(NULL), fn() => system_time_zones(NULL, FALSE)); + \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(NULL); - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(TRUE), fn() => system_time_zones(TRUE, FALSE)); + \Drupal\Core\Datetime\TimeZoneFormHelper::getOptionsList(TRUE); } ?> diff --git a/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones_dynamic_grouped.php.inc b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones_dynamic_grouped.php.inc new file mode 100644 index 000000000..35e4ebb5a --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/SystemTimeZonesRector/fixture/system_time_zones_dynamic_grouped.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/config/configured_rule.php deleted file mode 100644 index 24b63efdb..000000000 --- a/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/config/configured_rule.php +++ /dev/null @@ -1,14 +0,0 @@ -make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); $this->doTestFile($filePath); } @@ -27,6 +26,21 @@ public static function provideData(): \Iterator return self::yieldFilesFromDirectory(__DIR__.'/fixture'); } + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + public function provideConfigFilePath(): string { // must be implemented diff --git a/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture-below-version/watchdog_exception.php.inc b/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture-below-version/watchdog_exception.php.inc new file mode 100644 index 000000000..032e9d22f --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture-below-version/watchdog_exception.php.inc @@ -0,0 +1,35 @@ + 'bar'], RfcLogLevel::CRITICAL, 'http://example.com'); + + watchdog_exception('update', $exception, 'My custom message @foo', ['@foo' => 'bar']); + + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception), fn() => watchdog_exception('update', $exception)); +} +?> +----- + 'bar'], RfcLogLevel::CRITICAL, 'http://example.com'); + + watchdog_exception('update', $exception, 'My custom message @foo', ['@foo' => 'bar']); + + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception), fn() => watchdog_exception('update', $exception)); +} +?> diff --git a/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture/watchdog_exception.php.inc b/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture/watchdog_exception.php.inc index b3219444c..2231af2ad 100644 --- a/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture/watchdog_exception.php.inc +++ b/tests/src/Drupal10/Rector/Deprecation/WatchdogExceptionRector/fixture/watchdog_exception.php.inc @@ -19,16 +19,16 @@ function advanced() { \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception), fn() => watchdog_exception('update', $exception)); + \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception); } /** * A simple example. */ function advanced() { - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception, 'My custom message @foo', ['@foo' => 'bar', 'link' => 'http://example.com'], RfcLogLevel::CRITICAL), fn() => watchdog_exception('update', $exception, 'My custom message @foo', ['@foo' => 'bar', 'link' => 'http://example.com'], RfcLogLevel::CRITICAL, 'http://example.com')); + \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception, 'My custom message @foo', ['@foo' => 'bar', 'link' => 'http://example.com'], RfcLogLevel::CRITICAL); - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception, 'My custom message @foo', ['@foo' => 'bar']), fn() => watchdog_exception('update', $exception, 'My custom message @foo', ['@foo' => 'bar'])); + \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception, 'My custom message @foo', ['@foo' => 'bar']); \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Utility\Error::logException(\Drupal::logger('update'), $exception), fn() => watchdog_exception('update', $exception)); } diff --git a/tests/src/Drupal11/Rector/Deprecation/.gitkeep b/tests/src/Drupal11/Rector/Deprecation/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/BlockContentTestBaseStringToArrayRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/BlockContentTestBaseStringToArrayRectorTest.php new file mode 100644 index 000000000..a0d0c49f0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/BlockContentTestBaseStringToArrayRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/config/configured_rule.php new file mode 100644 index 000000000..8037813da --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/config/configured_rule.php @@ -0,0 +1,11 @@ +createBlockContentType('basic', TRUE); +$testBase->createBlockContentType('basic'); +?> +----- +createBlockContentType(['id' => 'basic'], TRUE); +$testBase->createBlockContentType(['id' => 'basic']); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..d6ad0d5ca --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/BlockContentTestBaseStringToArrayRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,21 @@ +createBlockContentType('basic', TRUE); + +// Two string args — InlineBlockTestBase pattern, must not change. +/** @var \Drupal\Tests\block_content\Traits\BlockContentCreationTrait $testBase */ +$testBase->createBlockContentType('basic', 'Basic block'); +?> +----- +createBlockContentType('basic', TRUE); + +// Two string args — InlineBlockTestBase pattern, must not change. +/** @var \Drupal\Tests\block_content\Traits\BlockContentCreationTrait $testBase */ +$testBase->createBlockContentType('basic', 'Basic block'); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/CheckMarkupToProcessedTextRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/CheckMarkupToProcessedTextRectorTest.php new file mode 100644 index 000000000..7d408f572 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/CheckMarkupToProcessedTextRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/config/configured_rule.php new file mode 100644 index 000000000..69f712c9a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/config/configured_rule.php @@ -0,0 +1,11 @@ + +----- + 'processed_text', '#text' => $text, '#format' => $format_id]; + $full = ['#type' => 'processed_text', '#text' => $text, '#format' => $format_id, '#langcode' => $langcode, '#filter_types_to_skip' => $filter_types_to_skip]; + $named = ['#type' => 'processed_text', '#text' => $text, '#format' => $format_id]; +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..fc1832e15 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/CheckMarkupToProcessedTextRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/DeprecatedFilterFunctionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/DeprecatedFilterFunctionsRectorTest.php new file mode 100644 index 000000000..875a2979a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/DeprecatedFilterFunctionsRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/config/configured_rule.php new file mode 100644 index 000000000..db9b87a6e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..387d7484e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/DeprecatedFilterFunctionsRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('plugin.manager.filter')->createInstance('filter_autop')->process($text, \Drupal::languageManager()->getCurrentLanguage()->getId())->getProcessedText(), fn() => _filter_autop($text)); +$result2 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('plugin.manager.filter')->createInstance('filter_html_escape')->process($text, \Drupal::languageManager()->getCurrentLanguage()->getId())->getProcessedText(), fn() => _filter_html_escape($text)); +$result3 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('plugin.manager.filter')->createInstance('filter_html_image_secure')->process($text, \Drupal::languageManager()->getCurrentLanguage()->getId())->getProcessedText(), fn() => _filter_html_image_secure_process($text, $filter)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/ErrorCurrentErrorHandlerRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/ErrorCurrentErrorHandlerRectorTest.php new file mode 100644 index 000000000..21dfd3c0f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/ErrorCurrentErrorHandlerRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/config/configured_rule.php new file mode 100644 index 000000000..b34b77ab0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/fixture/as_argument.php.inc b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/fixture/as_argument.php.inc new file mode 100644 index 000000000..578bbdc0b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/fixture/as_argument.php.inc @@ -0,0 +1,27 @@ + +----- + get_error_handler(), fn() => Error::currentErrorHandler())); + +// Result used in a boolean expression. +if (\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => get_error_handler(), fn() => Error::currentErrorHandler()) !== null) { + // do something +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/fixture/basic.php.inc new file mode 100644 index 000000000..c642204fd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ErrorCurrentErrorHandlerRector/fixture/basic.php.inc @@ -0,0 +1,13 @@ + +----- + get_error_handler(), fn() => \Drupal\Core\Utility\Error::currentErrorHandler()); +$other = OtherClass::currentErrorHandler(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/FileManagedFileSubmitRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/FileManagedFileSubmitRectorTest.php new file mode 100644 index 000000000..bb0491945 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/FileManagedFileSubmitRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/config/configured_rule.php new file mode 100644 index 000000000..d1f7225e5 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/fixture/basic.php.inc new file mode 100644 index 000000000..319bcdd8d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileManagedFileSubmitRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ + +----- + [\Drupal\file\Element\ManagedFile::class, 'submit'], fn() => 'file_managed_file_submit')]; + $form['actions']['#submit'][] = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => [\Drupal\file\Element\ManagedFile::class, 'submit'], fn() => 'file_managed_file_submit'); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/FileSystemBasenameToNativeRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/FileSystemBasenameToNativeRectorTest.php new file mode 100644 index 000000000..180580eb5 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/FileSystemBasenameToNativeRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/config/configured_rule.php new file mode 100644 index 000000000..1b8629d1c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/config/configured_rule.php @@ -0,0 +1,14 @@ +basename($uri, '.txt'); +$noop = $untyped->basename($uri, '.txt'); + +?> +----- +basename($uri, '.txt'); +$noop = $untyped->basename($uri, '.txt'); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/basic.php.inc new file mode 100644 index 000000000..2caf4bb58 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ +basename($uri, '.txt'); +$noop = $untyped->basename($uri, '.txt'); + +?> +----- + basename($uri, '.txt'), fn() => $fileSystem->basename($uri, '.txt')); +$noop = $untyped->basename($uri, '.txt'); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/concrete_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/concrete_class.php.inc new file mode 100644 index 000000000..c8af2d22a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/concrete_class.php.inc @@ -0,0 +1,13 @@ +basename($uri, '.txt'); + +?> +----- + basename($uri, '.txt'), fn() => $fileSystem->basename($uri, '.txt')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/no_suffix.php.inc b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/no_suffix.php.inc new file mode 100644 index 000000000..5797fabcd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FileSystemBasenameToNativeRector/fixture/no_suffix.php.inc @@ -0,0 +1,13 @@ +basename($uri); + +?> +----- + basename($uri), fn() => $fileSystem->basename($uri)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/FilterFormatFunctionsToServiceRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/FilterFormatFunctionsToServiceRectorTest.php new file mode 100644 index 000000000..fd2d759cb --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/FilterFormatFunctionsToServiceRectorTest.php @@ -0,0 +1,42 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/config/configured_rule.php new file mode 100644 index 000000000..5969602e3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/fixture/basic.php.inc new file mode 100644 index 000000000..509b4bbba --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/FilterFormatFunctionsToServiceRector/fixture/basic.php.inc @@ -0,0 +1,21 @@ + +----- + \Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getFallbackFormatId(), fn() => filter_fallback_format()); +$all = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getAllFormats(), fn() => filter_formats()); +$for_account = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getFormatsForAccount($account), fn() => filter_formats($account)); +$roles = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $format->getRoles(), fn() => filter_get_roles_by_format($format)); +$formats = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getFormatsByRole($rid), fn() => filter_get_formats_by_role($rid)); +$default_id = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getDefaultFormat()->id(), fn() => filter_default_format()); +$default_id_account = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\filter\FilterFormatRepositoryInterface::class)->getDefaultFormat($account)->id(), fn() => filter_default_format($account)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/GetNameToNameRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/GetNameToNameRectorTest.php new file mode 100644 index 000000000..09862c60b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/GetNameToNameRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/config/configured_rule.php new file mode 100644 index 000000000..579f5262a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/config/configured_rule.php @@ -0,0 +1,11 @@ +getName(); + } + + public function testGetNameWithFalse(): void + { + $name = $this->getName(false); + } +} +?> +----- +name(); + } + + public function testGetNameWithFalse(): void + { + $name = $this->name(); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..8e9525127 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetNameToNameRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,31 @@ +getName(); + } +} +?> +----- +getName(); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/GetOriginalClassToGetDecoratedClassesRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/GetOriginalClassToGetDecoratedClassesRectorTest.php new file mode 100644 index 000000000..f35f54c8e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/GetOriginalClassToGetDecoratedClassesRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/config/configured_rule.php new file mode 100644 index 000000000..b1b4fe348 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getOriginalClass(); +?> +----- +getOriginalClass(); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/fixture/basic.php.inc new file mode 100644 index 000000000..bf891c8ce --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/fixture/basic.php.inc @@ -0,0 +1,11 @@ +getOriginalClass(); +?> +----- + $entityType->getDecoratedClasses()[0], fn() => $entityType->getOriginalClass()); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..a0c0fb5a7 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/GetOriginalClassToGetDecoratedClassesRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,11 @@ +getOriginalClass(); +?> +----- +getOriginalClass(); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/LoadAllIncludesRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/LoadAllIncludesRectorTest.php new file mode 100644 index 000000000..49416e1b3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/LoadAllIncludesRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/config/configured_rule.php new file mode 100644 index 000000000..39cd8152e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/config/configured_rule.php @@ -0,0 +1,11 @@ +loadAllIncludes('install'); +$moduleHandler->loadAllIncludes('inc', 'admin'); +?> +----- +getModuleList() as $module => $filename) { + $moduleHandler->loadInclude($module, 'install'); +} +foreach ($moduleHandler->getModuleList() as $module => $filename) { + $moduleHandler->loadInclude($module, 'inc', 'admin'); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/class_property.php.inc b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/class_property.php.inc new file mode 100644 index 000000000..40e5dddbd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/class_property.php.inc @@ -0,0 +1,35 @@ +moduleHandler->loadAllIncludes('install'); + $this->moduleHandler->loadAllIncludes('inc', 'admin'); + } +} +?> +----- +moduleHandler->getModuleList() as $module => $filename) { + $this->moduleHandler->loadInclude($module, 'install'); + } + foreach ($this->moduleHandler->getModuleList() as $module => $filename) { + $this->moduleHandler->loadInclude($module, 'inc', 'admin'); + } + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/drupal_module_handler.php.inc b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/drupal_module_handler.php.inc new file mode 100644 index 000000000..03038256d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/drupal_module_handler.php.inc @@ -0,0 +1,14 @@ +loadAllIncludes('install'); + +?> +----- +getModuleList() as $module => $filename) { + \Drupal::moduleHandler()->loadInclude($module, 'install'); +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/no_change_class_property_untyped.php.inc b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/no_change_class_property_untyped.php.inc new file mode 100644 index 000000000..764a22b7d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/no_change_class_property_untyped.php.inc @@ -0,0 +1,14 @@ +moduleHandler = $moduleHandler; + } + + public function loadAll(): void { + $this->moduleHandler->loadAllIncludes('install'); + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/no_change_untyped.php.inc b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/no_change_untyped.php.inc new file mode 100644 index 000000000..8335985c4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/no_change_untyped.php.inc @@ -0,0 +1,4 @@ +loadAllIncludes('install'); diff --git a/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/traditional_constructor.php.inc b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/traditional_constructor.php.inc new file mode 100644 index 000000000..d6e8fea5d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LoadAllIncludesRector/fixture/traditional_constructor.php.inc @@ -0,0 +1,41 @@ +moduleHandler = $moduleHandler; + } + + public function installAll(): void { + $this->moduleHandler->loadAllIncludes('install'); + } +} +?> +----- +moduleHandler = $moduleHandler; + } + + public function installAll(): void { + foreach ($this->moduleHandler->getModuleList() as $module => $filename) { + $this->moduleHandler->loadInclude($module, 'install'); + } + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/LocaleCompareIncToServiceRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/LocaleCompareIncToServiceRectorTest.php new file mode 100644 index 000000000..15a308ef0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/LocaleCompareIncToServiceRectorTest.php @@ -0,0 +1,42 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/config/configured_rule.php new file mode 100644 index 000000000..ca706b8a2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/fixture/basic.php.inc new file mode 100644 index 000000000..45cefcaa5 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/LocaleCompareIncToServiceRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ + +----- + \Drupal::service(\Drupal\locale\LocaleProjectRepository::class)->deleteAll(), fn() => locale_translation_flush_projects()); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\locale\LocaleProjectRepository::class)->buildProjects(), fn() => locale_translation_build_projects()); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\locale\LocaleProjectChecker::class)->checkProjects(array_keys(\Drupal::service(\Drupal\locale\LocaleProjectRepository::class)->getAll())), fn() => locale_translation_check_projects()); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\locale\LocaleProjectChecker::class)->checkProjects(['drupal'], ['de']), fn() => locale_translation_check_projects(['drupal'], ['de'])); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\locale\LocaleProjectChecker::class)->checkLocalProjects(['drupal'], ['de']), fn() => locale_translation_check_projects_local(['drupal'], ['de'])); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/MediaFilterFormatEditFormValidateRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/MediaFilterFormatEditFormValidateRectorTest.php new file mode 100644 index 000000000..2c834c870 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/MediaFilterFormatEditFormValidateRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/config/configured_rule.php new file mode 100644 index 000000000..009413150 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/fixture/basic.php.inc new file mode 100644 index 000000000..6f2882a20 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MediaFilterFormatEditFormValidateRector/fixture/basic.php.inc @@ -0,0 +1,11 @@ + +----- + \Drupal::service(\Drupal\media\Hook\MediaHooks::class)->formatEditFormValidate($form, $form_state), fn() => media_filter_format_edit_form_validate($form, $form_state)); +$form['#validate'][] = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => [\Drupal\media\Hook\MediaHooks::class, 'formatEditFormValidate'], fn() => 'media_filter_format_edit_form_validate'); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/MigrateSqlGetMigrationPluginManagerRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/MigrateSqlGetMigrationPluginManagerRectorTest.php new file mode 100644 index 000000000..199500249 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/MigrateSqlGetMigrationPluginManagerRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/config/configured_rule.php new file mode 100644 index 000000000..d53538a49 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getMigrationPluginManager(); + $other = $this->someOtherMethod(); + } +} + +?> +----- +getMigrationPluginManager(); + $other = $this->someOtherMethod(); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture-below-version/chain_result.php.inc b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture-below-version/chain_result.php.inc new file mode 100644 index 000000000..ba9ff1226 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture-below-version/chain_result.php.inc @@ -0,0 +1,27 @@ +getMigrationPluginManager()->createInstances($requirements); + } +} + +?> +----- +getMigrationPluginManager()->createInstances($requirements); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture-below-version/parent_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture-below-version/parent_call.php.inc new file mode 100644 index 000000000..13860a87a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture-below-version/parent_call.php.inc @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/basic.php.inc new file mode 100644 index 000000000..0c5514d11 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/basic.php.inc @@ -0,0 +1,29 @@ +getMigrationPluginManager(); + $other = $this->someOtherMethod(); + } +} + +?> +----- + $this->migrationPluginManager, fn() => $this->getMigrationPluginManager()); + $other = $this->someOtherMethod(); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/chain_result.php.inc b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/chain_result.php.inc new file mode 100644 index 000000000..fbc66d074 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/chain_result.php.inc @@ -0,0 +1,27 @@ +getMigrationPluginManager()->createInstances($requirements); + } +} + +?> +----- + $this->migrationPluginManager, fn() => $this->getMigrationPluginManager())->createInstances($requirements); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..bd5bc6d17 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,10 @@ +getMigrationPluginManager(); + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/parent_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/parent_call.php.inc new file mode 100644 index 000000000..6f5a77696 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MigrateSqlGetMigrationPluginManagerRector/fixture/parent_call.php.inc @@ -0,0 +1,29 @@ + +----- + $this->migrationPluginManager, fn() => parent::getMigrationPluginManager()); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/MovePointerToMouseOverRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/MovePointerToMouseOverRectorTest.php new file mode 100644 index 000000000..1f195fbd4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/MovePointerToMouseOverRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/config/configured_rule.php new file mode 100644 index 000000000..3e72c7882 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/config/configured_rule.php @@ -0,0 +1,11 @@ +movePointerTo('#my-element'); +$test->movePointerTo('#another-id'); +?> +----- +getSession()->getDriver()->mouseOver('.//*[@id="my-element"]'); +$test->getSession()->getDriver()->mouseOver('.//*[@id="another-id"]'); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..8241e6974 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,29 @@ +movePointerTo('#my-element'); + +// Non-ID CSS selector — cannot be auto-converted to XPath. +/** @var \Drupal\Tests\layout_builder\FunctionalJavascript\LayoutBuilderDisableInteractionsTest $test */ +$test->movePointerTo('.some-class'); + +// Dynamic/variable argument — cannot be statically converted. +/** @var \Drupal\Tests\layout_builder\FunctionalJavascript\LayoutBuilderDisableInteractionsTest $test */ +$test->movePointerTo($selector); +?> +----- +movePointerTo('#my-element'); + +// Non-ID CSS selector — cannot be auto-converted to XPath. +/** @var \Drupal\Tests\layout_builder\FunctionalJavascript\LayoutBuilderDisableInteractionsTest $test */ +$test->movePointerTo('.some-class'); + +// Dynamic/variable argument — cannot be statically converted. +/** @var \Drupal\Tests\layout_builder\FunctionalJavascript\LayoutBuilderDisableInteractionsTest $test */ +$test->movePointerTo($selector); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/fixture/subclass.php.inc b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/fixture/subclass.php.inc new file mode 100644 index 000000000..0e4ea50cc --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/MovePointerToMouseOverRector/fixture/subclass.php.inc @@ -0,0 +1,21 @@ +movePointerTo('#my-element'); + } +} +?> +----- +getSession()->getDriver()->mouseOver('.//*[@id="my-element"]'); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/NodeAccessRebuildFunctionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/NodeAccessRebuildFunctionsRectorTest.php new file mode 100644 index 000000000..3aa98ebfd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/NodeAccessRebuildFunctionsRectorTest.php @@ -0,0 +1,42 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/config/configured_rule.php new file mode 100644 index 000000000..fb77332da --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..a9206d1b0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeAccessRebuildFunctionsRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ + +----- + \Drupal::service(\Drupal\node\NodeAccessRebuild::class)->rebuild(), fn() => node_access_rebuild()); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\node\NodeAccessRebuild::class)->rebuild(TRUE), fn() => node_access_rebuild(TRUE)); +$needs = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\node\NodeAccessRebuild::class)->needsRebuild(), fn() => node_access_needs_rebuild()); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\node\NodeAccessRebuild::class)->setNeedsRebuild(TRUE), fn() => node_access_needs_rebuild(TRUE)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\node\NodeAccessRebuild::class)->setNeedsRebuild(FALSE), fn() => node_access_needs_rebuild(FALSE)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/NodeStorageDeprecatedMethodsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/NodeStorageDeprecatedMethodsRectorTest.php new file mode 100644 index 000000000..3f908f110 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/NodeStorageDeprecatedMethodsRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/config/configured_rule.php new file mode 100644 index 000000000..6caedd687 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/config/configured_rule.php @@ -0,0 +1,11 @@ +revisionIds($node); +$revisions = $storage->userRevisionIds($account); +?> +----- +getQuery()->allRevisions()->condition('nid', $node->id())->accessCheck(FALSE)->execute()); +$revisions = array_keys($storage->getQuery()->allRevisions()->accessCheck(FALSE)->condition('uid', $account->id())->execute()); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/fixture/count_default_language_revisions.php.inc b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/fixture/count_default_language_revisions.php.inc new file mode 100644 index 000000000..5a641d3ce --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/fixture/count_default_language_revisions.php.inc @@ -0,0 +1,14 @@ +revisionIds($node); +$storage->countDefaultLanguageRevisions($node); + +?> +----- +getQuery()->allRevisions()->condition('nid', $node->id())->accessCheck(FALSE)->execute()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/fixture/foreach_usage.php.inc b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/fixture/foreach_usage.php.inc new file mode 100644 index 000000000..7fe1cdc0b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/NodeStorageDeprecatedMethodsRector/fixture/foreach_usage.php.inc @@ -0,0 +1,17 @@ +revisionIds($node) as $vid) { + // process revision +} + +?> +----- +getQuery()->allRevisions()->condition('nid', $node->id())->accessCheck(FALSE)->execute()) as $vid) { + // process revision +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/PluginBaseIsConfigurableRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/PluginBaseIsConfigurableRectorTest.php new file mode 100644 index 000000000..38d7b8560 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/PluginBaseIsConfigurableRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/config/configured_rule.php new file mode 100644 index 000000000..e844d7b22 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/config/configured_rule.php @@ -0,0 +1,14 @@ +isConfigurable()) { + // handle configurable + } + } +} +?> +----- +isConfigurable()) { + // handle configurable + } + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture-below-version/negated.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture-below-version/negated.php.inc new file mode 100644 index 000000000..fe64b0cdc --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture-below-version/negated.php.inc @@ -0,0 +1,27 @@ +isConfigurable()) { + return; + } + return $this->isConfigurable(); + } +} +?> +----- +isConfigurable()) { + return; + } + return $this->isConfigurable(); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture-below-version/property_delegation.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture-below-version/property_delegation.php.inc new file mode 100644 index 000000000..df1400117 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture-below-version/property_delegation.php.inc @@ -0,0 +1,35 @@ +plugin (not $this), but since $this->plugin +// is typed as PluginBase the rector must still replace the deprecated call. +class PluginWrapper extends CachePluginBase { + /** @var \Drupal\Component\Plugin\PluginBase */ + protected $plugin; + + public function isConfigurable(): bool { + return $this->plugin->isConfigurable(); + } +} +?> +----- +plugin (not $this), but since $this->plugin +// is typed as PluginBase the rector must still replace the deprecated call. +class PluginWrapper extends CachePluginBase { + /** @var \Drupal\Component\Plugin\PluginBase */ + protected $plugin; + + public function isConfigurable(): bool { + return $this->plugin->isConfigurable(); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/basic.php.inc new file mode 100644 index 000000000..b9cba38e8 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/basic.php.inc @@ -0,0 +1,25 @@ +isConfigurable()) { + // handle configurable + } + } +} +?> +----- + $this instanceof \Drupal\Component\Plugin\ConfigurableInterface, fn() => $this->isConfigurable())) { + // handle configurable + } + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/negated.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/negated.php.inc new file mode 100644 index 000000000..7af6f3e8e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/negated.php.inc @@ -0,0 +1,27 @@ +isConfigurable()) { + return; + } + return $this->isConfigurable(); + } +} +?> +----- + $this instanceof \Drupal\Component\Plugin\ConfigurableInterface, fn() => $this->isConfigurable())) { + return; + } + return \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => $this instanceof \Drupal\Component\Plugin\ConfigurableInterface, fn() => $this->isConfigurable()); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/no_change_other_variable.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/no_change_other_variable.php.inc new file mode 100644 index 000000000..768598c7f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/no_change_other_variable.php.inc @@ -0,0 +1,5 @@ +isConfigurable(); diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..668288a09 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,10 @@ +isConfigurable() must NOT be changed. +class UnrelatedPlugin { + public function doSomething(): void { + if ($this->isConfigurable()) { + // handle configurable + } + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/property_delegation.php.inc b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/property_delegation.php.inc new file mode 100644 index 000000000..f398b6af0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/PluginBaseIsConfigurableRector/fixture/property_delegation.php.inc @@ -0,0 +1,35 @@ +plugin (not $this), but since $this->plugin +// is typed as PluginBase the rector must still replace the deprecated call. +class PluginWrapper extends CachePluginBase { + /** @var \Drupal\Component\Plugin\PluginBase */ + protected $plugin; + + public function isConfigurable(): bool { + return $this->plugin->isConfigurable(); + } +} +?> +----- +plugin (not $this), but since $this->plugin +// is typed as PluginBase the rector must still replace the deprecated call. +class PluginWrapper extends CachePluginBase { + /** @var \Drupal\Component\Plugin\PluginBase */ + protected $plugin; + + public function isConfigurable(): bool { + return \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => $this->plugin instanceof \Drupal\Component\Plugin\ConfigurableInterface, fn() => $this->plugin->isConfigurable()); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/RemoveAutomatedCronSubmitHandlerRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/RemoveAutomatedCronSubmitHandlerRectorTest.php new file mode 100644 index 000000000..4cc0431a2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/RemoveAutomatedCronSubmitHandlerRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/config/configured_rule.php new file mode 100644 index 000000000..a907dc9cc --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(RemoveAutomatedCronSubmitHandlerRector::class); +}; diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/fixture/basic.php.inc new file mode 100644 index 000000000..f7de24077 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/fixture/basic.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/fixture/no_change_explicit_index.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/fixture/no_change_explicit_index.php.inc new file mode 100644 index 000000000..40d116a07 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveAutomatedCronSubmitHandlerRector/fixture/no_change_explicit_index.php.inc @@ -0,0 +1,4 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/config/configured_rule.php new file mode 100644 index 000000000..662385485 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/config/configured_rule.php @@ -0,0 +1,11 @@ +getRequestTime() - $this->options['lifespan']; + } + + protected function cacheSetMaxAge($type) + { + return $this->options['lifespan'] ?: \Drupal\Core\Cache\Cache::PERMANENT; + } +} + +class UnrelatedClass +{ + protected function cacheExpire($type) + { + return 0; + } +} + +?> +----- +options['lifespan'] ?: \Drupal\Core\Cache\Cache::PERMANENT; + } +} + +class UnrelatedClass +{ + protected function cacheExpire($type) + { + return 0; + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_fqcn.php.inc new file mode 100644 index 000000000..16e772f4a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_fqcn.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_none.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_none.php.inc new file mode 100644 index 000000000..deeec5d4a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_none.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_tag.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_tag.php.inc new file mode 100644 index 000000000..be0370195 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_tag.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_time.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_time.php.inc new file mode 100644 index 000000000..7fa252a6e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/extends_time.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/namespace_alias.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/namespace_alias.php.inc new file mode 100644 index 000000000..5ecc77ce9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/namespace_alias.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..6eb372826 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheExpireOverrideRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,10 @@ +notice('cacheExpire called'); + $this->options['last_expire'] = time(); + return \Drupal::time()->getRequestTime() - $this->options['lifespan']; + } + + protected function cacheSetMaxAge($type) + { + return $this->options['lifespan']; + } +} +?> +----- +options['lifespan']; + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/RemoveCacheTagChecksumAssertionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/RemoveCacheTagChecksumAssertionsRectorTest.php new file mode 100644 index 000000000..f6fda0f08 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/RemoveCacheTagChecksumAssertionsRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/config/configured_rule.php new file mode 100644 index 000000000..112c33c79 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/config/configured_rule.php @@ -0,0 +1,11 @@ +assertMetrics([ + 'CacheGetCount' => 5, + 'CacheTagChecksumCount' => 38, + 'CacheTagIsValidCount' => 43, + 'CacheTagInvalidationCount' => 0, +], $performance_data); +?> +----- +assertMetrics([ + 'CacheGetCount' => 5, + 'CacheTagInvalidationCount' => 0, +], $performance_data); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..3919e4a1e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveCacheTagChecksumAssertionsRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,9 @@ +assertMetrics([ + 'CacheGetCount' => 5, + 'CacheTagChecksumCount' => 38, + 'CacheTagIsValidCount' => 43, + 'CacheTagInvalidationCount' => 0, +], $performance_data); diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/RemoveConfigSaveTrustedDataArgRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/RemoveConfigSaveTrustedDataArgRectorTest.php new file mode 100644 index 000000000..c101fc642 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/RemoveConfigSaveTrustedDataArgRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/config/configured_rule.php new file mode 100644 index 000000000..c3554fc99 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/config/configured_rule.php @@ -0,0 +1,14 @@ +save(TRUE); +$config->save(FALSE); +$config->save(); + +// Untyped — must not be changed. +$other->save(TRUE); + +?> +----- +save(TRUE); +$config->save(FALSE); +$config->save(); + +// Untyped — must not be changed. +$other->save(TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/fixture/basic.php.inc new file mode 100644 index 000000000..9da7bdcb7 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/fixture/basic.php.inc @@ -0,0 +1,23 @@ +save(TRUE); +$config->save(FALSE); +$config->save(); + +// Untyped — must not be changed. +$other->save(TRUE); + +?> +----- + $config->save(), fn() => $config->save(TRUE)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $config->save(), fn() => $config->save(FALSE)); +$config->save(); + +// Untyped — must not be changed. +$other->save(TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/fixture/no_change_variable_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/fixture/no_change_variable_arg.php.inc new file mode 100644 index 000000000..35cb5ca43 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveConfigSaveTrustedDataArgRector/fixture/no_change_variable_arg.php.inc @@ -0,0 +1,5 @@ +save($trusted); diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector/RemoveFilterTipsLongParamRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector/RemoveFilterTipsLongParamRectorTest.php new file mode 100644 index 000000000..eec7ac4ab --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector/RemoveFilterTipsLongParamRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector/config/configured_rule.php new file mode 100644 index 000000000..d71fea5e2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveFilterTipsLongParamRector/config/configured_rule.php @@ -0,0 +1,11 @@ +t('No HTML tags allowed.'); + } +} + +// Class implementing FilterInterface — $long parameter removed. +class MyEscapeFilter implements FilterInterface +{ + public function tips($long = FALSE) + { + return $this->t('HTML is escaped.'); + } +} + +// Class extending FilterBase where $long IS used — must not change. +class MyLongTipsFilter extends FilterBase +{ + public function tips($long = FALSE) + { + if ($long) { + return $this->t('Long tip with details.'); + } + + return $this->t('Short tip.'); + } +} + +// Unrelated class — must not change. +class MyUnrelatedClass +{ + public function tips($long = FALSE) + { + return 'something'; + } +} + +// _filter_tips() call with second argument — second argument removed. +_filter_tips(0, FALSE); + +// _filter_tips() call without second argument — must not change. +_filter_tips(0); + +?> +----- +t('No HTML tags allowed.'); + } +} + +// Class implementing FilterInterface — $long parameter removed. +class MyEscapeFilter implements FilterInterface +{ + public function tips() + { + return $this->t('HTML is escaped.'); + } +} + +// Class extending FilterBase where $long IS used — must not change. +class MyLongTipsFilter extends FilterBase +{ + public function tips($long = FALSE) + { + if ($long) { + return $this->t('Long tip with details.'); + } + + return $this->t('Short tip.'); + } +} + +// Unrelated class — must not change. +class MyUnrelatedClass +{ + public function tips($long = FALSE) + { + return 'something'; + } +} + +// _filter_tips() call with second argument — second argument removed. +_filter_tips(0); + +// _filter_tips() call without second argument — must not change. +_filter_tips(0); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/RemoveHandlerBaseDefineExtraOptionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/RemoveHandlerBaseDefineExtraOptionsRectorTest.php new file mode 100644 index 000000000..e017a37bc --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/RemoveHandlerBaseDefineExtraOptionsRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/config/configured_rule.php new file mode 100644 index 000000000..88e73cf2e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(RemoveHandlerBaseDefineExtraOptionsRector::class); +}; diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/argument_plugin_base.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/argument_plugin_base.php.inc new file mode 100644 index 000000000..86b027ce7 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/argument_plugin_base.php.inc @@ -0,0 +1,32 @@ + '']; + } + + public function title() + { + return 'My Title'; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..7eb8cdf82 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/basic.php.inc @@ -0,0 +1,32 @@ + 'value']; + } + + public function query() + { + // Keep this. + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/filter_plugin_base.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/filter_plugin_base.php.inc new file mode 100644 index 000000000..8b76f06ef --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/filter_plugin_base.php.inc @@ -0,0 +1,32 @@ + FALSE]; + } + + public function adminSummary() + { + return 'summary'; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/fqcn_extends.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/fqcn_extends.php.inc new file mode 100644 index 000000000..ed3700e2f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/fqcn_extends.php.inc @@ -0,0 +1,19 @@ + 0]; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..d31b4221c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,23 @@ + 'value']; + } +} + +?> +----- + 'value']; + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/non_empty_body.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/non_empty_body.php.inc new file mode 100644 index 000000000..f4740c47b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/non_empty_body.php.inc @@ -0,0 +1,25 @@ + 'value_a']; + $option['key_b'] = ['default' => 0]; + $option['key_c'] = ['default' => FALSE]; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/relationship_plugin_base.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/relationship_plugin_base.php.inc new file mode 100644 index 000000000..a5acea32a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/relationship_plugin_base.php.inc @@ -0,0 +1,23 @@ + '']; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/sort_plugin_base.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/sort_plugin_base.php.inc new file mode 100644 index 000000000..98fd370a8 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveHandlerBaseDefineExtraOptionsRector/fixture/sort_plugin_base.php.inc @@ -0,0 +1,23 @@ + 'ASC']; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/RemoveLinkWidgetValidateTitleElementRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/RemoveLinkWidgetValidateTitleElementRectorTest.php new file mode 100644 index 000000000..73c93b3aa --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/RemoveLinkWidgetValidateTitleElementRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/config/configured_rule.php new file mode 100644 index 000000000..8cc290f6c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/config/configured_rule.php @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/fqcn.php.inc new file mode 100644 index 000000000..4fb313388 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/fqcn.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/no_change_method_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/no_change_method_call.php.inc new file mode 100644 index 000000000..2af53727c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/no_change_method_call.php.inc @@ -0,0 +1,5 @@ +validateTitleElement() — instance method call, not a static call. +// The rector only targets static calls, so this must NOT be removed. +$linkWidget->validateTitleElement($element, $form_state, $form); diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..15fe1f02e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveLinkWidgetValidateTitleElementRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,4 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/config/configured_rule.php new file mode 100644 index 000000000..211c18af3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/config/configured_rule.php @@ -0,0 +1,11 @@ +addModule('mymodule', 'modules/mymodule'); +$module_handler->addProfile('standard', 'core/profiles/standard'); +$module_handler->moduleExists('mymodule'); +?> +----- +moduleExists('mymodule'); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/concrete_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/concrete_class.php.inc new file mode 100644 index 000000000..8d56551e4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/concrete_class.php.inc @@ -0,0 +1,13 @@ +addModule('mymodule', 'modules/mymodule'); +$module_handler->addProfile('standard', 'core/profiles/standard'); +$module_handler->moduleExists('mymodule'); +?> +----- +moduleExists('mymodule'); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/no_change_fluent_chain.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/no_change_fluent_chain.php.inc new file mode 100644 index 000000000..b8e9ec940 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/no_change_fluent_chain.php.inc @@ -0,0 +1,15 @@ +addModule('mymodule', 'modules/mymodule')->getSomething(); +?> +----- +addModule('mymodule', 'modules/mymodule')->getSomething(); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..d1a8fb54c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerAddModuleCallsRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,15 @@ +addModule('mymodule', 'modules/mymodule'); +$manager->addProfile('standard', 'core/profiles/standard'); +?> +----- +addModule('mymodule', 'modules/mymodule'); +$manager->addProfile('standard', 'core/profiles/standard'); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/RemoveModuleHandlerDeprecatedMethodsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/RemoveModuleHandlerDeprecatedMethodsRectorTest.php new file mode 100644 index 000000000..81f708653 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/RemoveModuleHandlerDeprecatedMethodsRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/config/configured_rule.php new file mode 100644 index 000000000..1e7ecebb9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/config/configured_rule.php @@ -0,0 +1,11 @@ +writeCache(); +$hookInfo = $moduleHandler->getHookInfo(); +$untyped->writeCache(); + +?> +----- +writeCache(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/get_hook_info_expressions.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/get_hook_info_expressions.php.inc new file mode 100644 index 000000000..a1295bbc0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/get_hook_info_expressions.php.inc @@ -0,0 +1,19 @@ +getHookInfo()); +foreach ($moduleHandler->getHookInfo() as $hook => $info) { + // process hook info +} + +?> +----- + $info) { + // process hook info +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..65ce9687f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,13 @@ +getHookInfo(); +$untyped->writeCache(); + +?> +----- +getHookInfo(); +$untyped->writeCache(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/write_cache_chained_receiver.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/write_cache_chained_receiver.php.inc new file mode 100644 index 000000000..a162ddc25 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveModuleHandlerDeprecatedMethodsRector/fixture/write_cache_chained_receiver.php.inc @@ -0,0 +1,15 @@ +getModuleHandler()->writeCache(); + +?> +----- +getModuleHandler()->writeCache(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/RemoveRootFromConvertDbUrlRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/RemoveRootFromConvertDbUrlRectorTest.php new file mode 100644 index 000000000..e2850d67a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/RemoveRootFromConvertDbUrlRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/config/configured_rule.php new file mode 100644 index 000000000..9df53cd51 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/config/configured_rule.php @@ -0,0 +1,14 @@ +root, TRUE); +// With string root, no third arg +Database::convertDbUrlToConnectionInfo($url, $root); +// Already adapted (boolean second arg): leave unchanged +Database::convertDbUrlToConnectionInfo($url, TRUE); + +?> +----- +root, TRUE); +// With string root, no third arg +Database::convertDbUrlToConnectionInfo($url, $root); +// Already adapted (boolean second arg): leave unchanged +Database::convertDbUrlToConnectionInfo($url, TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/method_call_second_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/method_call_second_arg.php.inc new file mode 100644 index 000000000..7ad38b99e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/method_call_second_arg.php.inc @@ -0,0 +1,19 @@ +getRoot()); +Database::convertDbUrlToConnectionInfo($url, $this->getDrupalRoot(), TRUE); + +?> +----- +getRoot()); +Database::convertDbUrlToConnectionInfo($url, $this->getDrupalRoot(), TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/three_args_property_fetch.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/three_args_property_fetch.php.inc new file mode 100644 index 000000000..1efb3662d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/three_args_property_fetch.php.inc @@ -0,0 +1,17 @@ +root, TRUE); + +?> +----- +root, TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/two_args_property_fetch.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/two_args_property_fetch.php.inc new file mode 100644 index 000000000..b732f6d79 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/two_args_property_fetch.php.inc @@ -0,0 +1,17 @@ +root); + +?> +----- +root); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/two_args_string.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/two_args_string.php.inc new file mode 100644 index 000000000..6cd3fbb08 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture-below-version/two_args_string.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/basic.php.inc new file mode 100644 index 000000000..3022df892 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/basic.php.inc @@ -0,0 +1,25 @@ +root, TRUE); +// With string root, no third arg +Database::convertDbUrlToConnectionInfo($url, $root); +// Already adapted (boolean second arg): leave unchanged +Database::convertDbUrlToConnectionInfo($url, TRUE); + +?> +----- + Database::convertDbUrlToConnectionInfo($url, TRUE), fn() => Database::convertDbUrlToConnectionInfo($url, $this->root, TRUE)); +// With string root, no third arg +Database::convertDbUrlToConnectionInfo($url, $root); +// Already adapted (boolean second arg): leave unchanged +Database::convertDbUrlToConnectionInfo($url, TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/method_call_second_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/method_call_second_arg.php.inc new file mode 100644 index 000000000..2b790ff22 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/method_call_second_arg.php.inc @@ -0,0 +1,19 @@ +getRoot()); +Database::convertDbUrlToConnectionInfo($url, $this->getDrupalRoot(), TRUE); + +?> +----- + Database::convertDbUrlToConnectionInfo($url), fn() => Database::convertDbUrlToConnectionInfo($url, $this->getRoot())); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => Database::convertDbUrlToConnectionInfo($url, TRUE), fn() => Database::convertDbUrlToConnectionInfo($url, $this->getDrupalRoot(), TRUE)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/no_change_bool_second_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/no_change_bool_second_arg.php.inc new file mode 100644 index 000000000..cce0aa7fa --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/no_change_bool_second_arg.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/no_change_variable_second_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/no_change_variable_second_arg.php.inc new file mode 100644 index 000000000..68bfb5f24 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/no_change_variable_second_arg.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/three_args_property_fetch.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/three_args_property_fetch.php.inc new file mode 100644 index 000000000..dbe004c66 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/three_args_property_fetch.php.inc @@ -0,0 +1,17 @@ +root, TRUE); + +?> +----- + Database::convertDbUrlToConnectionInfo($url, TRUE), fn() => Database::convertDbUrlToConnectionInfo($url, $this->root, TRUE)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/two_args_property_fetch.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/two_args_property_fetch.php.inc new file mode 100644 index 000000000..7d11ad856 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/two_args_property_fetch.php.inc @@ -0,0 +1,17 @@ +root); + +?> +----- + Database::convertDbUrlToConnectionInfo($url), fn() => Database::convertDbUrlToConnectionInfo($url, $this->root)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/two_args_string.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/two_args_string.php.inc new file mode 100644 index 000000000..a2182a18c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromConvertDbUrlRector/fixture/two_args_string.php.inc @@ -0,0 +1,17 @@ + +----- + Database::convertDbUrlToConnectionInfo($url), fn() => Database::convertDbUrlToConnectionInfo($url, '/var/www/html')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/RemoveRootFromCreateConnectionOptionsFromUrlRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/RemoveRootFromCreateConnectionOptionsFromUrlRectorTest.php new file mode 100644 index 000000000..a8f9eb9fe --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/RemoveRootFromCreateConnectionOptionsFromUrlRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/config/configured_rule.php new file mode 100644 index 000000000..dba1e5b48 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/config/configured_rule.php @@ -0,0 +1,11 @@ +createConnectionOptionsFromUrl($url, $root); + +\Drupal\Core\Database\Connection::createConnectionOptionsFromUrl($url, $root); +?> +----- +createConnectionOptionsFromUrl($url, NULL); + +\Drupal\Core\Database\Connection::createConnectionOptionsFromUrl($url, NULL); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/fixture/no_change_already_null.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/fixture/no_change_already_null.php.inc new file mode 100644 index 000000000..b213d5b35 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/fixture/no_change_already_null.php.inc @@ -0,0 +1,13 @@ +createConnectionOptionsFromUrl($url, NULL); +?> +----- +createConnectionOptionsFromUrl($url, NULL); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..7d7c98067 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveRootFromCreateConnectionOptionsFromUrlRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,19 @@ +createConnectionOptionsFromUrl($url, $root); + +// Unrelated class — type guard prevents transformation. +/** @var \SomeOtherClass $other */ +$other->createConnectionOptionsFromUrl($url, $root); +?> +----- +createConnectionOptionsFromUrl($url, $root); + +// Unrelated class — type guard prevents transformation. +/** @var \SomeOtherClass $other */ +$other->createConnectionOptionsFromUrl($url, $root); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/RemoveSetUriCallbackRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/RemoveSetUriCallbackRectorTest.php new file mode 100644 index 000000000..76e6a7b83 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/RemoveSetUriCallbackRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/config/configured_rule.php new file mode 100644 index 000000000..2d774ca1b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/config/configured_rule.php @@ -0,0 +1,11 @@ +setUriCallback('my_entity_uri'); +$entity_types['other']->setUriCallback('other_uri')->setLabel('Other'); +$other->someMethod(); + +?> +----- +setLabel('Other'); +$other->someMethod(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/deep_chain.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/deep_chain.php.inc new file mode 100644 index 000000000..81e85daa1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/deep_chain.php.inc @@ -0,0 +1,15 @@ +setUriCallback()->setY() +/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ +$entity_types['my_entity']->setLinkTemplate('canonical', '/my-entity/{my_entity}')->setUriCallback('my_entity_uri')->setLabel('My Entity'); + +?> +----- +setUriCallback()->setY() +/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ +$entity_types['my_entity']->setLinkTemplate('canonical', '/my-entity/{my_entity}')->setLabel('My Entity'); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/extends_implementing_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/extends_implementing_class.php.inc new file mode 100644 index 000000000..42e240d21 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/extends_implementing_class.php.inc @@ -0,0 +1,22 @@ +setUriCallback('my_entity_uri'); + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/no_change_assignment.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/no_change_assignment.php.inc new file mode 100644 index 000000000..26b11317c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/no_change_assignment.php.inc @@ -0,0 +1,6 @@ +setUriCallback(...) is not handled because the +// rector only removes standalone Expression statements and mid-chain calls. +// The assignment wrapper prevents matching. +$x = $entity_type->setUriCallback('my_entity_uri'); diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/no_type_guard.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/no_type_guard.php.inc new file mode 100644 index 000000000..6a442992a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveSetUriCallbackRector/fixture/no_type_guard.php.inc @@ -0,0 +1,5 @@ +setUriCallback('my_entity_uri'); +$unrelated_object->someOtherMethod(); diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/RemoveStateCacheSettingRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/RemoveStateCacheSettingRectorTest.php new file mode 100644 index 000000000..a1529595e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/RemoveStateCacheSettingRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/config/configured_rule.php new file mode 100644 index 000000000..694ad2d16 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(RemoveStateCacheSettingRector::class); +}; diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/basic.php.inc new file mode 100644 index 000000000..820d68f39 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/basic.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/false_value.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/false_value.php.inc new file mode 100644 index 000000000..fca43c800 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/false_value.php.inc @@ -0,0 +1,12 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/no_change_nested_array.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/no_change_nested_array.php.inc new file mode 100644 index 000000000..434f01761 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/no_change_nested_array.php.inc @@ -0,0 +1,6 @@ + TRUE]; diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/no_change_similar_key.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/no_change_similar_key.php.inc new file mode 100644 index 000000000..f55a63bae --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveStateCacheSettingRector/fixture/no_change_similar_key.php.inc @@ -0,0 +1,6 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/config/configured_rule.php new file mode 100644 index 000000000..9f4066813 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/config/configured_rule.php @@ -0,0 +1,14 @@ +trustData()->save(); +$other->someMethod(); + +?> +----- +trustData()->save(); +$other->someMethod(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/assigned_result.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/assigned_result.php.inc new file mode 100644 index 000000000..6809b350d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/assigned_result.php.inc @@ -0,0 +1,15 @@ +trustData(); + +?> +----- + $entity, fn() => $entity->trustData()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/basic.php.inc new file mode 100644 index 000000000..8be26b12d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ +trustData()->save(); +$other->someMethod(); + +?> +----- + $entity, fn() => $entity->trustData())->save(); +$other->someMethod(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/no_change_other_method.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/no_change_other_method.php.inc new file mode 100644 index 000000000..6f9d62c10 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/no_change_other_method.php.inc @@ -0,0 +1,7 @@ +getData(); +$entity->save(); +$other->trustMe(); + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/standalone_statement.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/standalone_statement.php.inc new file mode 100644 index 000000000..12234db03 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTrustDataCallRector/fixture/standalone_statement.php.inc @@ -0,0 +1,15 @@ +trustData(); + +?> +----- + $entity, fn() => $entity->trustData()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/RemoveTwigNodeTransTagArgumentRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/RemoveTwigNodeTransTagArgumentRectorTest.php new file mode 100644 index 000000000..12883a804 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/RemoveTwigNodeTransTagArgumentRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/config/configured_rule.php new file mode 100644 index 000000000..bd52ab455 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getTag()); + +?> +----- +getTag()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture-below-version/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture-below-version/basic.php.inc new file mode 100644 index 000000000..f7cee716b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture-below-version/basic.php.inc @@ -0,0 +1,17 @@ +getTag()); +$other = new OtherClass($a, $b, $c, $d, $e, $f); + +?> +----- +getTag()); +$other = new OtherClass($a, $b, $c, $d, $e, $f); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture-below-version/fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture-below-version/fqcn.php.inc new file mode 100644 index 000000000..8c9773048 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture-below-version/fqcn.php.inc @@ -0,0 +1,13 @@ +getTag()); + +?> +----- +getTag()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/aliased_import.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/aliased_import.php.inc new file mode 100644 index 000000000..a38a9adb0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/aliased_import.php.inc @@ -0,0 +1,19 @@ +getTag()); + +?> +----- + new NodeTrans($body, $plural, $count, $options, $lineno), fn() => new NodeTrans($body, $plural, $count, $options, $lineno, $this->getTag())); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/basic.php.inc new file mode 100644 index 000000000..9050d119e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ +getTag()); +$other = new OtherClass($a, $b, $c, $d, $e, $f); + +?> +----- + new TwigNodeTrans($body, $plural, $count, $options, $lineno), fn() => new TwigNodeTrans($body, $plural, $count, $options, $lineno, $this->getTag())); +$other = new OtherClass($a, $b, $c, $d, $e, $f); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/fqcn.php.inc new file mode 100644 index 000000000..b193bc68e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/fqcn.php.inc @@ -0,0 +1,13 @@ +getTag()); + +?> +----- + new \Drupal\Core\Template\TwigNodeTrans($body, $plural, $count, $options, $lineno), fn() => new \Drupal\Core\Template\TwigNodeTrans($body, $plural, $count, $options, $lineno, $this->getTag())); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/no_change_fewer_args.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/no_change_fewer_args.php.inc new file mode 100644 index 000000000..f10f403fd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveTwigNodeTransTagArgumentRector/fixture/no_change_fewer_args.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/RemoveUpdaterPostInstallMethodsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/RemoveUpdaterPostInstallMethodsRectorTest.php new file mode 100644 index 000000000..d94e3d385 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/RemoveUpdaterPostInstallMethodsRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/config/configured_rule.php new file mode 100644 index 000000000..da92d99dd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/config/configured_rule.php @@ -0,0 +1,11 @@ + 'link', '#title' => 'My task']]; + } + + public function postInstall() + { + \Drupal::logger('mymodule')->info('Installed.'); + } + + public function keepThisMethod() + { + return 'kept'; + } +} + +class UnrelatedClass +{ + public function postInstall() {} +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/extends_module.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/extends_module.php.inc new file mode 100644 index 000000000..3c1046bf8 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/extends_module.php.inc @@ -0,0 +1,32 @@ +info('Module installed.'); + } + + public function keepThisMethod() + { + return 'kept'; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/extends_theme.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/extends_theme.php.inc new file mode 100644 index 000000000..376dc7a60 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/extends_theme.php.inc @@ -0,0 +1,32 @@ + 'link', '#title' => 'Theme task']]; + } + + public function keepThisMethod() + { + return 'kept'; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/no_change_short_name.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/no_change_short_name.php.inc new file mode 100644 index 000000000..a144c5a6f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/no_change_short_name.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/non_empty_body.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/non_empty_body.php.inc new file mode 100644 index 000000000..470df129c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveUpdaterPostInstallMethodsRector/fixture/non_empty_body.php.inc @@ -0,0 +1,33 @@ +info('Step 1.'); + \Drupal::logger('mymodule')->info('Step 2.'); + $this->doSomething(); + } + + public function postInstallTasks() + { + $tasks = []; + $tasks[] = ['#type' => 'link', '#title' => 'Task 1']; + $tasks[] = ['#type' => 'link', '#title' => 'Task 2']; + return $tasks; + } +} + +?> +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/RemoveViewsRowCacheKeysRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/RemoveViewsRowCacheKeysRectorTest.php new file mode 100644 index 000000000..3e06a54ae --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/RemoveViewsRowCacheKeysRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/config/configured_rule.php new file mode 100644 index 000000000..a10b872dc --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/config/configured_rule.php @@ -0,0 +1,11 @@ +getRowCacheKeys($row); +$id = $cache_plugin->getRowId($row); + +?> +----- + [ + 'keys' => $cache_plugin->getRowCacheKeys($row), + 'tags' => $cache_plugin->getRowCacheTags($row), + 'max-age' => $max_age, + ], +]; + +?> +----- + [ + 'tags' => $cache_plugin->getRowCacheTags($row), + 'max-age' => $max_age, + ], +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/both_deprecated_calls.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/both_deprecated_calls.php.inc new file mode 100644 index 000000000..1eced7f10 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/both_deprecated_calls.php.inc @@ -0,0 +1,25 @@ + [ + 'keys' => $cache_plugin->getRowCacheKeys($row), + 'id' => $cache_plugin->getRowId($row), + 'tags' => $cache_plugin->getRowCacheTags($row), + ], +]; + +?> +----- + [ + 'tags' => $cache_plugin->getRowCacheTags($row), + ], +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/delegation_method.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/delegation_method.php.inc new file mode 100644 index 000000000..43bf1a3ea --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/delegation_method.php.inc @@ -0,0 +1,41 @@ +plugin->getRowCacheKeys($row); + } + + public function getRowId(ResultRow $row) { + return $this->plugin->getRowId($row); + } + + public function getCacheMaxAge(): int { + return $this->plugin->getCacheMaxAge(); + } +} +?> +----- +plugin->getCacheMaxAge(); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/get_row_id.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/get_row_id.php.inc new file mode 100644 index 000000000..99de8044e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/get_row_id.php.inc @@ -0,0 +1,26 @@ + [ + 'keys' => $cache_plugin->getRowId($row), + 'tags' => $cache_plugin->getRowCacheTags($row), + 'max-age' => $max_age, + ], +]; + +?> +----- + [ + 'tags' => $cache_plugin->getRowCacheTags($row), + 'max-age' => $max_age, + ], +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/no_change_standalone_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/no_change_standalone_call.php.inc new file mode 100644 index 000000000..afa1ced3a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/no_change_standalone_call.php.inc @@ -0,0 +1,7 @@ +getRowCacheKeys($row); +$id = $cache_plugin->getRowId($row); diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..70b0e03f2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,10 @@ + [ + 'keys' => $unrelated->getRowCacheKeys($row), + 'id' => $unrelated->getRowId($row), + ], +]; diff --git a/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/variable_first.php.inc b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/variable_first.php.inc new file mode 100644 index 000000000..bdd500678 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RemoveViewsRowCacheKeysRector/fixture/variable_first.php.inc @@ -0,0 +1,25 @@ +getRowCacheKeys($row); +$cache = [ + '#cache' => [ + 'keys' => $keys, + 'contexts' => ['languages:language_interface', 'theme', 'user.permissions'], + ], +]; + +?> +----- + [ + 'contexts' => ['languages:language_interface', 'theme', 'user.permissions'], + ], +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/RenameStopProceduralHookScanRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/RenameStopProceduralHookScanRectorTest.php new file mode 100644 index 000000000..86b883691 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/RenameStopProceduralHookScanRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/config/configured_rule.php new file mode 100644 index 000000000..5b5efeaf9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/config/configured_rule.php @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/fqcn_attribute.php.inc b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/fqcn_attribute.php.inc new file mode 100644 index 000000000..3bbacf7de --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/fqcn_attribute.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/multiple_functions.php.inc b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/multiple_functions.php.inc new file mode 100644 index 000000000..834537190 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/multiple_functions.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/no_change_already_new_name.php.inc b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/no_change_already_new_name.php.inc new file mode 100644 index 000000000..50ee419b5 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/RenameStopProceduralHookScanRector/fixture/no_change_already_new_name.php.inc @@ -0,0 +1,7 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/config/configured_rule.php new file mode 100644 index 000000000..5ddc0ad9e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getDefinition('plugin.cache_clearer') + ->addMethodCall('addCachedDiscovery', [new Reference('my.plugin.manager')]); +} +?> +----- +getDefinition('plugin.cache_clearer') + ->addMethodCall('addCachedDiscovery', [new Reference('my.plugin.manager')]); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/fixture/basic.php.inc new file mode 100644 index 000000000..88a652281 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/fixture/basic.php.inc @@ -0,0 +1,23 @@ +getDefinition('plugin.cache_clearer') + ->addMethodCall('addCachedDiscovery', [new Reference('my.plugin.manager')]); +} +?> +----- + $container->getDefinition('my.plugin.manager')->addTag('plugin_manager_cache_clear'), fn() => $container->getDefinition('plugin.cache_clearer') + ->addMethodCall('addCachedDiscovery', [new Reference('my.plugin.manager')])); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..e31ebfc57 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAddCachedDiscoveryMethodCallRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,11 @@ +addMethodCall('addCachedDiscovery', [new \Symfony\Component\DependencyInjection\Reference('my.plugin.manager')]); +?> +----- +addMethodCall('addCachedDiscovery', [new \Symfony\Component\DependencyInjection\Reference('my.plugin.manager')]); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/ReplaceAlphadecimalToIntNullRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/ReplaceAlphadecimalToIntNullRectorTest.php new file mode 100644 index 000000000..1debde023 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/ReplaceAlphadecimalToIntNullRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/config/configured_rule.php new file mode 100644 index 000000000..f9b41f2f0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture-below-version/fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture-below-version/fqcn.php.inc new file mode 100644 index 000000000..6a06a4262 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture-below-version/fqcn.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture-below-version/inline_usage.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture-below-version/inline_usage.php.inc new file mode 100644 index 000000000..cc380cc2f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture-below-version/inline_usage.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/basic.php.inc new file mode 100644 index 000000000..986e1b07f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/basic.php.inc @@ -0,0 +1,21 @@ + +----- + 0, fn() => Number::alphadecimalToInt(NULL)); +$b = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => 0, fn() => Number::alphadecimalToInt('')); +$c = Number::alphadecimalToInt('abc'); +$d = OtherClass::alphadecimalToInt(NULL); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/fqcn.php.inc new file mode 100644 index 000000000..e5908a8d0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/fqcn.php.inc @@ -0,0 +1,15 @@ + +----- + 0, fn() => \Drupal\Component\Utility\Number::alphadecimalToInt(NULL)); +$b = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => 0, fn() => \Drupal\Component\Utility\Number::alphadecimalToInt('')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/inline_usage.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/inline_usage.php.inc new file mode 100644 index 000000000..458fd3c09 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/inline_usage.php.inc @@ -0,0 +1,19 @@ + +----- + 0, fn() => Number::alphadecimalToInt(null))); +$x = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => 0, fn() => Number::alphadecimalToInt('')) + 5; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/no_change_variable.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/no_change_variable.php.inc new file mode 100644 index 000000000..ac6178347 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceAlphadecimalToIntNullRector/fixture/no_change_variable.php.inc @@ -0,0 +1,7 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/config/configured_rule.php new file mode 100644 index 000000000..218f82f76 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getCountNewComments($entity); +?> +----- +getCountNewComments($entity); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/basic.php.inc new file mode 100644 index 000000000..f59832fc9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/basic.php.inc @@ -0,0 +1,11 @@ +getCountNewComments($entity); +?> +----- + \Drupal::service(\Drupal\history\HistoryManager::class)->getCountNewComments($entity), fn() => $comment_manager->getCountNewComments($entity)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/class_property.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/class_property.php.inc new file mode 100644 index 000000000..adb404e2f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/class_property.php.inc @@ -0,0 +1,29 @@ +commentManager->getCountNewComments($entity); + } +} +?> +----- + \Drupal::service(\Drupal\history\HistoryManager::class)->getCountNewComments($entity), fn() => $this->commentManager->getCountNewComments($entity)); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/concrete_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/concrete_class.php.inc new file mode 100644 index 000000000..a0e6e6fc0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/concrete_class.php.inc @@ -0,0 +1,9 @@ +getCountNewComments($entity); diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/multiple_args.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/multiple_args.php.inc new file mode 100644 index 000000000..17c29c469 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/multiple_args.php.inc @@ -0,0 +1,11 @@ +getCountNewComments($entity, 'comment', 0); +?> +----- + \Drupal::service(\Drupal\history\HistoryManager::class)->getCountNewComments($entity, 'comment', 0), fn() => $comment_manager->getCountNewComments($entity, 'comment', 0)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/no_change_service_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/no_change_service_call.php.inc new file mode 100644 index 000000000..6d78f47b0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/no_change_service_call.php.inc @@ -0,0 +1,5 @@ +getCountNewComments($entity); diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..cd1bd3adf --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,12 @@ +getCountNewComments($entity); diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/traditional_constructor.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/traditional_constructor.php.inc new file mode 100644 index 000000000..422c99c2a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceCommentManagerGetCountNewCommentsRector/fixture/traditional_constructor.php.inc @@ -0,0 +1,41 @@ +commentManager = $commentManager; + } + + public function getCountNewComments(\Drupal\Core\Entity\EntityInterface $entity): int|false { + return $this->commentManager->getCountNewComments($entity, 'comment_forum', 0); + } +} +?> +----- +commentManager = $commentManager; + } + + public function getCountNewComments(\Drupal\Core\Entity\EntityInterface $entity): int|false { + return \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service(\Drupal\history\HistoryManager::class)->getCountNewComments($entity, 'comment_forum', 0), fn() => $this->commentManager->getCountNewComments($entity, 'comment_forum', 0)); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/ReplaceDateTimeRangeConstantsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/ReplaceDateTimeRangeConstantsRectorTest.php new file mode 100644 index 000000000..db8f0a1d1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/ReplaceDateTimeRangeConstantsRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/config/configured_rule.php new file mode 100644 index 000000000..2aace06ea --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/basic.php.inc new file mode 100644 index 000000000..a04beb54f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/basic.php.inc @@ -0,0 +1,21 @@ + +----- + \Drupal\datetime_range\DateTimeRangeDisplayOptions::Both->value, fn() => DateTimeRangeConstantsInterface::BOTH); +$b = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\datetime_range\DateTimeRangeDisplayOptions::StartDate->value, fn() => DateTimeRangeConstantsInterface::START_DATE); +$c = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\datetime_range\DateTimeRangeDisplayOptions::EndDate->value, fn() => DateTimeRangeConstantsInterface::END_DATE); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('datetime.views_helper')->buildViewsData($field_storage, $data, 'value'), fn() => datetime_type_field_views_data_helper($field_storage, $data, 'value')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/function_replacement.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/function_replacement.php.inc new file mode 100644 index 000000000..324cf5fbe --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/function_replacement.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('datetime.views_helper')->buildViewsData($field_storage, $data, 'value'), fn() => datetime_type_field_views_data_helper($field_storage, $data, 'value')); + +$result = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('datetime.views_helper')->buildViewsData($field_storage, $data, $column), fn() => datetime_type_field_views_data_helper($field_storage, $data, $column)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/match_arm.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/match_arm.php.inc new file mode 100644 index 000000000..7464ff49a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/match_arm.php.inc @@ -0,0 +1,27 @@ + 'Both dates', + DateTimeRangeConstantsInterface::START_DATE => 'Start date only', + DateTimeRangeConstantsInterface::END_DATE => 'End date only', +}; + +?> +----- + \Drupal\datetime_range\DateTimeRangeDisplayOptions::Both->value, fn() => DateTimeRangeConstantsInterface::BOTH) => 'Both dates', + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\datetime_range\DateTimeRangeDisplayOptions::StartDate->value, fn() => DateTimeRangeConstantsInterface::START_DATE) => 'Start date only', + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\datetime_range\DateTimeRangeDisplayOptions::EndDate->value, fn() => DateTimeRangeConstantsInterface::END_DATE) => 'End date only', +}; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/self_static_in_implementor.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/self_static_in_implementor.php.inc new file mode 100644 index 000000000..9dfac06cd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceDateTimeRangeConstantsRector/fixture/self_static_in_implementor.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/ReplaceEditorLoadRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/ReplaceEditorLoadRectorTest.php new file mode 100644 index 000000000..9aea19411 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/ReplaceEditorLoadRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/config/configured_rule.php new file mode 100644 index 000000000..d500adcf9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/as_argument.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/as_argument.php.inc new file mode 100644 index 000000000..30b1132f0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/as_argument.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::entityTypeManager()->getStorage('editor')->load($format_id), fn() => editor_load($format_id))); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/basic.php.inc new file mode 100644 index 000000000..c7321a548 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/basic.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::entityTypeManager()->getStorage('editor')->load($format_id), fn() => editor_load($format_id)); +$other = other_function($format_id); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/inline_usage.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/inline_usage.php.inc new file mode 100644 index 000000000..efc2225a9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/inline_usage.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::entityTypeManager()->getStorage('editor')->load($format_id), fn() => editor_load($format_id)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_method_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_method_call.php.inc new file mode 100644 index 000000000..2c61c2693 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_method_call.php.inc @@ -0,0 +1,35 @@ +editor_load($id); + } + + private function editor_load($id) + { + return $id; + } +} + +?> +----- +editor_load($id); + } + + private function editor_load($id) + { + return $id; + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_multiple_args.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_multiple_args.php.inc new file mode 100644 index 000000000..eb07f1bf7 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_multiple_args.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_no_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_no_arg.php.inc new file mode 100644 index 000000000..31e26bf81 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEditorLoadRector/fixture/no_change_no_arg.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/ReplaceEntityOriginalPropertyRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/ReplaceEntityOriginalPropertyRectorTest.php new file mode 100644 index 000000000..ff61a698b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/ReplaceEntityOriginalPropertyRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/config/configured_rule.php new file mode 100644 index 000000000..0127f4881 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/config/configured_rule.php @@ -0,0 +1,14 @@ +original; +$entity->original = $unchanged; +$field = $entity->other_property; + +?> +----- +original; +$entity->original = $unchanged; +$field = $entity->other_property; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/basic.php.inc new file mode 100644 index 000000000..6fbc36bd9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ +original; +$entity->original = $unchanged; +$field = $entity->other_property; + +?> +----- + $entity->getOriginal(), fn() => $entity->original); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $entity->setOriginal($unchanged), fn() => $entity->original = $unchanged); +$field = $entity->other_property; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/chain.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/chain.php.inc new file mode 100644 index 000000000..64ff50bd5 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/chain.php.inc @@ -0,0 +1,13 @@ +original->id(); + +?> +----- + $entity->getOriginal(), fn() => $entity->original)->id(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/no_change_this_original.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/no_change_this_original.php.inc new file mode 100644 index 000000000..6eecb395e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/no_change_this_original.php.inc @@ -0,0 +1,17 @@ +original is preserved to avoid false positives on non-entity classes +// like EntityTypeEvent and FieldStorageDefinitionEvent that have a real $original property. +$value = $this->original; +$this->original = $other; + +?> +----- +original is preserved to avoid false positives on non-entity classes +// like EntityTypeEvent and FieldStorageDefinitionEvent that have a real $original property. +$value = $this->original; +$this->original = $other; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/nullsafe.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/nullsafe.php.inc new file mode 100644 index 000000000..42bc68e70 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/nullsafe.php.inc @@ -0,0 +1,13 @@ +original?->id(); + +?> +----- + $entity?->getOriginal(), fn() => $entity?->original)?->id(); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/write_complex_rhs.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/write_complex_rhs.php.inc new file mode 100644 index 000000000..ee8ffa616 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityOriginalPropertyRector/fixture/write_complex_rhs.php.inc @@ -0,0 +1,13 @@ +original = $storage->load($entity->id()); + +?> +----- + $entity->setOriginal($storage->load($entity->id())), fn() => $entity->original = $storage->load($entity->id())); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/ReplaceEntityReferenceRecursiveLimitRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/ReplaceEntityReferenceRecursiveLimitRectorTest.php new file mode 100644 index 000000000..c2cc1b060 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/ReplaceEntityReferenceRecursiveLimitRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/config/configured_rule.php new file mode 100644 index 000000000..ec4a93c1d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/basic.php.inc new file mode 100644 index 000000000..c40b528b6 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/basic.php.inc @@ -0,0 +1,23 @@ + EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT) { + return; +} + +$limit = EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT; + +?> +----- + EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT) { + return; +} + +$limit = EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/in_function_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/in_function_call.php.inc new file mode 100644 index 000000000..7e9d8c9f3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/in_function_call.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/in_ternary.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/in_ternary.php.inc new file mode 100644 index 000000000..5b7aba27b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture-below-version/in_ternary.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/aliased_import.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/aliased_import.php.inc new file mode 100644 index 000000000..de11144d8 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/aliased_import.php.inc @@ -0,0 +1,17 @@ + +----- + 20, fn() => Formatter::RECURSIVE_RENDER_LIMIT); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/basic.php.inc new file mode 100644 index 000000000..9dec15348 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/basic.php.inc @@ -0,0 +1,23 @@ + EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT) { + return; +} + +$limit = EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT; + +?> +----- + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => 20, fn() => EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT)) { + return; +} + +$limit = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => 20, fn() => EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/in_function_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/in_function_call.php.inc new file mode 100644 index 000000000..a663ddee1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/in_function_call.php.inc @@ -0,0 +1,23 @@ + +----- + 20, fn() => EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT)); + +// RECURSIVE_RENDER_LIMIT used as a second argument. +$result = some_check($id, \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => 20, fn() => EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/in_ternary.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/in_ternary.php.inc new file mode 100644 index 000000000..c80f529f1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/in_ternary.php.inc @@ -0,0 +1,23 @@ + +----- + 20, fn() => EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT) : 10; + +// RECURSIVE_RENDER_LIMIT used as the false branch of a ternary. +$limit2 = $override ? 5 : \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => 20, fn() => EntityReferenceEntityFormatter::RECURSIVE_RENDER_LIMIT); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/no_change_parent_in_subclass.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/no_change_parent_in_subclass.php.inc new file mode 100644 index 000000000..5b1cb5987 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceEntityReferenceRecursiveLimitRector/fixture/no_change_parent_in_subclass.php.inc @@ -0,0 +1,9 @@ + self::RECURSIVE_RENDER_LIMIT) { + return false; + } + if ($count > static::RECURSIVE_RENDER_LIMIT) { + return false; + } + return true; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/ReplaceFieldgroupToFieldsetRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/ReplaceFieldgroupToFieldsetRectorTest.php new file mode 100644 index 000000000..98ba5696b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/ReplaceFieldgroupToFieldsetRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/config/configured_rule.php new file mode 100644 index 000000000..5df4d2e89 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/config/configured_rule.php @@ -0,0 +1,14 @@ + 'fieldgroup', + '#title' => 'Account settings', +]; +$form['other'] = [ + '#type' => 'fieldset', + '#title' => 'Other settings', +]; + +?> +----- + 'fieldgroup', + '#title' => 'Account settings', +]; +$form['other'] = [ + '#type' => 'fieldset', + '#title' => 'Other settings', +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture-below-version/deeply_nested.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture-below-version/deeply_nested.php.inc new file mode 100644 index 000000000..702dbcc8d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture-below-version/deeply_nested.php.inc @@ -0,0 +1,21 @@ + 'fieldgroup' inside a deeply nested form array must be changed. +$form['wrapper']['group']['settings'] = [ + '#type' => 'fieldgroup', + '#title' => 'Advanced settings', + '#weight' => 10, +]; + +?> +----- + 'fieldgroup' inside a deeply nested form array must be changed. +$form['wrapper']['group']['settings'] = [ + '#type' => 'fieldgroup', + '#title' => 'Advanced settings', + '#weight' => 10, +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/basic.php.inc new file mode 100644 index 000000000..29a17e1e6 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/basic.php.inc @@ -0,0 +1,28 @@ + 'fieldgroup', + '#title' => 'Account settings', +]; +$form['other'] = [ + '#type' => 'fieldset', + '#title' => 'Other settings', +]; + +?> +----- + [ + '#type' => 'fieldset', + '#title' => 'Account settings', +], fn() => [ + '#type' => 'fieldgroup', + '#title' => 'Account settings', +]); +$form['other'] = [ + '#type' => 'fieldset', + '#title' => 'Other settings', +]; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/deeply_nested.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/deeply_nested.php.inc new file mode 100644 index 000000000..a7dda79c0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/deeply_nested.php.inc @@ -0,0 +1,25 @@ + 'fieldgroup' inside a deeply nested form array must be changed. +$form['wrapper']['group']['settings'] = [ + '#type' => 'fieldgroup', + '#title' => 'Advanced settings', + '#weight' => 10, +]; + +?> +----- + 'fieldgroup' inside a deeply nested form array must be changed. +$form['wrapper']['group']['settings'] = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => [ + '#type' => 'fieldset', + '#title' => 'Advanced settings', + '#weight' => 10, +], fn() => [ + '#type' => 'fieldgroup', + '#title' => 'Advanced settings', + '#weight' => 10, +]); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/no_change_type_without_hash.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/no_change_type_without_hash.php.inc new file mode 100644 index 000000000..a22da895d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/no_change_type_without_hash.php.inc @@ -0,0 +1,8 @@ + 'fieldgroup', + 'label' => 'My group', +]; diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/no_change_variable_assignment.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/no_change_variable_assignment.php.inc new file mode 100644 index 000000000..135c78a1e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceFieldgroupToFieldsetRector/fixture/no_change_variable_assignment.php.inc @@ -0,0 +1,6 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/config/configured_rule.php new file mode 100644 index 000000000..fe8222666 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..593f6ae2e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ + +----- + locale_config_batch_update_default_config_langcodes($context), fn() => locale_config_batch_set_config_langcodes($context)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => locale_config_batch_update_config_translations($names, $langcodes, $context), fn() => locale_config_batch_refresh_name($names, $langcodes, $context)); +other_function($context); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/expression_positions.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/expression_positions.php.inc new file mode 100644 index 000000000..5fcf58bb0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/expression_positions.php.inc @@ -0,0 +1,29 @@ + +----- + locale_config_batch_update_config_translations($names, $langcodes, $context), fn() => locale_config_batch_refresh_name($names, $langcodes, $context)); + +$result = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => locale_config_batch_update_config_translations($names, $langcodes, $context), fn() => locale_config_batch_refresh_name($names, $langcodes, $context)); + +if (\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => locale_config_batch_update_config_translations($names, $langcodes, $context), fn() => locale_config_batch_refresh_name($names, $langcodes, $context))) { + doSomething(); +} + +$items[] = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => locale_config_batch_update_config_translations($names, $langcodes, $context), fn() => locale_config_batch_refresh_name($names, $langcodes, $context)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/fqcn_prefix.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/fqcn_prefix.php.inc new file mode 100644 index 000000000..7a8e82ed2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceLocaleConfigBatchFunctionsRector/fixture/fqcn_prefix.php.inc @@ -0,0 +1,15 @@ + +----- + locale_config_batch_update_default_config_langcodes($context), fn() => \locale_config_batch_set_config_langcodes($context)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.1.0', fn() => locale_config_batch_update_config_translations($names, $langcodes, $context), fn() => \locale_config_batch_refresh_name($names, $langcodes, $context)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/ReplaceNodeAccessViewAllNodesRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/ReplaceNodeAccessViewAllNodesRectorTest.php new file mode 100644 index 000000000..bf75d17ce --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/ReplaceNodeAccessViewAllNodesRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/config/configured_rule.php new file mode 100644 index 000000000..d4fc71b79 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/fixture/basic.php.inc new file mode 100644 index 000000000..938d8f0f7 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ + +----- + \Drupal::entityTypeManager()->getAccessControlHandler('node')->checkAllGrants(\Drupal::currentUser()), fn() => node_access_view_all_nodes()); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::entityTypeManager()->getAccessControlHandler('node')->checkAllGrants($account), fn() => node_access_view_all_nodes($account)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service('node.view_all_nodes_memory_cache')->deleteAll(), fn() => drupal_static_reset('node_access_view_all_nodes')); +drupal_static_reset('other_function'); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/fixture/in_condition.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/fixture/in_condition.php.inc new file mode 100644 index 000000000..4029d7c5d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAccessViewAllNodesRector/fixture/in_condition.php.inc @@ -0,0 +1,27 @@ + +----- + \Drupal::entityTypeManager()->getAccessControlHandler('node')->checkAllGrants(\Drupal::currentUser()), fn() => node_access_view_all_nodes())) { + do_something(); +} + +// Result used in a condition with an explicit account +if (\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::entityTypeManager()->getAccessControlHandler('node')->checkAllGrants($account), fn() => node_access_view_all_nodes($account))) { + do_something_else(); +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/ReplaceNodeAddBodyFieldRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/ReplaceNodeAddBodyFieldRectorTest.php new file mode 100644 index 000000000..edbdaa6cd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/ReplaceNodeAddBodyFieldRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/config/configured_rule.php new file mode 100644 index 000000000..8ba11a88c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/basic.php.inc new file mode 100644 index 000000000..0054d5acc --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/basic.php.inc @@ -0,0 +1,13 @@ + +----- + $this->createBodyField('node', $nodeType->id()), fn() => node_add_body_field($nodeType)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $this->createBodyField('node', $nodeType->id(), 'body', 'My Body'), fn() => node_add_body_field($nodeType, 'My Body')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/fqcn_prefix.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/fqcn_prefix.php.inc new file mode 100644 index 000000000..e36d0281e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/fqcn_prefix.php.inc @@ -0,0 +1,13 @@ + +----- + $this->createBodyField('node', $nodeType->id()), fn() => \node_add_body_field($nodeType)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $this->createBodyField('node', $nodeType->id(), 'body', 'My Body'), fn() => \node_add_body_field($nodeType, 'My Body')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/method_call_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/method_call_arg.php.inc new file mode 100644 index 000000000..732b2a41f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/method_call_arg.php.inc @@ -0,0 +1,13 @@ +getNodeType()); +node_add_body_field($this->getNodeType(), 'Custom Label'); + +?> +----- + $this->createBodyField('node', $this->getNodeType()->id()), fn() => node_add_body_field($this->getNodeType())); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $this->createBodyField('node', $this->getNodeType()->id(), 'body', 'Custom Label'), fn() => node_add_body_field($this->getNodeType(), 'Custom Label')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/no_change_no_args.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/no_change_no_args.php.inc new file mode 100644 index 000000000..bd65bb760 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeAddBodyFieldRector/fixture/no_change_no_args.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/ReplaceNodeModuleProceduralFunctionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/ReplaceNodeModuleProceduralFunctionsRectorTest.php new file mode 100644 index 000000000..3de036586 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/ReplaceNodeModuleProceduralFunctionsRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/config/configured_rule.php new file mode 100644 index 000000000..5eab58cd1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..8c3540908 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/fixture/basic.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service('entity_type.bundle.info')->getBundleLabels('node'), fn() => node_type_get_names()); +$label = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $node->getBundleEntity()->label(), fn() => node_get_type_label($node)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/fixture/expression_positions.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/fixture/expression_positions.php.inc new file mode 100644 index 000000000..9f95ec6d0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeModuleProceduralFunctionsRector/fixture/expression_positions.php.inc @@ -0,0 +1,17 @@ +getNode()); + +?> +----- + \Drupal::service('entity_type.bundle.info')->getBundleLabels('node'), fn() => node_type_get_names()); +if (\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service('entity_type.bundle.info')->getBundleLabels('node'), fn() => node_type_get_names())) { +} +$label = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $this->getNode()->getBundleEntity()->label(), fn() => node_get_type_label($this->getNode())); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/ReplaceNodeSetPreviewModeRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/ReplaceNodeSetPreviewModeRectorTest.php new file mode 100644 index 000000000..d6871e82b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/ReplaceNodeSetPreviewModeRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/config/configured_rule.php new file mode 100644 index 000000000..9d83abc0e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/config/configured_rule.php @@ -0,0 +1,14 @@ +setPreviewMode(DRUPAL_DISABLED); +$nodeType->setPreviewMode(DRUPAL_OPTIONAL); +$nodeType->setPreviewMode(DRUPAL_REQUIRED); +$nodeType->setPreviewMode(0); +$nodeType->setPreviewMode(1); +$nodeType->setPreviewMode(2); + +?> +----- +setPreviewMode(DRUPAL_DISABLED); +$nodeType->setPreviewMode(DRUPAL_OPTIONAL); +$nodeType->setPreviewMode(DRUPAL_REQUIRED); +$nodeType->setPreviewMode(0); +$nodeType->setPreviewMode(1); +$nodeType->setPreviewMode(2); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/basic.php.inc new file mode 100644 index 000000000..2ad451c4f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/basic.php.inc @@ -0,0 +1,23 @@ +setPreviewMode(DRUPAL_DISABLED); +$nodeType->setPreviewMode(DRUPAL_OPTIONAL); +$nodeType->setPreviewMode(DRUPAL_REQUIRED); +$nodeType->setPreviewMode(0); +$nodeType->setPreviewMode(1); +$nodeType->setPreviewMode(2); + +?> +----- + $nodeType->setPreviewMode(\Drupal\node\NodePreviewMode::Disabled), fn() => $nodeType->setPreviewMode(DRUPAL_DISABLED)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $nodeType->setPreviewMode(\Drupal\node\NodePreviewMode::Optional), fn() => $nodeType->setPreviewMode(DRUPAL_OPTIONAL)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $nodeType->setPreviewMode(\Drupal\node\NodePreviewMode::Required), fn() => $nodeType->setPreviewMode(DRUPAL_REQUIRED)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $nodeType->setPreviewMode(\Drupal\node\NodePreviewMode::Disabled), fn() => $nodeType->setPreviewMode(0)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $nodeType->setPreviewMode(\Drupal\node\NodePreviewMode::Optional), fn() => $nodeType->setPreviewMode(1)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $nodeType->setPreviewMode(\Drupal\node\NodePreviewMode::Required), fn() => $nodeType->setPreviewMode(2)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_change_getpreviewmode.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_change_getpreviewmode.php.inc new file mode 100644 index 000000000..2044825ad --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_change_getpreviewmode.php.inc @@ -0,0 +1,19 @@ +getPreviewMode() === DRUPAL_DISABLED) { +} +$mode = $nodeType->getPreviewMode(TRUE); + +?> +----- +getPreviewMode() === DRUPAL_DISABLED) { +} +$mode = $nodeType->getPreviewMode(TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_change_unknown_int.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_change_unknown_int.php.inc new file mode 100644 index 000000000..2e0868534 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_change_unknown_int.php.inc @@ -0,0 +1,13 @@ +setPreviewMode(3); +$nodeType->setPreviewMode(-1); + +?> +----- +setPreviewMode(3); +$nodeType->setPreviewMode(-1); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_type_guard.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_type_guard.php.inc new file mode 100644 index 000000000..df8770891 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceNodeSetPreviewModeRector/fixture/no_type_guard.php.inc @@ -0,0 +1,5 @@ +setPreviewMode(DRUPAL_DISABLED); +$anyObject->setPreviewMode(1); diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/ReplacePdoFetchConstantsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/ReplacePdoFetchConstantsRectorTest.php new file mode 100644 index 000000000..71184476c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/ReplacePdoFetchConstantsRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/config/configured_rule.php new file mode 100644 index 000000000..c586928e1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/config/configured_rule.php @@ -0,0 +1,14 @@ +setFetchMode(\PDO::FETCH_ASSOC); + $statement->fetch(\PDO::FETCH_OBJ); + $rows = $statement->fetchAll(\PDO::FETCH_NUM); + $data = $statement->fetchAllAssoc('id', \PDO::FETCH_ASSOC); + $result = $db->query('SELECT name FROM {test}', [], ['fetch' => \PDO::FETCH_ASSOC]); + // Raw PDO: leave unchanged + $pdo = $statement->getClientStatement()->fetchAll(\PDO::FETCH_ASSOC); +} +?> +----- +setFetchMode(\PDO::FETCH_ASSOC); + $statement->fetch(\PDO::FETCH_OBJ); + $rows = $statement->fetchAll(\PDO::FETCH_NUM); + $data = $statement->fetchAllAssoc('id', \PDO::FETCH_ASSOC); + $result = $db->query('SELECT name FROM {test}', [], ['fetch' => \PDO::FETCH_ASSOC]); + // Raw PDO: leave unchanged + $pdo = $statement->getClientStatement()->fetchAll(\PDO::FETCH_ASSOC); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture-below-version/fetch_column_and_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture-below-version/fetch_column_and_class.php.inc new file mode 100644 index 000000000..91324c449 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture-below-version/fetch_column_and_class.php.inc @@ -0,0 +1,23 @@ +setFetchMode(\PDO::FETCH_COLUMN); + $statement->fetch(\PDO::FETCH_CLASS); + $rows = $statement->fetchAll(\PDO::FETCH_COLUMN); +} + +?> +----- +setFetchMode(\PDO::FETCH_COLUMN); + $statement->fetch(\PDO::FETCH_CLASS); + $rows = $statement->fetchAll(\PDO::FETCH_COLUMN); +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture-below-version/no_change_native_pdo.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture-below-version/no_change_native_pdo.php.inc new file mode 100644 index 000000000..f8c596974 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture-below-version/no_change_native_pdo.php.inc @@ -0,0 +1,4 @@ +fetchAll(\PDO::FETCH_ASSOC); diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/basic.php.inc new file mode 100644 index 000000000..38b8e8c9d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/basic.php.inc @@ -0,0 +1,29 @@ +setFetchMode(\PDO::FETCH_ASSOC); + $statement->fetch(\PDO::FETCH_OBJ); + $rows = $statement->fetchAll(\PDO::FETCH_NUM); + $data = $statement->fetchAllAssoc('id', \PDO::FETCH_ASSOC); + $result = $db->query('SELECT name FROM {test}', [], ['fetch' => \PDO::FETCH_ASSOC]); + // Raw PDO: leave unchanged + $pdo = $statement->getClientStatement()->fetchAll(\PDO::FETCH_ASSOC); +} +?> +----- + $statement->setFetchMode(\Drupal\Core\Database\Statement\FetchAs::Associative), fn() => $statement->setFetchMode(\PDO::FETCH_ASSOC)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $statement->fetch(\Drupal\Core\Database\Statement\FetchAs::Object), fn() => $statement->fetch(\PDO::FETCH_OBJ)); + $rows = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $statement->fetchAll(\Drupal\Core\Database\Statement\FetchAs::List), fn() => $statement->fetchAll(\PDO::FETCH_NUM)); + $data = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $statement->fetchAllAssoc('id', \Drupal\Core\Database\Statement\FetchAs::Associative), fn() => $statement->fetchAllAssoc('id', \PDO::FETCH_ASSOC)); + $result = $db->query('SELECT name FROM {test}', [], ['fetch' => \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Database\Statement\FetchAs::Associative, fn() => \PDO::FETCH_ASSOC)]); + // Raw PDO: leave unchanged + $pdo = $statement->getClientStatement()->fetchAll(\PDO::FETCH_ASSOC); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/fetch_column_and_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/fetch_column_and_class.php.inc new file mode 100644 index 000000000..364b85994 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/fetch_column_and_class.php.inc @@ -0,0 +1,23 @@ +setFetchMode(\PDO::FETCH_COLUMN); + $statement->fetch(\PDO::FETCH_CLASS); + $rows = $statement->fetchAll(\PDO::FETCH_COLUMN); +} + +?> +----- + $statement->setFetchMode(\Drupal\Core\Database\Statement\FetchAs::Column), fn() => $statement->setFetchMode(\PDO::FETCH_COLUMN)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $statement->fetch(\Drupal\Core\Database\Statement\FetchAs::ClassObject), fn() => $statement->fetch(\PDO::FETCH_CLASS)); + $rows = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $statement->fetchAll(\Drupal\Core\Database\Statement\FetchAs::Column), fn() => $statement->fetchAll(\PDO::FETCH_COLUMN)); +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/no_change_native_pdo.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/no_change_native_pdo.php.inc new file mode 100644 index 000000000..f8c596974 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/no_change_native_pdo.php.inc @@ -0,0 +1,4 @@ +fetchAll(\PDO::FETCH_ASSOC); diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/no_change_outside_method.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/no_change_outside_method.php.inc new file mode 100644 index 000000000..cd3e8028c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplacePdoFetchConstantsRector/fixture/no_change_outside_method.php.inc @@ -0,0 +1,23 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/ReplaceRecipeRunnerInstallModuleRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/ReplaceRecipeRunnerInstallModuleRectorTest.php new file mode 100644 index 000000000..d618dabe3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/ReplaceRecipeRunnerInstallModuleRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/config/configured_rule.php new file mode 100644 index 000000000..8a8c0f09f --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/basic.php.inc new file mode 100644 index 000000000..7b725fa0e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ + +----- + RecipeRunner::installModules([$module], $recipeConfigStorage, $context), fn() => RecipeRunner::installModule($module, $recipeConfigStorage, $context)); +RecipeRunner::installModules([$module], $recipeConfigStorage, $context); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/fqcn.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/fqcn.php.inc new file mode 100644 index 000000000..93cebfc21 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/fqcn.php.inc @@ -0,0 +1,11 @@ + +----- + \Drupal\Core\Recipe\RecipeRunner::installModules([$module], $recipeConfigStorage, $context), fn() => \Drupal\Core\Recipe\RecipeRunner::installModule($module, $recipeConfigStorage, $context)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/no_change_method_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/no_change_method_call.php.inc new file mode 100644 index 000000000..143d92971 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/no_change_method_call.php.inc @@ -0,0 +1,13 @@ +installModule($module, $storage, $context); + +?> +----- +installModule($module, $storage, $context); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..f7fe3e3df --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/self_static.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/self_static.php.inc new file mode 100644 index 000000000..d9c91d898 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceRecipeRunnerInstallModuleRector/fixture/self_static.php.inc @@ -0,0 +1,29 @@ + +----- + self::installModules([$module], $storage, $context), fn() => self::installModule($module, $storage, $context)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => static::installModules([$module], $storage, $context), fn() => static::installModule($module, $storage, $context)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/ReplaceSessionManagerDeleteRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/ReplaceSessionManagerDeleteRectorTest.php new file mode 100644 index 000000000..7e9bff552 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/ReplaceSessionManagerDeleteRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/config/configured_rule.php new file mode 100644 index 000000000..40c9952f6 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/config/configured_rule.php @@ -0,0 +1,14 @@ +delete($uid); +?> +----- +delete($uid); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture-below-version/class_property_phpdoc.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture-below-version/class_property_phpdoc.php.inc new file mode 100644 index 000000000..546904aff --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture-below-version/class_property_phpdoc.php.inc @@ -0,0 +1,51 @@ +sessionManager = $session_manager; + } + + public function deleteUserSessions(int $uid): void + { + $this->sessionManager->delete($uid); + } +} + +?> +----- +sessionManager = $session_manager; + } + + public function deleteUserSessions(int $uid): void + { + $this->sessionManager->delete($uid); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/basic.php.inc new file mode 100644 index 000000000..dac6e1931 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/basic.php.inc @@ -0,0 +1,11 @@ +delete($uid); +?> +----- + \Drupal::service(\Drupal\Core\Session\UserSessionRepositoryInterface::class)->deleteAll($uid), fn() => $session_manager->delete($uid)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/class_property.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/class_property.php.inc new file mode 100644 index 000000000..d8b756af3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/class_property.php.inc @@ -0,0 +1,35 @@ +sessionManager->delete($uid); + } +} + +?> +----- + \Drupal::service(\Drupal\Core\Session\UserSessionRepositoryInterface::class)->deleteAll($uid), fn() => $this->sessionManager->delete($uid)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/class_property_phpdoc.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/class_property_phpdoc.php.inc new file mode 100644 index 000000000..574501e10 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/class_property_phpdoc.php.inc @@ -0,0 +1,51 @@ +sessionManager = $session_manager; + } + + public function deleteUserSessions(int $uid): void + { + $this->sessionManager->delete($uid); + } +} + +?> +----- +sessionManager = $session_manager; + } + + public function deleteUserSessions(int $uid): void + { + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service(\Drupal\Core\Session\UserSessionRepositoryInterface::class)->deleteAll($uid), fn() => $this->sessionManager->delete($uid)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/interface_property.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/interface_property.php.inc new file mode 100644 index 000000000..925274fcf --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/interface_property.php.inc @@ -0,0 +1,35 @@ +sessionManager->delete($uid); + } +} + +?> +----- + \Drupal::service(\Drupal\Core\Session\UserSessionRepositoryInterface::class)->deleteAll($uid), fn() => $this->sessionManager->delete($uid)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/interface_variable.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/interface_variable.php.inc new file mode 100644 index 000000000..b2f0dd5d0 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/interface_variable.php.inc @@ -0,0 +1,13 @@ +delete($uid); + +?> +----- + \Drupal::service(\Drupal\Core\Session\UserSessionRepositoryInterface::class)->deleteAll($uid), fn() => $sessionManager->delete($uid)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..61393ec8a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionManagerDeleteRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,13 @@ +delete($uid); + +?> +----- +delete($uid); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/ReplaceSessionWritesWithRequestSessionRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/ReplaceSessionWritesWithRequestSessionRectorTest.php new file mode 100644 index 000000000..971539e44 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/ReplaceSessionWritesWithRequestSessionRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/config/configured_rule.php new file mode 100644 index 000000000..a57fc1466 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture-below-version/in_function.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture-below-version/in_function.php.inc new file mode 100644 index 000000000..ba54a969a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture-below-version/in_function.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/basic.php.inc new file mode 100644 index 000000000..0efaeb05b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::request()->getSession()->set('my_key', $value), fn() => $_SESSION['my_key'] = $value); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::request()->getSession()->set($dynamic_key, $other), fn() => $_SESSION[$dynamic_key] = $other); +$other_var['key'] = 'untouched'; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/in_function.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/in_function.php.inc new file mode 100644 index 000000000..1ee5f8041 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/in_function.php.inc @@ -0,0 +1,17 @@ + +----- + \Drupal::request()->getSession()->set($key, $value), fn() => $_SESSION[$key] = $value); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::request()->getSession()->set('prefix_' . $key, $value), fn() => $_SESSION['prefix_' . $key] = $value); +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/no_change_nested_and_clear.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/no_change_nested_and_clear.php.inc new file mode 100644 index 000000000..be6c0ea36 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/no_change_nested_and_clear.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/no_change_read_access.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/no_change_read_access.php.inc new file mode 100644 index 000000000..0be4038a2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSessionWritesWithRequestSessionRector/fixture/no_change_read_access.php.inc @@ -0,0 +1,17 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/ReplaceSystemPerformanceGzipKeyRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/ReplaceSystemPerformanceGzipKeyRectorTest.php new file mode 100644 index 000000000..ceb3d2322 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/ReplaceSystemPerformanceGzipKeyRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/config/configured_rule.php new file mode 100644 index 000000000..db21af6fd --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/config/configured_rule.php @@ -0,0 +1,14 @@ +get('css.gzip'); +$js = \Drupal::config('system.performance')->get('js.gzip'); +\Drupal::configFactory()->getEditable('system.performance')->set('css.gzip', TRUE); +// Unrelated: leave unchanged +$other = \Drupal::config('other.config')->get('css.gzip'); + +?> +----- +get('css.gzip'); +$js = \Drupal::config('system.performance')->get('js.gzip'); +\Drupal::configFactory()->getEditable('system.performance')->set('css.gzip', TRUE); +// Unrelated: leave unchanged +$other = \Drupal::config('other.config')->get('css.gzip'); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture-below-version/set_js_gzip.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture-below-version/set_js_gzip.php.inc new file mode 100644 index 000000000..952b88c24 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture-below-version/set_js_gzip.php.inc @@ -0,0 +1,13 @@ +getEditable('system.performance')->set('js.gzip', TRUE); +\Drupal::configFactory()->getEditable('system.performance')->set('js.gzip', FALSE); + +?> +----- +getEditable('system.performance')->set('js.gzip', TRUE); +\Drupal::configFactory()->getEditable('system.performance')->set('js.gzip', FALSE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture-below-version/this_config.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture-below-version/this_config.php.inc new file mode 100644 index 000000000..a9d6ef02d --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture-below-version/this_config.php.inc @@ -0,0 +1,15 @@ +config('system.performance')->get('css.gzip'); +$js = $this->config('system.performance')->get('js.gzip'); +$this->config('system.performance')->set('css.gzip', TRUE); + +?> +----- +config('system.performance')->get('css.gzip'); +$js = $this->config('system.performance')->get('js.gzip'); +$this->config('system.performance')->set('css.gzip', TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/basic.php.inc new file mode 100644 index 000000000..f24e49099 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/basic.php.inc @@ -0,0 +1,19 @@ +get('css.gzip'); +$js = \Drupal::config('system.performance')->get('js.gzip'); +\Drupal::configFactory()->getEditable('system.performance')->set('css.gzip', TRUE); +// Unrelated: leave unchanged +$other = \Drupal::config('other.config')->get('css.gzip'); + +?> +----- + \Drupal::config('system.performance')->get('css.compress'), fn() => \Drupal::config('system.performance')->get('css.gzip')); +$js = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::config('system.performance')->get('js.compress'), fn() => \Drupal::config('system.performance')->get('js.gzip')); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::configFactory()->getEditable('system.performance')->set('css.compress', TRUE), fn() => \Drupal::configFactory()->getEditable('system.performance')->set('css.gzip', TRUE)); +// Unrelated: leave unchanged +$other = \Drupal::config('other.config')->get('css.gzip'); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/no_change_unrelated_keys.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/no_change_unrelated_keys.php.inc new file mode 100644 index 000000000..952d49512 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/no_change_unrelated_keys.php.inc @@ -0,0 +1,21 @@ +get('gzip'); +\Drupal::config('system.performance')->get('css'); +\Drupal::config('system.performance')->get('css.preprocess'); +// Variable key — String_ guard prevents transformation. +\Drupal::config('system.performance')->get($key); + +?> +----- +get('gzip'); +\Drupal::config('system.performance')->get('css'); +\Drupal::config('system.performance')->get('css.preprocess'); +// Variable key — String_ guard prevents transformation. +\Drupal::config('system.performance')->get($key); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/set_js_gzip.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/set_js_gzip.php.inc new file mode 100644 index 000000000..b128bbcb5 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/set_js_gzip.php.inc @@ -0,0 +1,13 @@ +getEditable('system.performance')->set('js.gzip', TRUE); +\Drupal::configFactory()->getEditable('system.performance')->set('js.gzip', FALSE); + +?> +----- + \Drupal::configFactory()->getEditable('system.performance')->set('js.compress', TRUE), fn() => \Drupal::configFactory()->getEditable('system.performance')->set('js.gzip', TRUE)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::configFactory()->getEditable('system.performance')->set('js.compress', FALSE), fn() => \Drupal::configFactory()->getEditable('system.performance')->set('js.gzip', FALSE)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/this_config.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/this_config.php.inc new file mode 100644 index 000000000..1e0808cea --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceSystemPerformanceGzipKeyRector/fixture/this_config.php.inc @@ -0,0 +1,15 @@ +config('system.performance')->get('css.gzip'); +$js = $this->config('system.performance')->get('js.gzip'); +$this->config('system.performance')->set('css.gzip', TRUE); + +?> +----- + $this->config('system.performance')->get('css.compress'), fn() => $this->config('system.performance')->get('css.gzip')); +$js = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $this->config('system.performance')->get('js.compress'), fn() => $this->config('system.performance')->get('js.gzip')); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $this->config('system.performance')->set('css.compress', TRUE), fn() => $this->config('system.performance')->set('css.gzip', TRUE)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/ReplaceThemeGetSettingRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/ReplaceThemeGetSettingRectorTest.php new file mode 100644 index 000000000..29276dd50 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/ReplaceThemeGetSettingRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/config/configured_rule.php new file mode 100644 index 000000000..a01db0678 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/basic.php.inc new file mode 100644 index 000000000..08304351b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting('logo.url'), fn() => theme_get_setting('logo.url')); +$fav = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting('favicon.url', 'claro'), fn() => theme_get_setting('favicon.url', 'claro')); +$features = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\Core\Extension\ThemeSettingsProvider::DEFAULT_THEME_FEATURES, fn() => _system_default_theme_features()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/fqcn_prefix.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/fqcn_prefix.php.inc new file mode 100644 index 000000000..297b6d505 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/fqcn_prefix.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting('logo.url'), fn() => \theme_get_setting('logo.url')); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\Core\Extension\ThemeSettingsProvider::DEFAULT_THEME_FEATURES, fn() => \_system_default_theme_features()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/inline_usage.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/inline_usage.php.inc new file mode 100644 index 000000000..660959374 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/inline_usage.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting('logo.use_default'), fn() => theme_get_setting('logo.use_default'))) { + $url = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting('logo.url', $theme_name), fn() => theme_get_setting('logo.url', $theme_name)); +} + +$features = array_keys(\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\Core\Extension\ThemeSettingsProvider::DEFAULT_THEME_FEATURES, fn() => _system_default_theme_features())); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/variable_key.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/variable_key.php.inc new file mode 100644 index 000000000..69493f42e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceThemeGetSettingRector/fixture/variable_key.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting($setting_name), fn() => theme_get_setting($setting_name)); +$setting = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service(\Drupal\Core\Extension\ThemeSettingsProvider::class)->getSetting($setting_name, $theme), fn() => theme_get_setting($setting_name, $theme)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/ReplaceTwigExtensionRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/ReplaceTwigExtensionRectorTest.php new file mode 100644 index 000000000..de3b06443 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/ReplaceTwigExtensionRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/config/configured_rule.php new file mode 100644 index 000000000..c8c72ec89 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/fixture/basic.php.inc new file mode 100644 index 000000000..9d93c74b9 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceTwigExtensionRector/fixture/basic.php.inc @@ -0,0 +1,9 @@ + +----- + '.html.twig', fn() => twig_extension()); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/ReplaceUserSessionNamePropertyRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/ReplaceUserSessionNamePropertyRectorTest.php new file mode 100644 index 000000000..49aa1a26e --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/ReplaceUserSessionNamePropertyRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/config/configured_rule.php new file mode 100644 index 000000000..6784a56f3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/config/configured_rule.php @@ -0,0 +1,14 @@ +name; +$other = $untyped->name; + +?> +----- +name; +$other = $untyped->name; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/basic.php.inc new file mode 100644 index 000000000..e2197f019 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/basic.php.inc @@ -0,0 +1,15 @@ +name; +$other = $untyped->name; + +?> +----- + $userSession->getAccountName(), fn() => $userSession->name); +$other = $untyped->name; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/inline_usage.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/inline_usage.php.inc new file mode 100644 index 000000000..868c169f8 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/inline_usage.php.inc @@ -0,0 +1,19 @@ +name === 'admin') { + return $session->name; +} +$label = 'Hello ' . $session->name; + +?> +----- + $session->getAccountName(), fn() => $session->name) === 'admin') { + return \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $session->getAccountName(), fn() => $session->name); +} +$label = 'Hello ' . \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $session->getAccountName(), fn() => $session->name); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/no_change_this.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/no_change_this.php.inc new file mode 100644 index 000000000..ec7a760d4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/no_change_this.php.inc @@ -0,0 +1,31 @@ +name inside the class is not transformed — would cause + // infinite recursion if rewritten to $this->getAccountName(). + return $this->name; + } +} + +?> +----- +name inside the class is not transformed — would cause + // infinite recursion if rewritten to $this->getAccountName(). + return $this->name; + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/nullsafe.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/nullsafe.php.inc new file mode 100644 index 000000000..d05bbe096 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceUserSessionNamePropertyRector/fixture/nullsafe.php.inc @@ -0,0 +1,17 @@ +name) is a different node type from +// PropertyFetch — not handled by this rector. +/** @var \Drupal\Core\Session\UserSession|null $session */ +$name = $session?->name; + +?> +----- +name) is a different node type from +// PropertyFetch — not handled by this rector. +/** @var \Drupal\Core\Session\UserSession|null $session */ +$name = $session?->name; + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/ReplaceViewsProceduralFunctionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/ReplaceViewsProceduralFunctionsRectorTest.php new file mode 100644 index 000000000..ea24c6c8c --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/ReplaceViewsProceduralFunctionsRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('11.3.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/config/configured_rule.php new file mode 100644 index 000000000..4f7e21ceb --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..659d67a1b --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/basic.php.inc @@ -0,0 +1,19 @@ + +----- + $view->status(), fn() => views_view_is_enabled($view)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => !$view->status(), fn() => views_view_is_disabled($view)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $view->enable()->save(), fn() => views_enable_view($view)); +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => $view->disable()->save(), fn() => views_disable_view($view)); +$result = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\views\Views::getViewResult('my_view', 'page_1', 'arg1'), fn() => views_get_view_result('my_view', 'page_1', 'arg1')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/expression_positions.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/expression_positions.php.inc new file mode 100644 index 000000000..3192f9b76 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/expression_positions.php.inc @@ -0,0 +1,19 @@ + +----- + $view->status(), fn() => views_view_is_enabled($view))) { + $label = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => !$view->status(), fn() => views_view_is_disabled($view)) ? 'disabled' : 'enabled'; +} +$result = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\views\Views::getViewResult('frontpage'), fn() => views_get_view_result('frontpage')); +$result2 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\views\Views::getViewResult('archive', 'block_1'), fn() => views_get_view_result('archive', 'block_1')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/no_change_no_args.php.inc b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/no_change_no_args.php.inc new file mode 100644 index 000000000..569e6a8f2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ReplaceViewsProceduralFunctionsRector/fixture/no_change_no_args.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/StatementPrefetchIteratorFetchColumnRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/StatementPrefetchIteratorFetchColumnRectorTest.php new file mode 100644 index 000000000..1eccee390 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/StatementPrefetchIteratorFetchColumnRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/config/configured_rule.php new file mode 100644 index 000000000..b6e74363a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/config/configured_rule.php @@ -0,0 +1,14 @@ +fetchColumn(0); + $other = $this->clientStatement->fetchColumn(0); + } +} +?> +----- +fetchColumn(0); + $other = $this->clientStatement->fetchColumn(0); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/basic.php.inc new file mode 100644 index 000000000..e7f066e04 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/basic.php.inc @@ -0,0 +1,23 @@ +fetchColumn(0); + $other = $this->clientStatement->fetchColumn(0); + } +} +?> +----- + $statement->fetchField(0), fn() => $statement->fetchColumn(0)); + $other = $this->clientStatement->fetchColumn(0); + } +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/chained_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/chained_call.php.inc new file mode 100644 index 000000000..c017e79c7 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/chained_call.php.inc @@ -0,0 +1,6 @@ +query('SELECT name FROM {users}')->fetchColumn(); +$id = $connection->select('node', 'n')->fetchColumn(1); diff --git a/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/no_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/no_arg.php.inc new file mode 100644 index 000000000..022ef14b6 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/no_arg.php.inc @@ -0,0 +1,15 @@ +fetchColumn(); + +?> +----- + $statement->fetchField(), fn() => $statement->fetchColumn()); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/property_not_client_statement.php.inc b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/property_not_client_statement.php.inc new file mode 100644 index 000000000..b218a8714 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StatementPrefetchIteratorFetchColumnRector/fixture/property_not_client_statement.php.inc @@ -0,0 +1,33 @@ +statement->fetchColumn(0); + $result2 = $this->stmt->fetchColumn(2); + } +} + +?> +----- + $this->statement->fetchField(0), fn() => $this->statement->fetchColumn(0)); + $result2 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $this->stmt->fetchField(2), fn() => $this->stmt->fetchColumn(2)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/StripMigrationDependenciesExpandArgRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/StripMigrationDependenciesExpandArgRectorTest.php new file mode 100644 index 000000000..9a4f086ac --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/StripMigrationDependenciesExpandArgRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/config/configured_rule.php new file mode 100644 index 000000000..1d539e090 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getMigrationDependencies(TRUE); +$noop = $migration->getMigrationDependencies(); +$untyped = $other->getMigrationDependencies(TRUE); + +?> +----- +getMigrationDependencies(TRUE); +$noop = $migration->getMigrationDependencies(); +$untyped = $other->getMigrationDependencies(TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture-below-version/false_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture-below-version/false_arg.php.inc new file mode 100644 index 000000000..69f65d00a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture-below-version/false_arg.php.inc @@ -0,0 +1,15 @@ +getMigrationDependencies(FALSE); + +?> +----- +getMigrationDependencies(FALSE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/basic.php.inc new file mode 100644 index 000000000..780bae4a3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/basic.php.inc @@ -0,0 +1,17 @@ +getMigrationDependencies(TRUE); +$noop = $migration->getMigrationDependencies(); +$untyped = $other->getMigrationDependencies(TRUE); + +?> +----- + $migration->getMigrationDependencies(), fn() => $migration->getMigrationDependencies(TRUE)); +$noop = $migration->getMigrationDependencies(); +$untyped = $other->getMigrationDependencies(TRUE); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/false_arg.php.inc b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/false_arg.php.inc new file mode 100644 index 000000000..b2a70fa23 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/false_arg.php.inc @@ -0,0 +1,15 @@ +getMigrationDependencies(FALSE); + +?> +----- + $migration->getMigrationDependencies(), fn() => $migration->getMigrationDependencies(FALSE)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/this_caller.php.inc b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/this_caller.php.inc new file mode 100644 index 000000000..b23759708 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/StripMigrationDependenciesExpandArgRector/fixture/this_caller.php.inc @@ -0,0 +1,25 @@ +getMigrationDependencies(TRUE); + } +} + +?> +----- +getMigrationDependencies(TRUE); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/SystemRegionFunctionsRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/SystemRegionFunctionsRectorTest.php new file mode 100644 index 000000000..cceabdbb6 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/SystemRegionFunctionsRectorTest.php @@ -0,0 +1,42 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** @return \Iterator> */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/config/configured_rule.php new file mode 100644 index 000000000..eb46ef3e4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/fixture/basic.php.inc new file mode 100644 index 000000000..5115e08fe --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/fixture/basic.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal::service('theme_handler')->getTheme($theme)->listAllRegions(), fn() => system_region_list($theme)); +$all_explicit = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('theme_handler')->getTheme($theme)->listAllRegions(), fn() => system_region_list($theme, REGIONS_ALL)); +$visible = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('theme_handler')->getTheme($theme)->listVisibleRegions(), fn() => system_region_list($theme, REGIONS_VISIBLE)); +$visible_class_const = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('theme_handler')->getTheme($theme)->listVisibleRegions(), fn() => system_region_list($theme, \Drupal\block\BlockRepositoryInterface::REGIONS_VISIBLE)); +$visible_string = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('theme_handler')->getTheme($theme)->listVisibleRegions(), fn() => system_region_list($theme, 'visible')); +$default = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('theme_handler')->getTheme($theme)->getDefaultRegion(), fn() => system_default_region($theme)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..5a05595e8 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemRegionFunctionsRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/SystemSortThemesRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/SystemSortThemesRectorTest.php new file mode 100644 index 000000000..3c02529df --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/SystemSortThemesRectorTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/config/configured_rule.php new file mode 100644 index 000000000..8516871b1 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/config/configured_rule.php @@ -0,0 +1,11 @@ + +----- +is_default) { + return -1; + } + if ($b->is_default) { + return 1; + } + return strcasecmp($a->info['name'], $b->info['name']); + }); +} +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/fixture/no_change_unrelated.php.inc b/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/fixture/no_change_unrelated.php.inc new file mode 100644 index 000000000..706de9f53 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/SystemSortThemesRector/fixture/no_change_unrelated.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/UseEntityTypeHasIntegerIdRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/UseEntityTypeHasIntegerIdRectorTest.php new file mode 100644 index 000000000..4b235dc89 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/UseEntityTypeHasIntegerIdRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/config/configured_rule.php new file mode 100644 index 000000000..a0eb8e7b3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/config/configured_rule.php @@ -0,0 +1,14 @@ +getEntityTypeIdKeyType($entity_type) === 'integer') { + $result = 'integer'; + } + } +} + +?> +----- +getEntityTypeIdKeyType($entity_type) === 'integer') { + $result = 'integer'; + } + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/basic.php.inc new file mode 100644 index 000000000..ac8d9ce45 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/basic.php.inc @@ -0,0 +1,27 @@ +getEntityTypeIdKeyType($entity_type) === 'integer') { + $result = 'integer'; + } + } +} + +?> +----- + $entity_type->hasIntegerId(), fn() => $this->getEntityTypeIdKeyType($entity_type) === 'integer')) { + $result = 'integer'; + } + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/entity_type_supports_comments.php.inc b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/entity_type_supports_comments.php.inc new file mode 100644 index 000000000..afa620be2 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/entity_type_supports_comments.php.inc @@ -0,0 +1,23 @@ +entityTypeSupportsComments($entity_type); + } +} + +?> +----- + $entity_type->hasIntegerId(), fn() => $this->entityTypeSupportsComments($entity_type)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/has_integer_id_helper.php.inc b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/has_integer_id_helper.php.inc new file mode 100644 index 000000000..8a6b5db82 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/has_integer_id_helper.php.inc @@ -0,0 +1,23 @@ +hasIntegerId($entity_type); + } +} + +?> +----- + $entity_type->hasIntegerId(), fn() => $this->hasIntegerId($entity_type)); + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/no_change.php.inc b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/no_change.php.inc new file mode 100644 index 000000000..46370aeb6 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/no_change.php.inc @@ -0,0 +1,20 @@ +getEntityTypeIdKeyType($entity_type) === 'string') { + $result = 'string'; + } + } +} + +// Not called on $this: no transformation. +if ($other->getEntityTypeIdKeyType($entity_type) === 'integer') { + $result = 'other'; +} + +// entityTypeSupportsComments without $this: no transformation. +$a = $other->entityTypeSupportsComments($entity_type); diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/no_change_unrelated_class.php.inc b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/no_change_unrelated_class.php.inc new file mode 100644 index 000000000..6fa444196 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/no_change_unrelated_class.php.inc @@ -0,0 +1,10 @@ +entityTypeSupportsComments($entity_type); + $b = $this->hasIntegerId($entity_type); + if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {} + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/string_reversed.php.inc b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/string_reversed.php.inc new file mode 100644 index 000000000..fc1101704 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/UseEntityTypeHasIntegerIdRector/fixture/string_reversed.php.inc @@ -0,0 +1,29 @@ +getEntityTypeIdKeyType($entity_type)) { + $result = 'integer'; + } + } +} + +?> +----- + $entity_type->hasIntegerId(), fn() => 'integer' === $this->getEntityTypeIdKeyType($entity_type))) { + $result = 'integer'; + } + } +} + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/ViewsPluginHandlerManagerRectorTest.php b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/ViewsPluginHandlerManagerRectorTest.php new file mode 100644 index 000000000..d2971c106 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/ViewsPluginHandlerManagerRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/config/configured_rule.php b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/config/configured_rule.php new file mode 100644 index 000000000..3c18486a3 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/basic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/basic.php.inc new file mode 100644 index 000000000..606110174 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/basic.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal::service('plugin.manager.views.filter'), fn() => Views::handlerManager('filter')); +$displayManager = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('plugin.manager.views.display'), fn() => Views::pluginManager('display')); +$type = 'sort'; +$manager = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('views.plugin_managers')->get($type), fn() => Views::pluginManager($type)); +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/chained_call.php.inc b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/chained_call.php.inc new file mode 100644 index 000000000..64795770a --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/chained_call.php.inc @@ -0,0 +1,23 @@ +getHandler($options, $id); + +// pluginManager result used in a method chain. +$instance = Views::pluginManager('display')->createInstance($id); + +?> +----- + \Drupal::service('plugin.manager.views.field'), fn() => Views::handlerManager('field'))->getHandler($options, $id); + +// pluginManager result used in a method chain. +$instance = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('plugin.manager.views.display'), fn() => Views::pluginManager('display'))->createInstance($id); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/fqcn_prefix.php.inc b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/fqcn_prefix.php.inc new file mode 100644 index 000000000..e7ebded76 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/fqcn_prefix.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('plugin.manager.views.filter'), fn() => \Drupal\views\Views::handlerManager('filter')); +$displayManager = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('plugin.manager.views.display'), fn() => \Drupal\views\Views::pluginManager('display')); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/handler_manager_dynamic.php.inc b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/handler_manager_dynamic.php.inc new file mode 100644 index 000000000..32bb4a0a4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/handler_manager_dynamic.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal::service('views.plugin_managers')->get($type), fn() => Views::handlerManager($type)); + +?> diff --git a/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/no_change_no_args.php.inc b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/no_change_no_args.php.inc new file mode 100644 index 000000000..fa8dc4dc4 --- /dev/null +++ b/tests/src/Drupal11/Rector/Deprecation/ViewsPluginHandlerManagerRector/fixture/no_change_no_args.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Drupal8/Rector/Deprecation/DrupalSetMessageRector/DrupalSetMessageRectorTest.php b/tests/src/Drupal8/Rector/Deprecation/DrupalSetMessageRector/DrupalSetMessageRectorTest.php index f1e76f52d..aa3f512a4 100644 --- a/tests/src/Drupal8/Rector/Deprecation/DrupalSetMessageRector/DrupalSetMessageRectorTest.php +++ b/tests/src/Drupal8/Rector/Deprecation/DrupalSetMessageRector/DrupalSetMessageRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal8\Rector\Deprecation\DrupalSetMessageRector; +namespace DrupalRector\Tests\Drupal8\Rector\Deprecation\DrupalSetMessageRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class DrupalSetMessageRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class DrupalSetMessageRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal8/Rector/Deprecation/EntityDeleteMultipleRector/EntityDeleteMultipleRectorTest.php b/tests/src/Drupal8/Rector/Deprecation/EntityDeleteMultipleRector/EntityDeleteMultipleRectorTest.php index ee51d2116..24ea512cc 100644 --- a/tests/src/Drupal8/Rector/Deprecation/EntityDeleteMultipleRector/EntityDeleteMultipleRectorTest.php +++ b/tests/src/Drupal8/Rector/Deprecation/EntityDeleteMultipleRector/EntityDeleteMultipleRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal8\Rector\Deprecation\EntityDeleteMultipleRector; +namespace DrupalRector\Tests\Drupal8\Rector\Deprecation\EntityDeleteMultipleRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class EntityDeleteMultipleRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class EntityDeleteMultipleRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal8/Rector/Deprecation/EntityManagerRector/EntityManagerRectorTest.php b/tests/src/Drupal8/Rector/Deprecation/EntityManagerRector/EntityManagerRectorTest.php index 7e85bc4ff..03078f0ae 100644 --- a/tests/src/Drupal8/Rector/Deprecation/EntityManagerRector/EntityManagerRectorTest.php +++ b/tests/src/Drupal8/Rector/Deprecation/EntityManagerRector/EntityManagerRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal8\Rector\Deprecation\EntityManagerRector; +namespace DrupalRector\Tests\Drupal8\Rector\Deprecation\EntityManagerRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class EntityManagerRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class EntityManagerRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal8/Rector/Deprecation/EntityViewRector/EntityViewRectorTest.php b/tests/src/Drupal8/Rector/Deprecation/EntityViewRector/EntityViewRectorTest.php index 2f5806d91..690f24e51 100644 --- a/tests/src/Drupal8/Rector/Deprecation/EntityViewRector/EntityViewRectorTest.php +++ b/tests/src/Drupal8/Rector/Deprecation/EntityViewRector/EntityViewRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal8\Rector\Deprecation\EntityViewRector; +namespace DrupalRector\Tests\Drupal8\Rector\Deprecation\EntityViewRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class EntityViewRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class EntityViewRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector/FunctionalTestDefaultThemePropertyRectorTest.php b/tests/src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector/FunctionalTestDefaultThemePropertyRectorTest.php index 0bf8cea90..9b0d579a3 100644 --- a/tests/src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector/FunctionalTestDefaultThemePropertyRectorTest.php +++ b/tests/src/Drupal8/Rector/Deprecation/FunctionalTestDefaultThemePropertyRector/FunctionalTestDefaultThemePropertyRectorTest.php @@ -4,14 +4,12 @@ namespace DrupalRector\Tests\Drupal8\Rector\Deprecation\FunctionalTestDefaultThemePropertyRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use DrupalRector\Tests\Rector\Class_\FunctionalTestDefaultThemePropertyRector\Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class FunctionalTestDefaultThemePropertyRectorTest extends AbstractRectorTestCase +final class FunctionalTestDefaultThemePropertyRectorTest extends AbstractDrupalRectorTestCase { - /** - * @dataProvider provideData() - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal8/Rector/Deprecation/GetMockRector/GetMockRectorTest.php b/tests/src/Drupal8/Rector/Deprecation/GetMockRector/GetMockRectorTest.php index a3e89fd77..f48485909 100644 --- a/tests/src/Drupal8/Rector/Deprecation/GetMockRector/GetMockRectorTest.php +++ b/tests/src/Drupal8/Rector/Deprecation/GetMockRector/GetMockRectorTest.php @@ -2,16 +2,14 @@ declare(strict_types=1); -namespace Drupal8\Rector\Deprecation\GetMockRector; +namespace DrupalRector\Tests\Drupal8\Rector\Deprecation\GetMockRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class GetMockRectorTest extends AbstractRectorTestCase +class GetMockRectorTest extends AbstractDrupalRectorTestCase { - /** - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertFieldByIdRector/AssertFieldByIdRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/AssertFieldByIdRector/AssertFieldByIdRectorTest.php index 7ef7ff76d..589c68877 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertFieldByIdRector/AssertFieldByIdRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/AssertFieldByIdRector/AssertFieldByIdRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\AssertFieldByIdRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class AssertFieldByIdRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class AssertFieldByIdRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertFieldByNameRector/AssertFieldByNameRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/AssertFieldByNameRector/AssertFieldByNameRectorTest.php index bdcc9448c..a695696c3 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertFieldByNameRector/AssertFieldByNameRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/AssertFieldByNameRector/AssertFieldByNameRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\AssertFieldByNameRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class AssertFieldByNameRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class AssertFieldByNameRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertLegacyTraitRector/AssertLegacyTraitRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/AssertLegacyTraitRector/AssertLegacyTraitRectorTest.php index 8a01cbe64..fac69eaf8 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertLegacyTraitRector/AssertLegacyTraitRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/AssertLegacyTraitRector/AssertLegacyTraitRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\AssertLegacyTraitRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\AssertLegacyTraitRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class AssertLegacyTraitRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class AssertLegacyTraitRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertNoFieldByNameRector/AssertNoFieldByNameRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/AssertNoFieldByNameRector/AssertNoFieldByNameRectorTest.php index 698194cfb..cc8c7ed42 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertNoFieldByNameRector/AssertNoFieldByNameRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/AssertNoFieldByNameRector/AssertNoFieldByNameRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\AssertNoFieldByNameRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\AssertNoFieldByNameRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class AssertNoFieldByNameRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class AssertNoFieldByNameRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/VersionedFunctionToServiceRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/AssertNoUniqueTextRectorTest.php similarity index 59% rename from tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/VersionedFunctionToServiceRectorTest.php rename to tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/AssertNoUniqueTextRectorTest.php index 930edc550..13c9b29ad 100644 --- a/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/VersionedFunctionToServiceRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/AssertNoUniqueTextRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace DrupalRector\Tests\Rector\Deprecation\VersionedFunctionToServiceRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\AssertNoUniqueTextRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class VersionedFunctionToServiceRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class AssertNoUniqueTextRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text.php.inc b/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text.php.inc index 47e87c475..c19d6bb56 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text.php.inc +++ b/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text.php.inc @@ -22,7 +22,8 @@ class BrowserTestBaseGetMock extends BrowserTestBase { /** * A simple example using the class property. */ - public function example() { + public function example() + { $page_text = $this->getSession()->getPage()->getText(); $nr_found = substr_count($page_text, 'Duplicated message'); $this->assertGreaterThan(1, $nr_found, "'Duplicated message' found more than once on the page"); diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text_by_var.php.inc b/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text_by_var.php.inc index 301b3f87b..a8b8e5980 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text_by_var.php.inc +++ b/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/fixture/assert_no_unique_text_by_var.php.inc @@ -20,6 +20,10 @@ class BrowserTestBaseGetMock extends BrowserTestBase { ----- doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/FileFunctionRector/FileFunctionRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/FileFunctionRector/FileFunctionRectorTest.php index 3213371c9..61b6bed57 100644 --- a/tests/src/Drupal9/Rector/Deprecation/FileFunctionRector/FileFunctionRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/FileFunctionRector/FileFunctionRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\FileFunctionRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\FileFunctionRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class FileFunctionRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class FileFunctionRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/FileUrlGeneratorRector/FileUrlGeneratorRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/FileUrlGeneratorRector/FileUrlGeneratorRectorTest.php index abe65fc3c..c211a962d 100644 --- a/tests/src/Drupal9/Rector/Deprecation/FileUrlGeneratorRector/FileUrlGeneratorRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/FileUrlGeneratorRector/FileUrlGeneratorRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\FileUrlGeneratorRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\FileUrlGeneratorRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class FileUrlGeneratorRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class FileUrlGeneratorRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/ModuleLoadRector/ModuleLoadRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/ModuleLoadRector/ModuleLoadRectorTest.php index cebd2d9c5..66ca93efb 100644 --- a/tests/src/Drupal9/Rector/Deprecation/ModuleLoadRector/ModuleLoadRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/ModuleLoadRector/ModuleLoadRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\ModuleLoadRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\ModuleLoadRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class ModuleLoadRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class ModuleLoadRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/PassRector/PassRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/PassRector/PassRectorTest.php index 7060d2995..13748ec6a 100644 --- a/tests/src/Drupal9/Rector/Deprecation/PassRector/PassRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/PassRector/PassRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\PassRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\PassRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class PassRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class PassRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { if (str_contains($filePath, 'skip') && method_exists($this, 'doTestFileExpectingWarningAboutRuleApplied')) { diff --git a/tests/src/Drupal9/Rector/Deprecation/SystemSortByInfoName/SystemSortByInfoNameRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/SystemSortByInfoName/SystemSortByInfoNameRectorTest.php index f9c53df23..51cf1cd63 100644 --- a/tests/src/Drupal9/Rector/Deprecation/SystemSortByInfoName/SystemSortByInfoNameRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/SystemSortByInfoName/SystemSortByInfoNameRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\SystemSortByInfoName; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\SystemSortByInfoName; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class SystemSortByInfoNameRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class SystemSortByInfoNameRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/TaxonomyRectorCollection/TaxonomyRectorCollectionTest.php b/tests/src/Drupal9/Rector/Deprecation/TaxonomyRectorCollection/TaxonomyRectorCollectionTest.php index 6d8281d56..363ed2b77 100644 --- a/tests/src/Drupal9/Rector/Deprecation/TaxonomyRectorCollection/TaxonomyRectorCollectionTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/TaxonomyRectorCollection/TaxonomyRectorCollectionTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\TaxonomyRectorCollection; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\TaxonomyRectorCollection; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class TaxonomyRectorCollectionTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class TaxonomyRectorCollectionTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/UiHelperTraitDrupalPostFormRector/UiHelperTraitDrupalPostFormRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/UiHelperTraitDrupalPostFormRector/UiHelperTraitDrupalPostFormRectorTest.php index 35acf6c2d..22a851202 100644 --- a/tests/src/Drupal9/Rector/Deprecation/UiHelperTraitDrupalPostFormRector/UiHelperTraitDrupalPostFormRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/UiHelperTraitDrupalPostFormRector/UiHelperTraitDrupalPostFormRectorTest.php @@ -2,18 +2,15 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\UiHelperTraitDrupalPostFormRector; +namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\UiHelperTraitDrupalPostFormRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class UiHelperTraitDrupalPostFormRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class UiHelperTraitDrupalPostFormRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Deprecation/UserPasswordRector/UserPasswordRectorTest.php b/tests/src/Drupal9/Rector/Deprecation/UserPasswordRector/UserPasswordRectorTest.php index b0d894b74..ba9aa404d 100644 --- a/tests/src/Drupal9/Rector/Deprecation/UserPasswordRector/UserPasswordRectorTest.php +++ b/tests/src/Drupal9/Rector/Deprecation/UserPasswordRector/UserPasswordRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Drupal9\Rector\Deprecation\UserPasswordRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class UserPasswordRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class UserPasswordRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Drupal9/Rector/Property/ProtectedStaticModulesPropertyRector/ProtectedStaticModulesPropertyRectorTest.php b/tests/src/Drupal9/Rector/Property/ProtectedStaticModulesPropertyRector/ProtectedStaticModulesPropertyRectorTest.php index dfda547aa..d32475692 100644 --- a/tests/src/Drupal9/Rector/Property/ProtectedStaticModulesPropertyRector/ProtectedStaticModulesPropertyRectorTest.php +++ b/tests/src/Drupal9/Rector/Property/ProtectedStaticModulesPropertyRector/ProtectedStaticModulesPropertyRectorTest.php @@ -4,14 +4,12 @@ namespace DrupalRector\Tests\Drupal9\Rector\Property\ProtectedStaticModulesPropertyRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class ProtectedStaticModulesPropertyRectorTest extends AbstractRectorTestCase +final class ProtectedStaticModulesPropertyRectorTest extends AbstractDrupalRectorTestCase { - /** - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Rector/AbstractDrupalCoreRector/AbstractDrupalCoreRectorBcTest.php b/tests/src/Rector/AbstractDrupalCoreRector/AbstractDrupalCoreRectorBcTest.php new file mode 100644 index 000000000..f23331b47 --- /dev/null +++ b/tests/src/Rector/AbstractDrupalCoreRector/AbstractDrupalCoreRectorBcTest.php @@ -0,0 +1,30 @@ +doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-bc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule_bc.php'; + } +} diff --git a/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/AssertNoUniqueTextRector.php b/tests/src/Rector/AbstractDrupalCoreRector/AbstractDrupalCoreRectorTest.php similarity index 65% rename from tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/AssertNoUniqueTextRector.php rename to tests/src/Rector/AbstractDrupalCoreRector/AbstractDrupalCoreRectorTest.php index 38783eb9b..8bc5a0abb 100644 --- a/tests/src/Drupal9/Rector/Deprecation/AssertNoUniqueTextRector/AssertNoUniqueTextRector.php +++ b/tests/src/Rector/AbstractDrupalCoreRector/AbstractDrupalCoreRectorTest.php @@ -2,18 +2,14 @@ declare(strict_types=1); -namespace Drupal9\Rector\Deprecation\AssertNoUniqueTextRector; +namespace DrupalRector\Tests\Rector\AbstractDrupalCoreRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class AssertNoUniqueTextRector extends AbstractRectorTestCase +class AbstractDrupalCoreRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Rector/AbstractDrupalCoreRector/Stub/ClassConstFetchBCRector.php b/tests/src/Rector/AbstractDrupalCoreRector/Stub/ClassConstFetchBCRector.php new file mode 100644 index 000000000..6eadcb151 --- /dev/null +++ b/tests/src/Rector/AbstractDrupalCoreRector/Stub/ClassConstFetchBCRector.php @@ -0,0 +1,89 @@ +class instanceof Node\Name) { + return null; + } + + if ($this->getName($node->class) !== 'OldClass') { + return null; + } + + if (!$node->name instanceof Node\Identifier || $node->name->toString() !== 'OLD_CONST') { + return null; + } + + return new Node\Expr\ClassConstFetch( + new Node\Name\FullyQualified('NewClass'), + new Node\Identifier('NEW_CONST') + ); + } + + /** + * {@inheritdoc} + */ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Test stub: replaces OldClass::OLD_CONST with \\NewClass::NEW_CONST, exercising the Expr BC-wrap path.', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +$value = OldClass::OLD_CONST; +CODE_BEFORE, + <<<'CODE_AFTER' +$value = \NewClass::NEW_CONST; +CODE_AFTER, + [ + new DrupalIntroducedVersionConfiguration('10.1.0'), + ] + ), + ]); + } +} diff --git a/tests/src/Rector/AbstractDrupalCoreRector/config/configured_rule.php b/tests/src/Rector/AbstractDrupalCoreRector/config/configured_rule.php new file mode 100644 index 000000000..11023ec7d --- /dev/null +++ b/tests/src/Rector/AbstractDrupalCoreRector/config/configured_rule.php @@ -0,0 +1,14 @@ +singleton(DrupalRectorSettings::class, fn () => (new DrupalRectorSettings())->setMinimumCoreVersionSupported('10.1.0')); + + $rectorConfig->ruleWithConfiguration(ClassConstFetchBCRector::class, [ + new DrupalIntroducedVersionConfiguration('11.0.0'), + ]); +}; diff --git a/tests/src/Rector/AbstractDrupalCoreRector/fixture-bc/class_const_fetch.php.inc b/tests/src/Rector/AbstractDrupalCoreRector/fixture-bc/class_const_fetch.php.inc new file mode 100644 index 000000000..509e38baf --- /dev/null +++ b/tests/src/Rector/AbstractDrupalCoreRector/fixture-bc/class_const_fetch.php.inc @@ -0,0 +1,13 @@ + +----- + \NewClass::NEW_CONST, fn() => OldClass::OLD_CONST); +} +?> diff --git a/tests/src/Rector/AbstractDrupalCoreRector/fixture/class_const_fetch.php.inc b/tests/src/Rector/AbstractDrupalCoreRector/fixture/class_const_fetch.php.inc new file mode 100644 index 000000000..a5e55aeb3 --- /dev/null +++ b/tests/src/Rector/AbstractDrupalCoreRector/fixture/class_const_fetch.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/AbstractDrupalCoreRectorTest.php b/tests/src/Rector/AbstractDrupalCoreRectorTest.php new file mode 100644 index 000000000..fb5ad1401 --- /dev/null +++ b/tests/src/Rector/AbstractDrupalCoreRectorTest.php @@ -0,0 +1,93 @@ +enableBackwardCompatibility() : $settings->disableBackwardCompatibility(); + $settings->setMinimumCoreVersionSupported($minimumVersion); + + $rector = self::makeRector($settings); + $configuration = self::makeConfiguration($introducedVersion); + + self::assertSame($expected, $rector->supportBackwardsCompatibility($configuration)); + } + + /** + * @return \Iterator + */ + public static function provideSupportBackwardsCompatibility(): \Iterator + { + // BC disabled wins regardless of versions. + yield 'bc disabled returns false' => [false, '10.1.0', '10.2.0', false]; + + // Minimum supported version below the BC-eligible floor (10.1.0). + yield 'minimum 10.0.0 below floor' => [true, '10.0.0', '10.2.0', false]; + yield 'minimum 9.5.0 below floor' => [true, '9.5.0', '10.2.0', false]; + + // Introduced version below 10.0.0 — no BC wrappers for pre-10 deprecations. + yield 'introduced 9.5.0 too old' => [true, '10.1.0', '9.5.0', false]; + + // Project minimum already covers the introduced version — wrap unnecessary. + yield 'minimum equal to introduced' => [true, '10.2.0', '10.2.0', false]; + yield 'minimum above introduced' => [true, '11.0.0', '10.2.0', false]; + yield 'minimum 10.1.0 equal to introduced floor' => [true, '10.1.0', '10.1.0', false]; + + // Happy paths. + yield 'minimum 10.1.0 introduced 10.2.0' => [true, '10.1.0', '10.2.0', true]; + yield 'minimum 10.1.0 introduced 11.4.0' => [true, '10.1.0', '11.4.0', true]; + yield 'minimum 10.5.0 introduced 11.2.0' => [true, '10.5.0', '11.2.0', true]; + } + + private static function makeRector(DrupalRectorSettings $settings): AbstractDrupalCoreRector + { + return new class($settings) extends AbstractDrupalCoreRector { + public function getNodeTypes(): array + { + return []; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('test', []); + } + + protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration) + { + return null; + } + }; + } + + private static function makeConfiguration(string $introducedVersion): VersionedConfigurationInterface + { + return new class($introducedVersion) implements VersionedConfigurationInterface { + public function __construct(private readonly string $introducedVersion) + { + } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } + }; + } +} diff --git a/tests/src/Rector/Convert/HookConvertRector/HookConvertRectorFixtureTest.php b/tests/src/Rector/Convert/HookConvertRector/HookConvertRectorFixtureTest.php new file mode 100644 index 000000000..67b1e55c9 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/HookConvertRectorFixtureTest.php @@ -0,0 +1,26 @@ +doTestFile($filePath); + } + + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture', '*.module.inc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Rector/Convert/HookConvertRector/HookConvertRectorTest.php b/tests/src/Rector/Convert/HookConvertRector/HookConvertRectorTest.php new file mode 100644 index 000000000..95390375d --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/HookConvertRectorTest.php @@ -0,0 +1,128 @@ +=2.4.5 + // ExprAnalyzer now requires NodeNameResolver, which transitively + // needs ReflectionProvider). + $printer = (new \ReflectionClass(BetterStandardPrinter::class))->newInstanceWithoutConstructor(); + $this->rector = new HookConvertRector($printer); + + $ref = new \ReflectionClass($this->rector); + + $module = $ref->getProperty('module'); + $module->setAccessible(true); + $module->setValue($this->rector, 'mymodule'); + + $classConst = new Node\Expr\ClassConstFetch( + new FullyQualified('Drupal\\mymodule\\Hook\\MymoduleHooks'), + 'class' + ); + $drupalServiceCall = new Node\Expr\StaticCall( + new FullyQualified('Drupal'), + 'service', + [new Node\Arg($classConst)] + ); + $dsc = $ref->getProperty('drupalServiceCall'); + $dsc->setAccessible(true); + $dsc->setValue($this->rector, $drupalServiceCall); + + $hookClass = $ref->getProperty('hookClass'); + $hookClass->setAccessible(true); + $hookClass->setValue($this->rector, new Class_(new Identifier('MymoduleHooks'))); + } + + private function parseFunction(string $code): Function_ + { + $stmts = (new ParserFactory())->createForNewestSupportedVersion()->parse($code); + foreach ($stmts as $stmt) { + if ($stmt instanceof Function_) { + return $stmt; + } + } + throw new \RuntimeException('No function found in code snippet.'); + } + + public function testExplicitVoidReturnTypeGeneratesExpression(): void + { + $fn = $this->parseFunction('rector->getLegacyHookFunction($fn); + $this->assertInstanceOf(Node\Stmt\Expression::class, $result->stmts[0]); + } + + public function testBareReturnGeneratesExpression(): void + { + $fn = $this->parseFunction('rector->getLegacyHookFunction($fn); + $this->assertInstanceOf(Node\Stmt\Expression::class, $result->stmts[0]); + } + + public function testValueReturnGeneratesReturnStatement(): void + { + $fn = $this->parseFunction('rector->getLegacyHookFunction($fn); + $this->assertInstanceOf(Node\Stmt\Return_::class, $result->stmts[0]); + } + + public function testNoReturnGeneratesExpression(): void + { + $fn = $this->parseFunction('block(); }'); + $result = $this->rector->getLegacyHookFunction($fn); + $this->assertInstanceOf(Node\Stmt\Expression::class, $result->stmts[0]); + } + + public function testLegacyHookAttributeIsAdded(): void + { + $fn = $this->parseFunction('rector->getLegacyHookFunction($fn); + + $found = false; + foreach ($result->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + if (str_ends_with((string) $attr->name, 'LegacyHook')) { + $found = true; + } + } + } + $this->assertTrue($found, 'Expected #[LegacyHook] attribute on the function.'); + } + + public function testReturnStatementForwardsMethodCall(): void + { + $fn = $this->parseFunction('rector->getLegacyHookFunction($fn); + + /** @var Node\Stmt\Return_ $returnStmt */ + $returnStmt = $result->stmts[0]; + $this->assertInstanceOf(Node\Expr\MethodCall::class, $returnStmt->expr); + } + + public function testExpressionStatementForwardsMethodCall(): void + { + $fn = $this->parseFunction('rector->getLegacyHookFunction($fn); + + /** @var Node\Stmt\Expression $exprStmt */ + $exprStmt = $result->stmts[0]; + $this->assertInstanceOf(Node\Expr\MethodCall::class, $exprStmt->expr); + } +} diff --git a/tests/src/Rector/Convert/HookConvertRector/Stub/TestHookConvertRector.php b/tests/src/Rector/Convert/HookConvertRector/Stub/TestHookConvertRector.php new file mode 100644 index 000000000..6b40e90be --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/Stub/TestHookConvertRector.php @@ -0,0 +1,22 @@ +.php and a services + * YAML file. Those writes are irrelevant to the transformation under test and + * would pollute the fixture directory, so this subclass skips them. + */ +final class TestHookConvertRector extends HookConvertRector +{ + public function __destruct() + { + $this->module = ''; + } +} diff --git a/tests/src/Rector/Convert/HookConvertRector/config/configured_rule.php b/tests/src/Rector/Convert/HookConvertRector/config/configured_rule.php new file mode 100644 index 000000000..6831d9093 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/config/configured_rule.php @@ -0,0 +1,11 @@ +rule(TestHookConvertRector::class); + $rectorConfig->fileExtensions(['module']); +}; diff --git a/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/bare_return.module.inc b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/bare_return.module.inc new file mode 100644 index 000000000..07d2cbcf4 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/bare_return.module.inc @@ -0,0 +1,25 @@ + +----- +pageAttachments($page); +} +?> diff --git a/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/hookconvertrector.info.yml b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/hookconvertrector.info.yml new file mode 100644 index 000000000..f8da32606 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/hookconvertrector.info.yml @@ -0,0 +1,3 @@ +name: 'Hook Convert Rector' +type: module +core_version_requirement: ^10 || ^11 diff --git a/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/no_return.module.inc b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/no_return.module.inc new file mode 100644 index 000000000..45d934017 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/no_return.module.inc @@ -0,0 +1,22 @@ +block(); +} +?> +----- +userCancel($edit, $account, $method); +} +?> diff --git a/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/value_return.module.inc b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/value_return.module.inc new file mode 100644 index 000000000..85194c2c3 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/value_return.module.inc @@ -0,0 +1,22 @@ + +----- +nodeAccess($node, $op, $account); +} +?> diff --git a/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/void_return_type.module.inc b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/void_return_type.module.inc new file mode 100644 index 000000000..2657d5918 --- /dev/null +++ b/tests/src/Rector/Convert/HookConvertRector/fixture/hookconvertrector/void_return_type.module.inc @@ -0,0 +1,22 @@ + +----- +cacheFlush(); +} +?> diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/ClassConstantToClassConstantRectorTest.php b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/ClassConstantToClassConstantRectorTest.php new file mode 100644 index 000000000..d1a0c8bff --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/ClassConstantToClassConstantRectorTest.php @@ -0,0 +1,48 @@ +doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + // must be implemented + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/config/configured_rule.php b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/config/configured_rule.php index 4d1352cb0..4a031c861 100644 --- a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/config/configured_rule.php +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/config/configured_rule.php @@ -9,23 +9,129 @@ return static function (RectorConfig $rectorConfig): void { DeprecationBase::addClass(ClassConstantToClassConstantRector::class, $rectorConfig, false, [ + // https://www.drupal.org/node/3550054 (Drupal 11.4) + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'FORM_BELOW', + 'Drupal\comment\FormLocation', + 'Below', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'FORM_SEPARATE_PAGE', + 'Drupal\comment\FormLocation', + 'SeparatePage', + '11.4.0', + ), + // https://www.drupal.org/node/3151009 (Drupal 9.1) new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'ROUTE_NAME', 'Drupal\Core\Routing\RouteObjectInterface', 'ROUTE_NAME', + '9.1.0', ), new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'ROUTE_OBJECT', 'Drupal\Core\Routing\RouteObjectInterface', 'ROUTE_OBJECT', + '9.1.0', ), new ClassConstantToClassConstantConfiguration( 'Symfony\Cmf\Component\Routing\RouteObjectInterface', 'CONTROLLER_NAME', 'Drupal\Core\Routing\RouteObjectInterface', 'CONTROLLER_NAME', + '9.1.0', + ), + // https://www.drupal.org/node/3574661 (Drupal 11.4) + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'HIDDEN', + 'Drupal\comment\CommentingStatus', + 'Hidden', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'CLOSED', + 'Drupal\comment\CommentingStatus', + 'Closed', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\Plugin\Field\FieldType\CommentItemInterface', + 'OPEN', + 'Drupal\comment\CommentingStatus', + 'Open', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\CommentInterface', + 'ANONYMOUS_MAYNOT_CONTACT', + 'Drupal\comment\AnonymousContact', + 'Forbidden', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\CommentInterface', + 'ANONYMOUS_MAY_CONTACT', + 'Drupal\comment\AnonymousContact', + 'Allowed', + '11.4.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\comment\CommentInterface', + 'ANONYMOUS_MUST_CONTACT', + 'Drupal\comment\AnonymousContact', + 'Required', + '11.4.0', + ), + // https://www.drupal.org/node/3575575 (Drupal 10.3) + new ClassConstantToClassConstantConfiguration( + 'Drupal\Core\File\FileSystemInterface', + 'EXISTS_RENAME', + 'Drupal\Core\File\FileExists', + 'Rename', + '10.3.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\Core\File\FileSystemInterface', + 'EXISTS_REPLACE', + 'Drupal\Core\File\FileExists', + 'Replace', + '10.3.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\Core\File\FileSystemInterface', + 'EXISTS_ERROR', + 'Drupal\Core\File\FileExists', + 'Error', + '10.3.0', + ), + // https://www.drupal.org/node/3575841 (Drupal 11.2) + new ClassConstantToClassConstantConfiguration( + 'Drupal\system\SystemManager', + 'REQUIREMENT_OK', + 'Drupal\Core\Extension\Requirement\RequirementSeverity', + 'OK', + '11.2.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\system\SystemManager', + 'REQUIREMENT_WARNING', + 'Drupal\Core\Extension\Requirement\RequirementSeverity', + 'Warning', + '11.2.0', + ), + new ClassConstantToClassConstantConfiguration( + 'Drupal\system\SystemManager', + 'REQUIREMENT_ERROR', + 'Drupal\Core\Extension\Requirement\RequirementSeverity', + 'Error', + '11.2.0', ), ]); }; diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture-below-version/comment_form_location.php.inc b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture-below-version/comment_form_location.php.inc new file mode 100644 index 000000000..d14f0f162 --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture-below-version/comment_form_location.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture-below-version/filesystem_exists_constants.php.inc b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture-below-version/filesystem_exists_constants.php.inc new file mode 100644 index 000000000..611ae98e4 --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture-below-version/filesystem_exists_constants.php.inc @@ -0,0 +1,21 @@ +copy($src, $dst, FileSystemInterface::EXISTS_RENAME); + $fileSystem->move($src, $dst, FileSystemInterface::EXISTS_REPLACE); + $fileSystem->saveData($data, $dst, FileSystemInterface::EXISTS_ERROR); +} +?> +----- +copy($src, $dst, FileSystemInterface::EXISTS_RENAME); + $fileSystem->move($src, $dst, FileSystemInterface::EXISTS_REPLACE); + $fileSystem->saveData($data, $dst, FileSystemInterface::EXISTS_ERROR); +} +?> diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/comment_form_location.php.inc b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/comment_form_location.php.inc new file mode 100644 index 000000000..b9318e0bc --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/comment_form_location.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal\comment\FormLocation::Below, fn() => CommentItemInterface::FORM_BELOW); +$other = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\comment\FormLocation::SeparatePage, fn() => CommentItemInterface::FORM_SEPARATE_PAGE); +?> diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/comment_item_interface_constants.php.inc b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/comment_item_interface_constants.php.inc new file mode 100644 index 000000000..19e3243f6 --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/comment_item_interface_constants.php.inc @@ -0,0 +1,25 @@ + +----- + \Drupal\comment\CommentingStatus::Hidden, fn() => CommentItemInterface::HIDDEN); +$closed = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\comment\CommentingStatus::Closed, fn() => CommentItemInterface::CLOSED); +$open = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\comment\CommentingStatus::Open, fn() => CommentItemInterface::OPEN); +$forbidden = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\comment\AnonymousContact::Forbidden, fn() => CommentInterface::ANONYMOUS_MAYNOT_CONTACT); +$allowed = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\comment\AnonymousContact::Allowed, fn() => CommentInterface::ANONYMOUS_MAY_CONTACT); +$required = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\comment\AnonymousContact::Required, fn() => CommentInterface::ANONYMOUS_MUST_CONTACT); +?> diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/filesystem_exists_constants.php.inc b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/filesystem_exists_constants.php.inc new file mode 100644 index 000000000..2deac287c --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/filesystem_exists_constants.php.inc @@ -0,0 +1,21 @@ +copy($src, $dst, FileSystemInterface::EXISTS_RENAME); + $fileSystem->move($src, $dst, FileSystemInterface::EXISTS_REPLACE); + $fileSystem->saveData($data, $dst, FileSystemInterface::EXISTS_ERROR); +} +?> +----- +copy($src, $dst, \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => \Drupal\Core\File\FileExists::Rename, fn() => FileSystemInterface::EXISTS_RENAME)); + $fileSystem->move($src, $dst, \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => \Drupal\Core\File\FileExists::Replace, fn() => FileSystemInterface::EXISTS_REPLACE)); + $fileSystem->saveData($data, $dst, \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => \Drupal\Core\File\FileExists::Error, fn() => FileSystemInterface::EXISTS_ERROR)); +} +?> diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/system_manager_requirement_constants.php.inc b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/system_manager_requirement_constants.php.inc new file mode 100644 index 000000000..d72e93e1a --- /dev/null +++ b/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/fixture/system_manager_requirement_constants.php.inc @@ -0,0 +1,21 @@ + +----- + \Drupal\Core\Extension\Requirement\RequirementSeverity::OK, fn() => SystemManager::REQUIREMENT_OK); + $requirements['check']['severity'] = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Extension\Requirement\RequirementSeverity::Warning, fn() => SystemManager::REQUIREMENT_WARNING); + $requirements['check']['severity'] = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Extension\Requirement\RequirementSeverity::Error, fn() => SystemManager::REQUIREMENT_ERROR); +} +?> diff --git a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/ConstantToClassConstantRectorTest.php b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/ConstantToClassConstantRectorTest.php index b7698b1e6..78c991107 100644 --- a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/ConstantToClassConstantRectorTest.php +++ b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/ConstantToClassConstantRectorTest.php @@ -2,18 +2,16 @@ declare(strict_types=1); -namespace DrupalRector\Rector\Deprecation\ConstantToClassConstantRector; +namespace DrupalRector\Tests\Rector\Deprecation\ConstantToClassConstantRector; +use DrupalRector\Services\DrupalRectorSettings; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class ConstantToClassConstantRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class ConstantToClassConstantRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); @@ -27,6 +25,21 @@ public static function provideData(): \Iterator return self::yieldFilesFromDirectory(__DIR__.'/fixture'); } + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + public function provideConfigFilePath(): string { // must be implemented diff --git a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/config/configured_rule.php b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/config/configured_rule.php index fe541df3a..a1e4ec518 100644 --- a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/config/configured_rule.php +++ b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/config/configured_rule.php @@ -13,6 +13,7 @@ 'DATETIME_STORAGE_TIMEZONE', 'Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface', 'STORAGE_TIMEZONE', + '8.5.0', ), ]); DeprecationBase::addClass(ConstantToClassConstantRector::class, $rectorConfig, false, [ @@ -20,6 +21,19 @@ 'FILE_STATUS_PERMANENT', 'Drupal\file\FileInterface', 'STATUS_PERMANENT', + '9.3.0', ), ]); + DeprecationBase::addClass(ConstantToClassConstantRector::class, $rectorConfig, false, [ + new ConstantToClassConfiguration('REQUIREMENT_INFO', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'Info', '11.2.0'), + new ConstantToClassConfiguration('REQUIREMENT_OK', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'OK', '11.2.0'), + new ConstantToClassConfiguration('REQUIREMENT_WARNING', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'Warning', '11.2.0'), + new ConstantToClassConfiguration('REQUIREMENT_ERROR', 'Drupal\Core\Extension\Requirement\RequirementSeverity', 'Error', '11.2.0'), + new ConstantToClassConfiguration('LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN', 'Drupal', 'TRANSLATION_DEFAULT_SERVER_PATTERN', '11.2.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_ALL', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_ALL', '11.3.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_PUBLISHED', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_PUBLISHED', '11.3.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_ENABLED', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_ENABLED', '11.3.0'), + new ConstantToClassConfiguration('JSONAPI_FILTER_AMONG_OWN', 'Drupal\jsonapi\JsonApiFilter', 'AMONG_OWN', '11.3.0'), + new ConstantToClassConfiguration('IMAGE_DERIVATIVE_TOKEN', 'Drupal\image\ImageStyleInterface', 'TOKEN', '11.4.0'), + ]); }; diff --git a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture-below-version/drupal_11_4_image_constants.php.inc b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture-below-version/drupal_11_4_image_constants.php.inc new file mode 100644 index 000000000..e9c345185 --- /dev/null +++ b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture-below-version/drupal_11_4_image_constants.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture-below-version/drupal_11_constants.php.inc b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture-below-version/drupal_11_constants.php.inc new file mode 100644 index 000000000..89892d0a3 --- /dev/null +++ b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture-below-version/drupal_11_constants.php.inc @@ -0,0 +1,21 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture/drupal_11_4_image_constants.php.inc b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture/drupal_11_4_image_constants.php.inc new file mode 100644 index 000000000..b8f7aa7c8 --- /dev/null +++ b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture/drupal_11_4_image_constants.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal\image\ImageStyleInterface::TOKEN, fn() => IMAGE_DERIVATIVE_TOKEN); + +?> diff --git a/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture/drupal_11_constants.php.inc b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture/drupal_11_constants.php.inc new file mode 100644 index 000000000..9056c77f4 --- /dev/null +++ b/tests/src/Rector/Deprecation/ConstantToClassConstantRector/fixture/drupal_11_constants.php.inc @@ -0,0 +1,37 @@ + +----- + \Drupal\Core\Extension\Requirement\RequirementSeverity::Info, fn() => REQUIREMENT_INFO); +$b = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Extension\Requirement\RequirementSeverity::OK, fn() => REQUIREMENT_OK); +$c = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Extension\Requirement\RequirementSeverity::Warning, fn() => REQUIREMENT_WARNING); +$d = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\Core\Extension\Requirement\RequirementSeverity::Error, fn() => REQUIREMENT_ERROR); + +// Drupal 11.2: LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN → \Drupal::TRANSLATION_DEFAULT_SERVER_PATTERN +$pattern = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::TRANSLATION_DEFAULT_SERVER_PATTERN, fn() => LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN); + +// Drupal 11.3: JSONAPI_FILTER_AMONG_* → JsonApiFilter class constants +$filter1 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\jsonapi\JsonApiFilter::AMONG_ALL, fn() => JSONAPI_FILTER_AMONG_ALL); +$filter2 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\jsonapi\JsonApiFilter::AMONG_PUBLISHED, fn() => JSONAPI_FILTER_AMONG_PUBLISHED); +$filter3 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\jsonapi\JsonApiFilter::AMONG_ENABLED, fn() => JSONAPI_FILTER_AMONG_ENABLED); +$filter4 = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal\jsonapi\JsonApiFilter::AMONG_OWN, fn() => JSONAPI_FILTER_AMONG_OWN); + +?> diff --git a/tests/src/Rector/Deprecation/DeprecationBase.php b/tests/src/Rector/Deprecation/DeprecationBase.php index b324f1ffc..bd9d514ea 100644 --- a/tests/src/Rector/Deprecation/DeprecationBase.php +++ b/tests/src/Rector/Deprecation/DeprecationBase.php @@ -5,6 +5,7 @@ namespace DrupalRector\Tests\Rector\Deprecation; use DrupalRector\Services\AddCommentService; +use DrupalRector\Services\DrupalRectorSettings; use Rector\Config\RectorConfig; /** @@ -22,6 +23,8 @@ class DeprecationBase */ public static function addClass(string $rectorClass, RectorConfig $rectorConfig, bool $add_notice_config = true, array $configuration = []) { + $rectorConfig->singleton(DrupalRectorSettings::class); + if ($add_notice_config) { $rectorConfig->singleton(AddCommentService::class, function () { return new AddCommentService(true); diff --git a/tests/src/Rector/Deprecation/DeprecationHelperRemoveRector/DeprecationHelperRemoveRectorTest.php b/tests/src/Rector/Deprecation/DeprecationHelperRemoveRector/DeprecationHelperRemoveRectorTest.php index 4af2ba260..1bdb89b50 100644 --- a/tests/src/Rector/Deprecation/DeprecationHelperRemoveRector/DeprecationHelperRemoveRectorTest.php +++ b/tests/src/Rector/Deprecation/DeprecationHelperRemoveRector/DeprecationHelperRemoveRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Rector\Deprecation\DeprecationHelperRemoveRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class DeprecationHelperRemoveRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class DeprecationHelperRemoveRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/ConstantToClassConstantRectorTest.php b/tests/src/Rector/Deprecation/DrupalServiceRenameRector/DrupalServiceRenameRectorTest.php similarity index 58% rename from tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/ConstantToClassConstantRectorTest.php rename to tests/src/Rector/Deprecation/DrupalServiceRenameRector/DrupalServiceRenameRectorTest.php index 2c7181c73..f9319e3a6 100644 --- a/tests/src/Rector/Deprecation/ClassConstantToClassConstantRector/ConstantToClassConstantRectorTest.php +++ b/tests/src/Rector/Deprecation/DrupalServiceRenameRector/DrupalServiceRenameRectorTest.php @@ -2,18 +2,14 @@ declare(strict_types=1); -namespace DrupalRector\Rector\Deprecation\ClassConstantToClassConstantRector; +namespace DrupalRector\Tests\Rector\Deprecation\DrupalServiceRenameRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class ClassConstantToClassConstantRectorTest extends AbstractRectorTestCase +class DrupalServiceRenameRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); @@ -29,7 +25,6 @@ public static function provideData(): \Iterator public function provideConfigFilePath(): string { - // must be implemented return __DIR__.'/config/configured_rule.php'; } } diff --git a/tests/src/Rector/Deprecation/DrupalServiceRenameRector/config/configured_rule.php b/tests/src/Rector/Deprecation/DrupalServiceRenameRector/config/configured_rule.php new file mode 100644 index 000000000..dc6edc410 --- /dev/null +++ b/tests/src/Rector/Deprecation/DrupalServiceRenameRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + \Drupal::service('new.service'), fn() => \Drupal::service('old.service')); + $other = \Drupal::service('untouched.service'); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/FunctionCallRemovalRectorTest.php b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/FunctionCallRemovalRectorTest.php new file mode 100644 index 000000000..28d0c0f71 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/FunctionCallRemovalRectorTest.php @@ -0,0 +1,29 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/config/configured_rule.php b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/config/configured_rule.php new file mode 100644 index 000000000..db062706c --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/config/configured_rule.php @@ -0,0 +1,29 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/block_theme_initialize.php.inc b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/block_theme_initialize.php.inc new file mode 100644 index 000000000..11c44ebd0 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/block_theme_initialize.php.inc @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/fqcn_prefix.php.inc b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/fqcn_prefix.php.inc new file mode 100644 index 000000000..8feabfba4 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/fqcn_prefix.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/no_change_expression_usage.php.inc b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/no_change_expression_usage.php.inc new file mode 100644 index 000000000..ca774bc79 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/no_change_expression_usage.php.inc @@ -0,0 +1,19 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/syslog_functions.php.inc b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/syslog_functions.php.inc new file mode 100644 index 000000000..7b37ca6e2 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/syslog_functions.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/taxonomy_node_index_functions.php.inc b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/taxonomy_node_index_functions.php.inc new file mode 100644 index 000000000..35291ba1c --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionCallRemovalRector/fixture/taxonomy_node_index_functions.php.inc @@ -0,0 +1,15 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/FunctionToFirstArgMethodRectorTest.php b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/FunctionToFirstArgMethodRectorTest.php new file mode 100644 index 000000000..fccf95dac --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/FunctionToFirstArgMethodRectorTest.php @@ -0,0 +1,46 @@ +make(DrupalRectorSettings::class)->setDrupalVersion('99.99.99'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return \Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + + public function provideConfigFilePath(): string + { + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/config/configured_rule.php b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/config/configured_rule.php new file mode 100644 index 000000000..fc231e8d0 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/config/configured_rule.php @@ -0,0 +1,16 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture-below-version/file_get_content_headers_basic.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture-below-version/file_get_content_headers_basic.php.inc new file mode 100644 index 000000000..6570d9034 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture-below-version/file_get_content_headers_basic.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture-below-version/node_type_get_description_basic.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture-below-version/node_type_get_description_basic.php.inc new file mode 100644 index 000000000..121ddf342 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture-below-version/node_type_get_description_basic.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_as_argument.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_as_argument.php.inc new file mode 100644 index 000000000..8a08f8ad6 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_as_argument.php.inc @@ -0,0 +1,13 @@ + +----- + $comment->permalink(), fn() => comment_uri($comment))); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_basic.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_basic.php.inc new file mode 100644 index 000000000..c0bc221a2 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_basic.php.inc @@ -0,0 +1,11 @@ + +----- + $comment->permalink(), fn() => comment_uri($comment)); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_complex_expression.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_complex_expression.php.inc new file mode 100644 index 000000000..c5e88b96e --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_complex_expression.php.inc @@ -0,0 +1,13 @@ +getComment()->permalink() +$url = comment_uri($this->getComment()); + +?> +----- +getComment()->permalink() +$url = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => $this->getComment()->permalink(), fn() => comment_uri($this->getComment())); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_inline_usage.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_inline_usage.php.inc new file mode 100644 index 000000000..9023e2857 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_inline_usage.php.inc @@ -0,0 +1,13 @@ + +----- + $comment->permalink(), fn() => comment_uri($comment)); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_no_change_zero_args.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_no_change_zero_args.php.inc new file mode 100644 index 000000000..395fbd73e --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/comment_uri_no_change_zero_args.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_as_argument.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_as_argument.php.inc new file mode 100644 index 000000000..67e9098e7 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_as_argument.php.inc @@ -0,0 +1,13 @@ + +----- + $file->getDownloadHeaders(), fn() => file_get_content_headers($file))); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_basic.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_basic.php.inc new file mode 100644 index 000000000..730f87aa1 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_basic.php.inc @@ -0,0 +1,13 @@ + +----- + $file->getDownloadHeaders(), fn() => file_get_content_headers($file)); +$other = file_get_something_else($file); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_inline_in_array.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_inline_in_array.php.inc new file mode 100644 index 000000000..5273cd8c6 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_inline_in_array.php.inc @@ -0,0 +1,13 @@ + file_get_content_headers($file), 'name' => 'test']; + +?> +----- + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => $file->getDownloadHeaders(), fn() => file_get_content_headers($file)), 'name' => 'test']; + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_method_call_as_arg.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_method_call_as_arg.php.inc new file mode 100644 index 000000000..7e90f3180 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_method_call_as_arg.php.inc @@ -0,0 +1,13 @@ +getFile()); + +?> +----- + $this->getFile()->getDownloadHeaders(), fn() => file_get_content_headers($this->getFile())); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_no_change_multiple_args.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_no_change_multiple_args.php.inc new file mode 100644 index 000000000..a2f20b553 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_no_change_multiple_args.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_no_change_zero_args.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_no_change_zero_args.php.inc new file mode 100644 index 000000000..dc8f1f3b6 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/file_get_content_headers_no_change_zero_args.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/node_type_get_description_basic.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/node_type_get_description_basic.php.inc new file mode 100644 index 000000000..5c56b9645 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/node_type_get_description_basic.php.inc @@ -0,0 +1,11 @@ + +----- + $node_type->getDescription(), fn() => node_type_get_description($node_type)); + +?> diff --git a/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/node_type_get_description_no_change_zero_args.php.inc b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/node_type_get_description_no_change_zero_args.php.inc new file mode 100644 index 000000000..586625d3a --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToFirstArgMethodRector/fixture/node_type_get_description_no_change_zero_args.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/FunctionToServiceRectorTest.php b/tests/src/Rector/Deprecation/FunctionToServiceRector/FunctionToServiceRectorTest.php index 7398a9e72..feeb54d56 100644 --- a/tests/src/Rector/Deprecation/FunctionToServiceRector/FunctionToServiceRectorTest.php +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/FunctionToServiceRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Rector\Deprecation\FunctionToServiceRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class FunctionToServiceRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class FunctionToServiceRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/config/configured_rule.php b/tests/src/Rector/Deprecation/FunctionToServiceRector/config/configured_rule.php index 6bfbfe980..e9470ee86 100644 --- a/tests/src/Rector/Deprecation/FunctionToServiceRector/config/configured_rule.php +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/config/configured_rule.php @@ -15,5 +15,93 @@ new FunctionToServiceConfiguration('9.3.0', 'file_save_data', 'file.repository', 'writeData'), new FunctionToServiceConfiguration('10.1.0', 'drupal_theme_rebuild', 'theme.registry', 'reset'), new FunctionToServiceConfiguration('10.2.0', '_drupal_flush_css_js', 'asset.query_string', 'reset'), + // https://www.drupal.org/node/3489502 (Drupal 11.2) + new FunctionToServiceConfiguration('11.2.0', '_views_field_get_entity_type_storage', 'views.field_data_provider', 'getSqlStorageForField'), + new FunctionToServiceConfiguration('11.2.0', 'views_entity_field_label', 'entity_field.manager', 'getFieldLabels'), + new FunctionToServiceConfiguration('11.2.0', 'views_field_default_views_data', 'views.field_data_provider', 'defaultFieldImplementation'), + // https://www.drupal.org/node/3501136 (Drupal 11.2) + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_time', 'Drupal\Core\Datetime\DatePreprocess', 'preprocessTime'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_datetime_form', 'Drupal\Core\Datetime\DatePreprocess', 'preprocessDatetimeForm'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_datetime_wrapper', 'Drupal\Core\Datetime\DatePreprocess', 'preprocessDatetimeWrapper'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_links', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessLinks'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_container', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessContainer'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_html', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessHtml'), + new FunctionToServiceConfiguration('11.2.0', 'template_preprocess_page', 'Drupal\Core\Theme\ThemePreprocess', 'preprocessPage'), + // https://www.drupal.org/node/3548329 (Drupal 11.3) + new FunctionToServiceConfiguration('11.3.0', '_responsive_image_build_source_attributes', 'Drupal\responsive_image\ResponsiveImageBuilder', 'buildSourceAttributes'), + new FunctionToServiceConfiguration('11.3.0', '_responsive_image_image_style_url', 'Drupal\responsive_image\ResponsiveImageBuilder', 'getImageStyleUrl'), + new FunctionToServiceConfiguration('11.3.0', 'responsive_image_get_image_dimensions', 'Drupal\responsive_image\ResponsiveImageBuilder', 'getImageDimensions'), + new FunctionToServiceConfiguration('11.3.0', 'responsive_image_get_mime_type', 'Drupal\responsive_image\ResponsiveImageBuilder', 'getMimeType'), + // https://www.drupal.org/node/3533083 (Drupal 11.3) + new FunctionToServiceConfiguration('11.3.0', 'node_mass_update', 'Drupal\node\NodeBulkUpdate', 'process', true), + // https://www.drupal.org/node/3571382 (Drupal 11.3) + new FunctionToServiceConfiguration('11.3.0', 'template_preprocess_layout', 'Drupal\layout_discovery\Hook\LayoutDiscoveryThemeHooks', 'preprocessLayout', true), + // https://www.drupal.org/node/1685492 (Drupal 11.3) + new FunctionToServiceConfiguration('11.3.0', 'twig_render_template', 'Drupal\Core\Template\TwigThemeEngine', 'renderTemplate'), + // https://www.drupal.org/node/3568088 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', '_contextual_links_to_id', 'Drupal\contextual\ContextualLinksSerializer', 'linksToId'), + new FunctionToServiceConfiguration('11.4.0', '_contextual_id_to_links', 'Drupal\contextual\ContextualLinksSerializer', 'idToLinks'), + // https://www.drupal.org/node/3570917 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'editor_filter_xss', 'element.editor', 'filterXss'), + new FunctionToServiceConfiguration('11.4.0', 'editor_image_upload_settings_form', 'Drupal\editor\EditorImageUploadSettings', 'getForm'), + // https://www.drupal.org/node/3494023 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'field_purge_batch', 'Drupal\Core\Field\FieldPurger', 'purgeBatch'), + // https://www.drupal.org/node/3567619 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'image_path_flush', 'Drupal\image\ImageDerivativeUtilities', 'pathFlush'), + new FunctionToServiceConfiguration('11.4.0', 'image_style_options', 'Drupal\image\ImageDerivativeUtilities', 'styleOptions'), + // https://www.drupal.org/node/3577675 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'locale_translate_get_interface_translation_files', 'Drupal\locale\File\LocaleFileManager', 'getInterfaceTranslationFiles'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_http_check', 'Drupal\locale\File\LocaleFileManager', 'checkRemoteFileStatus'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translate_delete_translation_files', 'Drupal\locale\File\LocaleFileManager', 'deleteTranslationFiles'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_download_source', 'Drupal\locale\File\LocaleFileManager', 'downloadTranslationSource'), + // https://www.drupal.org/node/3566774 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', '_media_library_media_type_form_submit', 'Drupal\media_library\Hook\MediaLibraryHooks', 'mediaTypeFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', '_media_library_views_form_media_library_after_build', 'Drupal\media_library\Hook\MediaLibraryHooks', 'viewsFormAfterBuild'), + // https://www.drupal.org/node/3574727 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'language_process_language_select', 'Drupal\language\Hook\LanguageHooks', 'processLanguageSelect'), + // https://www.drupal.org/node/3566792 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'ckeditor5_filter_format_edit_form_submit', 'Drupal\ckeditor5\Hook\Ckeditor5Hooks', 'filterFormatEditFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', '_update_ckeditor5_html_filter', 'Drupal\ckeditor5\Hook\Ckeditor5Hooks', 'updateCkeditor5HtmlFilter'), + // https://www.drupal.org/node/3560398 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', '_dblog_get_message_types', 'Drupal\dblog\DbLogFilters', 'getMessageTypes'), + new FunctionToServiceConfiguration('11.4.0', 'dblog_filters', 'Drupal\dblog\DbLogFilters', 'filters'), + // https://www.drupal.org/node/3566888 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'contact_user_profile_form_submit', 'Drupal\contact\Hook\ContactFormHooks', 'profileFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', 'contact_form_user_admin_settings_submit', 'Drupal\contact\Hook\ContactFormHooks', 'userAdminSettingsSubmit'), + // https://www.drupal.org/node/3548571 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'content_translation_translate_access', 'content_translation.manager', 'access'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_enable_widget', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'getWidget'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_language_configuration_element_process', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'configElementProcess'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_language_configuration_element_validate', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'configElementValidate'), + new FunctionToServiceConfiguration('11.4.0', 'content_translation_language_configuration_element_submit', 'Drupal\content_translation\ContentTranslationEnableTranslationPerBundle', 'configElementSubmit'), + new FunctionToServiceConfiguration('11.4.0', '_content_translation_install_field_storage_definitions', 'Drupal\content_translation\Hook\ContentTranslationHooks', 'installFieldStorageDefinitions'), + // https://www.drupal.org/node/3572339 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_batch_update_build', 'Drupal\locale\LocaleFetch', 'batchUpdateBuild'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_batch_fetch_build', 'Drupal\locale\LocaleFetch', 'batchFetchBuild'), + // https://www.drupal.org/node/3569328 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_get_projects', 'locale.project', 'getProjects'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_clear_cache_projects', 'locale.project', 'resetCache'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_load_sources', 'Drupal\locale\LocaleSource', 'loadSources'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_build_sources', 'Drupal\locale\LocaleSource', 'buildSources'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_source_check_file', 'Drupal\locale\LocaleSource', 'sourceCheckFile'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_source_build', 'Drupal\locale\LocaleSource', 'sourceBuild'), + new FunctionToServiceConfiguration('11.4.0', 'locale_translation_build_server_pattern', 'Drupal\locale\LocaleSource', 'buildServerPattern'), + // https://www.drupal.org/node/3571400 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', '_menu_ui_node_save', 'Drupal\menu_ui\MenuUiUtility', 'menuUiNodeSave'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_get_menu_link_defaults', 'Drupal\menu_ui\MenuUiUtility', 'getMenuLinkDefaults'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_node_builder', 'Drupal\menu_ui\Hook\MenuUiHooks', 'nodeBuilder'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_form_node_form_submit', 'Drupal\menu_ui\Hook\MenuUiHooks', 'formNodeFormSubmit'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_form_node_type_form_validate', 'Drupal\menu_ui\Hook\MenuUiHooks', 'formNodeTypeFormValidate'), + new FunctionToServiceConfiguration('11.4.0', 'menu_ui_form_node_type_form_builder', 'Drupal\menu_ui\Hook\MenuUiHooks', 'formNodeTypeFormBuilder'), + // https://www.drupal.org/node/3568387 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'text_summary', 'Drupal\text\TextSummary', 'generate'), + // https://www.drupal.org/node/3582106 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'user_form_process_password_confirm', 'Drupal\user\Hook\UserThemeHooks', 'processPasswordConfirm'), + // https://www.drupal.org/node/2473041 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'node_access_grants', 'Drupal\node\NodeGrantsHelper', 'nodeAccessGrants', true), + // https://www.drupal.org/node/2571679 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'views_add_contextual_links', 'Drupal\views\ContextualLinksHelper', 'addLinks', true), + // https://www.drupal.org/node/3567163 (Drupal 11.4) + new FunctionToServiceConfiguration('11.4.0', 'field_ui_form_manage_field_form_submit', 'Drupal\field_ui\Hook\FieldUiHooks', 'manageFieldFormSubmit', true), ]); }; diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/ckeditor5_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/ckeditor5_procedural_functions.php.inc new file mode 100644 index 000000000..e92463255 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/ckeditor5_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\ckeditor5\Hook\Ckeditor5Hooks')->filterFormatEditFormSubmit($form, $form_state), fn() => ckeditor5_filter_format_edit_form_submit($form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\ckeditor5\Hook\Ckeditor5Hooks')->updateCkeditor5HtmlFilter($form, $form_state), fn() => _update_ckeditor5_html_filter($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/contact_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/contact_procedural_functions.php.inc new file mode 100644 index 000000000..e54ebc1ec --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/contact_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\contact\Hook\ContactFormHooks')->profileFormSubmit($form, $form_state), fn() => contact_user_profile_form_submit($form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\contact\Hook\ContactFormHooks')->userAdminSettingsSubmit($form, $form_state), fn() => contact_form_user_admin_settings_submit($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/content_translation_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/content_translation_procedural_functions.php.inc new file mode 100644 index 000000000..beda962f0 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/content_translation_procedural_functions.php.inc @@ -0,0 +1,23 @@ + +----- + \Drupal::service('content_translation.manager')->access($entity), fn() => content_translation_translate_access($entity)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\content_translation\ContentTranslationEnableTranslationPerBundle')->getWidget($entity_type, $bundle, $form, $form_state), fn() => content_translation_enable_widget($entity_type, $bundle, $form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\content_translation\ContentTranslationEnableTranslationPerBundle')->configElementProcess($element, $form_state, $form), fn() => content_translation_language_configuration_element_process($element, $form_state, $form)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\content_translation\ContentTranslationEnableTranslationPerBundle')->configElementValidate($element, $form_state, $form), fn() => content_translation_language_configuration_element_validate($element, $form_state, $form)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\content_translation\ContentTranslationEnableTranslationPerBundle')->configElementSubmit($form, $form_state), fn() => content_translation_language_configuration_element_submit($form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\content_translation\Hook\ContentTranslationHooks')->installFieldStorageDefinitions($entity_type_id), fn() => _content_translation_install_field_storage_definitions($entity_type_id)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/contextual_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/contextual_procedural_functions.php.inc new file mode 100644 index 000000000..c875a7aff --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/contextual_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\contextual\ContextualLinksSerializer')->linksToId($contextual_links), fn() => _contextual_links_to_id($contextual_links)); + $links = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\contextual\ContextualLinksSerializer')->idToLinks($id), fn() => _contextual_id_to_links($id)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/dblog_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/dblog_procedural_functions.php.inc new file mode 100644 index 000000000..edc361a2a --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/dblog_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\dblog\DbLogFilters')->getMessageTypes(), fn() => _dblog_get_message_types()); + $filters = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\dblog\DbLogFilters')->filters(), fn() => dblog_filters()); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/drupal_theme_rebuild.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/drupal_theme_rebuild.php.inc index b3eb1fc3d..30df79a25 100644 --- a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/drupal_theme_rebuild.php.inc +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/drupal_theme_rebuild.php.inc @@ -16,6 +16,6 @@ function append_file_info_install() { */ function append_file_info_install() { require_once DRUPAL_ROOT . '/includes/theme.inc'; - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal::service('theme.registry')->reset(), fn() => drupal_theme_rebuild()); + \Drupal::service('theme.registry')->reset(); } diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/editor_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/editor_procedural_functions.php.inc new file mode 100644 index 000000000..b239149e5 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/editor_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('element.editor')->filterXss($html, $format, $original_format), fn() => editor_filter_xss($html, $format, $original_format)); + $form = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\editor\EditorImageUploadSettings')->getForm($editor), fn() => editor_image_upload_settings_form($editor)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/field_purge_batch.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/field_purge_batch.php.inc new file mode 100644 index 000000000..e0b7bbbd6 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/field_purge_batch.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service('Drupal\Core\Field\FieldPurger')->purgeBatch($batch_size, $field_storage_unique_id), fn() => field_purge_batch($batch_size, $field_storage_unique_id)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/field_ui_form_manage_field_form_submit.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/field_ui_form_manage_field_form_submit.php.inc new file mode 100644 index 000000000..7beb65fb3 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/field_ui_form_manage_field_form_submit.php.inc @@ -0,0 +1,9 @@ + +----- + \Drupal::service(\Drupal\field_ui\Hook\FieldUiHooks::class)->manageFieldFormSubmit($form, $form_state), fn() => field_ui_form_manage_field_form_submit($form, $form_state)); +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/image_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/image_procedural_functions.php.inc new file mode 100644 index 000000000..7eadf3eac --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/image_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\image\ImageDerivativeUtilities')->pathFlush($path), fn() => image_path_flush($path)); + $options = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\image\ImageDerivativeUtilities')->styleOptions($include_empty), fn() => image_style_options($include_empty)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/language_process_language_select.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/language_process_language_select.php.inc new file mode 100644 index 000000000..ae553fcb1 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/language_process_language_select.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service('Drupal\language\Hook\LanguageHooks')->processLanguageSelect($element), fn() => language_process_language_select($element)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_fetch_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_fetch_functions.php.inc new file mode 100644 index 000000000..208abfd38 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_fetch_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\locale\LocaleFetch')->batchUpdateBuild($projects, $langcodes, $options), fn() => locale_translation_batch_update_build($projects, $langcodes, $options)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\LocaleFetch')->batchFetchBuild($projects, $langcodes, $options), fn() => locale_translation_batch_fetch_build($projects, $langcodes, $options)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_file_manager_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_file_manager_functions.php.inc new file mode 100644 index 000000000..1928b027a --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_file_manager_functions.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal::service('Drupal\locale\File\LocaleFileManager')->getInterfaceTranslationFiles($projects, $langcodes), fn() => locale_translate_get_interface_translation_files($projects, $langcodes)); + $status = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\File\LocaleFileManager')->checkRemoteFileStatus($uri), fn() => locale_translation_http_check($uri)); + $result = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\File\LocaleFileManager')->deleteTranslationFiles($projects, $langcodes), fn() => locale_translate_delete_translation_files($projects, $langcodes)); + $downloaded = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\File\LocaleFileManager')->downloadTranslationSource($source_file, $directory), fn() => locale_translation_download_source($source_file, $directory)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_translation_inc_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_translation_inc_functions.php.inc new file mode 100644 index 000000000..d68b7ea85 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/locale_translation_inc_functions.php.inc @@ -0,0 +1,25 @@ + +----- + \Drupal::service('locale.project')->getProjects(['mymodule']), fn() => locale_translation_get_projects(['mymodule'])); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('locale.project')->resetCache(), fn() => locale_translation_clear_cache_projects()); + $sources = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\LocaleSource')->loadSources($projects, $langcodes), fn() => locale_translation_load_sources($projects, $langcodes)); + $built = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\LocaleSource')->buildSources($projects, $langcodes), fn() => locale_translation_build_sources($projects, $langcodes)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\LocaleSource')->sourceCheckFile($source), fn() => locale_translation_source_check_file($source)); + $obj = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\LocaleSource')->sourceBuild($project, 'fr', 'mymodule.po'), fn() => locale_translation_source_build($project, 'fr', 'mymodule.po')); + $pattern = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\locale\LocaleSource')->buildServerPattern($project, $template), fn() => locale_translation_build_server_pattern($project, $template)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/media_library_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/media_library_procedural_functions.php.inc new file mode 100644 index 000000000..e48c132f8 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/media_library_procedural_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal::service('Drupal\media_library\Hook\MediaLibraryHooks')->mediaTypeFormSubmit($form, $form_state), fn() => _media_library_media_type_form_submit($form, $form_state)); + $result = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\media_library\Hook\MediaLibraryHooks')->viewsFormAfterBuild($form, $form_state), fn() => _media_library_views_form_media_library_after_build($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/menu_ui_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/menu_ui_procedural_functions.php.inc new file mode 100644 index 000000000..d8571666a --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/menu_ui_procedural_functions.php.inc @@ -0,0 +1,23 @@ + +----- + \Drupal::service('Drupal\menu_ui\MenuUiUtility')->menuUiNodeSave($node, $values), fn() => _menu_ui_node_save($node, $values)); + $defaults = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\menu_ui\MenuUiUtility')->getMenuLinkDefaults($node), fn() => menu_ui_get_menu_link_defaults($node)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\menu_ui\Hook\MenuUiHooks')->nodeBuilder($node, $form, $form_state), fn() => menu_ui_node_builder($node, $form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\menu_ui\Hook\MenuUiHooks')->formNodeFormSubmit($form, $form_state), fn() => menu_ui_form_node_form_submit($form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\menu_ui\Hook\MenuUiHooks')->formNodeTypeFormValidate($form, $form_state), fn() => menu_ui_form_node_type_form_validate($form, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal::service('Drupal\menu_ui\Hook\MenuUiHooks')->formNodeTypeFormBuilder($node, $form, $form_state), fn() => menu_ui_form_node_type_form_builder($node, $form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/node_access_grants.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/node_access_grants.php.inc new file mode 100644 index 000000000..a74b45ae5 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/node_access_grants.php.inc @@ -0,0 +1,9 @@ + +----- + \Drupal::service(\Drupal\node\NodeGrantsHelper::class)->nodeAccessGrants($operation, $account), fn() => node_access_grants($operation, $account)); +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/node_mass_update.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/node_mass_update.php.inc new file mode 100644 index 000000000..5809dcdbb --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/node_mass_update.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service(\Drupal\node\NodeBulkUpdate::class)->process($nids, $updates, $langcode, true, false), fn() => node_mass_update($nids, $updates, $langcode, true, false)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/responsive_image_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/responsive_image_functions.php.inc new file mode 100644 index 000000000..a5cef4c79 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/responsive_image_functions.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal::service('Drupal\responsive_image\ResponsiveImageBuilder')->buildSourceAttributes($variables, $breakpoint, $multipliers), fn() => _responsive_image_build_source_attributes($variables, $breakpoint, $multipliers)); + $dims = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service('Drupal\responsive_image\ResponsiveImageBuilder')->getImageDimensions($image_style_name, $dimensions, $uri), fn() => responsive_image_get_image_dimensions($image_style_name, $dimensions, $uri)); + $mime = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service('Drupal\responsive_image\ResponsiveImageBuilder')->getMimeType($image_style_name, $extension), fn() => responsive_image_get_mime_type($image_style_name, $extension)); + $url = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.3.0', fn() => \Drupal::service('Drupal\responsive_image\ResponsiveImageBuilder')->getImageStyleUrl($style_name, $path), fn() => _responsive_image_image_style_url($style_name, $path)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/template_preprocess_layout.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/template_preprocess_layout.php.inc new file mode 100644 index 000000000..cf57556a2 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/template_preprocess_layout.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service(\Drupal\layout_discovery\Hook\LayoutDiscoveryThemeHooks::class)->preprocessLayout($variables), fn() => template_preprocess_layout($variables)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/template_preprocess_service.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/template_preprocess_service.php.inc new file mode 100644 index 000000000..e4a3db4cf --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/template_preprocess_service.php.inc @@ -0,0 +1,25 @@ + +----- + \Drupal::service('Drupal\Core\Theme\ThemePreprocess')->preprocessContainer($variables), fn() => template_preprocess_container($variables)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('Drupal\Core\Theme\ThemePreprocess')->preprocessLinks($variables), fn() => template_preprocess_links($variables)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('Drupal\Core\Theme\ThemePreprocess')->preprocessHtml($variables), fn() => template_preprocess_html($variables)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('Drupal\Core\Theme\ThemePreprocess')->preprocessPage($variables), fn() => template_preprocess_page($variables)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('Drupal\Core\Datetime\DatePreprocess')->preprocessTime($variables), fn() => template_preprocess_time($variables)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('Drupal\Core\Datetime\DatePreprocess')->preprocessDatetimeForm($variables), fn() => template_preprocess_datetime_form($variables)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('Drupal\Core\Datetime\DatePreprocess')->preprocessDatetimeWrapper($variables), fn() => template_preprocess_datetime_wrapper($variables)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/text_summary.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/text_summary.php.inc new file mode 100644 index 000000000..d1339671c --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/text_summary.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service('Drupal\text\TextSummary')->generate($body, $format, $size), fn() => text_summary($body, $format, $size)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/twig_render_template.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/twig_render_template.php.inc new file mode 100644 index 000000000..b5e5d8e7f --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/twig_render_template.php.inc @@ -0,0 +1,9 @@ + +----- + \Drupal::service('Drupal\Core\Template\TwigThemeEngine')->renderTemplate($template_file, $variables), fn() => twig_render_template($template_file, $variables)); +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/user_form_process_password_confirm.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/user_form_process_password_confirm.php.inc new file mode 100644 index 000000000..65376018d --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/user_form_process_password_confirm.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal::service('Drupal\user\Hook\UserThemeHooks')->processPasswordConfirm($element), fn() => user_form_process_password_confirm($element)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/views-add-contextual-links-2571679.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/views-add-contextual-links-2571679.php.inc new file mode 100644 index 000000000..e11ae19b0 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/views-add-contextual-links-2571679.php.inc @@ -0,0 +1,9 @@ + +----- + \Drupal::service(\Drupal\views\ContextualLinksHelper::class)->addLinks($element, 'view', $display_id), fn() => views_add_contextual_links($element, 'view', $display_id)); +?> diff --git a/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/views_procedural_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/views_procedural_functions.php.inc new file mode 100644 index 000000000..3dcfae473 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToServiceRector/fixture/views_procedural_functions.php.inc @@ -0,0 +1,17 @@ + +----- + \Drupal::service('views.field_data_provider')->getSqlStorageForField($field_storage), fn() => _views_field_get_entity_type_storage($field_storage)); + $labels = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('entity_field.manager')->getFieldLabels($entity_type, $field_name), fn() => views_entity_field_label($entity_type, $field_name)); + $data = \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal::service('views.field_data_provider')->defaultFieldImplementation($field_storage), fn() => views_field_default_views_data($field_storage)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/FunctionToStaticRectorTest.php b/tests/src/Rector/Deprecation/FunctionToStaticRector/FunctionToStaticRectorTest.php index 778a56e4c..3585ecdb9 100644 --- a/tests/src/Rector/Deprecation/FunctionToStaticRector/FunctionToStaticRectorTest.php +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/FunctionToStaticRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Rector\Deprecation\FunctionToStaticRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class FunctionToStaticRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class FunctionToStaticRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/config/configured_rule.php b/tests/src/Rector/Deprecation/FunctionToStaticRector/config/configured_rule.php index 5c2cec688..64f0d2168 100644 --- a/tests/src/Rector/Deprecation/FunctionToStaticRector/config/configured_rule.php +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/config/configured_rule.php @@ -14,5 +14,22 @@ new FunctionToStaticConfiguration('10.2.0', 'format_size', 'Drupal\Core\StringTranslation\ByteSizeMarkup', 'create'), new FunctionToStaticConfiguration('10.3.0', 'file_icon_class', 'Drupal\file\IconMimeTypes', 'getIconClass'), new FunctionToStaticConfiguration('10.3.0', 'file_icon_map', 'Drupal\file\IconMimeTypes', 'getGenericMimeType'), + // https://www.drupal.org/node/3574727 (Drupal 11.4) + new FunctionToStaticConfiguration('11.4.0', 'language_configuration_element_submit', 'Drupal\language\Element\LanguageConfiguration', 'submit'), + // https://www.drupal.org/node/3035340 (Drupal 11.4) + new FunctionToStaticConfiguration('11.4.0', 'views_ui_form_button_was_clicked', 'Drupal\views\ViewsFormHelperTrait', 'formButtonWasClicked'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_add_limited_validation', 'Drupal\views\ViewsFormAjaxHelperTrait', 'addLimitedValidation'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_add_ajax_wrapper', 'Drupal\views\ViewsFormAjaxHelperTrait', 'addAjaxWrapper'), + new FunctionToStaticConfiguration('11.4.0', 'views_ui_nojs_submit', 'Drupal\views\ViewsFormAjaxHelperTrait', 'noJsSubmit'), + // https://www.drupal.org/node/3534092 (Drupal 11.3) + new FunctionToStaticConfiguration('11.3.0', 'file_system_settings_submit', 'Drupal\file\Hook\FileHooks', 'settingsSubmit'), + // https://www.drupal.org/node/3534089 (Drupal 11.3) + new FunctionToStaticConfiguration('11.3.0', 'file_managed_file_submit', 'Drupal\file\Element\ManagedFile', 'submit'), + // https://www.drupal.org/node/3566774 (Drupal 11.4) + new FunctionToStaticConfiguration('11.4.0', '_media_library_configure_form_display', 'Drupal\media_library\MediaLibraryDisplayManager', 'configureFormDisplay'), + new FunctionToStaticConfiguration('11.4.0', '_media_library_configure_view_display', 'Drupal\media_library\MediaLibraryDisplayManager', 'configureViewDisplay'), + // https://www.drupal.org/node/3495966 (Drupal 11.2) + new FunctionToStaticConfiguration('11.2.0', 'entity_test_create_bundle', 'Drupal\entity_test\EntityTestHelper', 'createBundle'), + new FunctionToStaticConfiguration('11.2.0', 'entity_test_delete_bundle', 'Drupal\entity_test\EntityTestHelper', 'deleteBundle'), ]); }; diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/entity_test_bundle_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/entity_test_bundle_functions.php.inc new file mode 100644 index 000000000..d7387d743 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/entity_test_bundle_functions.php.inc @@ -0,0 +1,21 @@ + +----- + \Drupal\entity_test\EntityTestHelper::createBundle($bundle, $text, $entity_type), fn() => entity_test_create_bundle($bundle, $text, $entity_type)); +} + +function example_delete() { + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => \Drupal\entity_test\EntityTestHelper::deleteBundle($bundle, $entity_type), fn() => entity_test_delete_bundle($bundle, $entity_type)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/file_managed_file_submit.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/file_managed_file_submit.php.inc new file mode 100644 index 000000000..41c59c0b1 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/file_managed_file_submit.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal\file\Element\ManagedFile::submit($form, $form_state), fn() => file_managed_file_submit($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/file_system_settings_submit.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/file_system_settings_submit.php.inc new file mode 100644 index 000000000..60cd333c1 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/file_system_settings_submit.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal\file\Hook\FileHooks::settingsSubmit($form, $form_state), fn() => file_system_settings_submit($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/function_to_static_call.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/function_to_static_call.php.inc index 3191188a6..fbe2baa78 100644 --- a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/function_to_static_call.php.inc +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/function_to_static_call.php.inc @@ -33,7 +33,7 @@ function simple_example_file_icon_class() { function simple_example() { $settings = []; $filename = 'simple_filename.yaml'; - \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => \Drupal\Core\Site\SettingsEditor::rewrite($filename, $settings), fn() => drupal_rewrite_settings($settings, $filename)); + \Drupal\Core\Site\SettingsEditor::rewrite($filename, $settings); } /** diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/language_configuration_element_submit.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/language_configuration_element_submit.php.inc new file mode 100644 index 000000000..57094ee03 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/language_configuration_element_submit.php.inc @@ -0,0 +1,13 @@ + +----- + \Drupal\language\Element\LanguageConfiguration::submit($form, $form_state), fn() => language_configuration_element_submit($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/media_library_display_manager_static_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/media_library_display_manager_static_functions.php.inc new file mode 100644 index 000000000..ee73df690 --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/media_library_display_manager_static_functions.php.inc @@ -0,0 +1,15 @@ + +----- + \Drupal\media_library\MediaLibraryDisplayManager::configureFormDisplay($type), fn() => _media_library_configure_form_display($type)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\media_library\MediaLibraryDisplayManager::configureViewDisplay($type), fn() => _media_library_configure_view_display($type)); +} +?> diff --git a/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/views_ui_static_functions.php.inc b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/views_ui_static_functions.php.inc new file mode 100644 index 000000000..c37217f6a --- /dev/null +++ b/tests/src/Rector/Deprecation/FunctionToStaticRector/fixture/views_ui_static_functions.php.inc @@ -0,0 +1,19 @@ + +----- + \Drupal\views\ViewsFormHelperTrait::formButtonWasClicked($element, $form_state), fn() => views_ui_form_button_was_clicked($element, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\views\ViewsFormAjaxHelperTrait::addLimitedValidation($element, $form_state), fn() => views_ui_add_limited_validation($element, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\views\ViewsFormAjaxHelperTrait::addAjaxWrapper($element, $form_state), fn() => views_ui_add_ajax_wrapper($element, $form_state)); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.4.0', fn() => \Drupal\views\ViewsFormAjaxHelperTrait::noJsSubmit($form, $form_state), fn() => views_ui_nojs_submit($form, $form_state)); +} +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/MethodToMethodWithCheckRectorTest.php b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/MethodToMethodWithCheckRectorTest.php index 6aad8a87c..24867e25e 100644 --- a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/MethodToMethodWithCheckRectorTest.php +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/MethodToMethodWithCheckRectorTest.php @@ -4,16 +4,14 @@ namespace DrupalRector\Tests\Rector\Deprecation\MethodToMethodWithCheckRector; +use DrupalRector\Services\DrupalRectorSettings; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class MethodToMethodWithCheckRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class MethodToMethodWithCheckRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); @@ -27,6 +25,21 @@ public static function provideData(): \Iterator return self::yieldFilesFromDirectory(__DIR__.'/fixture'); } + #[\PHPUnit\Framework\Attributes\DataProvider('provideDataBelowVersion')] + public function testBelowVersion(string $filePath): void + { + static::getContainer()->make(DrupalRectorSettings::class)->setDrupalVersion('1.0.0'); + $this->doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideDataBelowVersion(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture-below-version'); + } + public function provideConfigFilePath(): string { // must be implemented diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/config/configured_rule.php b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/config/configured_rule.php index 9e886e74c..e023819ec 100644 --- a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/config/configured_rule.php +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/config/configured_rule.php @@ -8,10 +8,13 @@ use Rector\Config\RectorConfig; return static function (RectorConfig $rectorConfig): void { - DeprecationBase::addClass(MethodToMethodWithCheckRector::class, $rectorConfig, true, [ - new MethodToMethodWithCheckConfiguration('Drupal\Core\Session\MetadataBag', 'clearCsrfTokenSeed', 'stampNew'), - new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityInterface', 'urlInfo', 'toUrl'), - new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage'), - new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage'), + DeprecationBase::addClass(MethodToMethodWithCheckRector::class, $rectorConfig, false, [ + new MethodToMethodWithCheckConfiguration('Drupal\Core\Session\MetadataBag', 'clearCsrfTokenSeed', 'stampNew', '9.2.0'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Entity\EntityInterface', 'urlInfo', 'toUrl', '8.0.0'), + new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage', '10.2.0'), + new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage', '10.2.0'), + new MethodToMethodWithCheckConfiguration('Drupal\path_alias\AliasManager', 'pathAliasWhitelistRebuild', 'pathAliasPrefixListRebuild', '11.1.0'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Cache\CacheBackendInterface', 'invalidateAll', 'deleteAll', '11.2.0'), + new MethodToMethodWithCheckConfiguration('Drupal\Core\Render\RendererInterface', 'renderPlain', 'renderInIsolation', '10.3.0'), ]); }; diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture-below-version/cache_invalidate_all.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture-below-version/cache_invalidate_all.php.inc new file mode 100644 index 000000000..83fd06e5c --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture-below-version/cache_invalidate_all.php.inc @@ -0,0 +1,11 @@ +invalidateAll(); +?> +----- +invalidateAll(); +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture-below-version/renderer_render_plain.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture-below-version/renderer_render_plain.php.inc new file mode 100644 index 000000000..a5c75d48a --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture-below-version/renderer_render_plain.php.inc @@ -0,0 +1,15 @@ + 'Hello']; + $renderer->renderPlain($elements); +} +?> +----- + 'Hello']; + $renderer->renderPlain($elements); +} +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/basic.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/basic.php.inc index c4d2afddc..9845bf8e6 100644 --- a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/basic.php.inc +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/basic.php.inc @@ -25,7 +25,7 @@ function simple_example() { $form_state->setRedirectUrl($untranslated_entity->toUrl('canonical')); $toolkit = new \Drupal\system\Plugin\ImageToolkit\GDToolkit; - $toolkit->getImage(); - $toolkit->setImage(); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.2.0', fn() => $toolkit->getImage(), fn() => $toolkit->getResource()); + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.2.0', fn() => $toolkit->setImage(), fn() => $toolkit->setResource()); } ?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/cache_invalidate_all.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/cache_invalidate_all.php.inc new file mode 100644 index 000000000..f4d4a5cb3 --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/cache_invalidate_all.php.inc @@ -0,0 +1,11 @@ +invalidateAll(); +?> +----- + $cache->deleteAll(), fn() => $cache->invalidateAll()); +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/cache_invalidate_all_service.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/cache_invalidate_all_service.php.inc new file mode 100644 index 000000000..bc63809cf --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/cache_invalidate_all_service.php.inc @@ -0,0 +1,13 @@ +invalidateAll(); + +Drupal::service('cache.render')->invalidateAll(); +?> +----- + \Drupal::service('cache.render')->deleteAll(), fn() => \Drupal::service('cache.render')->invalidateAll()); + +\Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '11.2.0', fn() => Drupal::service('cache.render')->deleteAll(), fn() => Drupal::service('cache.render')->invalidateAll()); +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/path_alias_whitelist_method.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/path_alias_whitelist_method.php.inc new file mode 100644 index 000000000..5f46d5547 --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/path_alias_whitelist_method.php.inc @@ -0,0 +1,11 @@ +pathAliasWhitelistRebuild($path); +?> +----- + $alias_manager->pathAliasPrefixListRebuild($path), fn() => $alias_manager->pathAliasWhitelistRebuild($path)); +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/renderer_render_plain.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/renderer_render_plain.php.inc new file mode 100644 index 000000000..3de7f29ac --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/renderer_render_plain.php.inc @@ -0,0 +1,33 @@ + 'Hello']; + $renderer->renderPlain($elements); +} + +// \Drupal::service() with @var RendererInterface docblock. +function service_locator_example() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $elements = ['#markup' => 'Hello']; + $renderer->renderPlain($elements); +} +?> +----- + 'Hello']; + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => $renderer->renderInIsolation($elements), fn() => $renderer->renderPlain($elements)); +} + +// \Drupal::service() with @var RendererInterface docblock. +function service_locator_example() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $elements = ['#markup' => 'Hello']; + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => $renderer->renderInIsolation($elements), fn() => $renderer->renderPlain($elements)); +} +?> diff --git a/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/renderer_renderer_class.php.inc b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/renderer_renderer_class.php.inc new file mode 100644 index 000000000..3fec4369c --- /dev/null +++ b/tests/src/Rector/Deprecation/MethodToMethodWithCheckRector/fixture/renderer_renderer_class.php.inc @@ -0,0 +1,15 @@ + 'Hello']; + $renderer->renderPlain($elements); +} +?> +----- + 'Hello']; + \Drupal\Component\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.3.0', fn() => $renderer->renderInIsolation($elements), fn() => $renderer->renderPlain($elements)); +} +?> diff --git a/tests/src/Rector/PHPUnit/ShouldCallParentMethodsRector/ShouldCallParentMethodsRectorTest.php b/tests/src/Rector/PHPUnit/ShouldCallParentMethodsRector/ShouldCallParentMethodsRectorTest.php index 999cb72c8..14da9386b 100644 --- a/tests/src/Rector/PHPUnit/ShouldCallParentMethodsRector/ShouldCallParentMethodsRectorTest.php +++ b/tests/src/Rector/PHPUnit/ShouldCallParentMethodsRector/ShouldCallParentMethodsRectorTest.php @@ -4,16 +4,13 @@ namespace DrupalRector\Tests\Rector\PHPUnit\ShouldCallParentMethodsRector; +use DrupalRector\Tests\AbstractDrupalRectorTestCase; use Iterator; -use Rector\Testing\PHPUnit\AbstractRectorTestCase; -class ShouldCallParentMethodsRectorTest extends AbstractRectorTestCase +#[\PHPUnit\Framework\Attributes\CoversFunction('refactor')] +class ShouldCallParentMethodsRectorTest extends AbstractDrupalRectorTestCase { - /** - * @covers ::refactor - * - * @dataProvider provideData - */ + #[\PHPUnit\Framework\Attributes\DataProvider('provideData')] public function test(string $filePath): void { $this->doTestFile($filePath); diff --git a/tests/src/Services/DrupalRectorSettingsTest.php b/tests/src/Services/DrupalRectorSettingsTest.php new file mode 100644 index 000000000..9b82b199d --- /dev/null +++ b/tests/src/Services/DrupalRectorSettingsTest.php @@ -0,0 +1,92 @@ +isBackwardCompatibilityEnabled()); + self::assertSame('10.1.0', $settings->getMinimumCoreVersionSupported()); + self::assertNull($settings->getDrupalVersion()); + } + + public function testDisableBackwardCompatibilityReturnsSelf(): void + { + $settings = new DrupalRectorSettings(); + $result = $settings->disableBackwardCompatibility(); + + self::assertSame($settings, $result); + self::assertFalse($settings->isBackwardCompatibilityEnabled()); + } + + public function testEnableBackwardCompatibilityAfterDisable(): void + { + $settings = new DrupalRectorSettings(); + $settings->disableBackwardCompatibility(); + $settings->enableBackwardCompatibility(); + + self::assertTrue($settings->isBackwardCompatibilityEnabled()); + } + + public function testEnableBackwardCompatibilityReturnsSelf(): void + { + $settings = new DrupalRectorSettings(); + $result = $settings->enableBackwardCompatibility(); + + self::assertSame($settings, $result); + } + + public function testSetMinimumCoreVersionSupported(): void + { + $settings = new DrupalRectorSettings(); + $result = $settings->setMinimumCoreVersionSupported('11.0.0'); + + self::assertSame($settings, $result); + self::assertSame('11.0.0', $settings->getMinimumCoreVersionSupported()); + } + + public function testSetMinimumCoreVersionSupportedThrowsOnEmpty(): void + { + $this->expectException(\InvalidArgumentException::class); + + (new DrupalRectorSettings())->setMinimumCoreVersionSupported(''); + } + + public function testSetDrupalVersion(): void + { + $settings = new DrupalRectorSettings(); + $result = $settings->setDrupalVersion('99.99.99'); + + self::assertSame($settings, $result); + self::assertSame('99.99.99', $settings->getDrupalVersion()); + } + + public function testSetDrupalVersionCanBeResetToNull(): void + { + $settings = new DrupalRectorSettings(); + $settings->setDrupalVersion('11.0.0'); + $settings->setDrupalVersion(null); + + self::assertNull($settings->getDrupalVersion()); + } + + public function testFluentChain(): void + { + $settings = (new DrupalRectorSettings()) + ->disableBackwardCompatibility() + ->setMinimumCoreVersionSupported('10.5.0') + ->setDrupalVersion('10.5.1'); + + self::assertFalse($settings->isBackwardCompatibilityEnabled()); + self::assertSame('10.5.0', $settings->getMinimumCoreVersionSupported()); + self::assertSame('10.5.1', $settings->getDrupalVersion()); + } +}