rectorphp/rector · error · ShouldNotHappenException
"%s" is deprecated as noisy change with little value. Use ma
Error message
"%s" is deprecated as noisy change with little value. Use manually or custom rule where needed instead
What it means
StaticClosureRector used to prepend static to closures (function () {...} -> static function () {...}) to prevent implicit $this binding. It is deprecated as a noisy change with little value — it rewrites every closure in a codebase for a micro-optimisation most projects never need. refactor() now throws ShouldNotHappenException on every Closure node; the rule is a tombstone that only exists to break configs that still enable it.
Source
Thrown at rules/CodingStyle/Rector/Closure/StaticClosureRector.php:52
return 2;
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Closure::class];
}
/**
* @param Closure $node
*/
public function refactor(Node $node): ?Node
{
throw new ShouldNotHappenException(sprintf('"%s" is deprecated as noisy change with little value. Use manually or custom rule where needed instead', self::class));
}
}
View on GitHub (pinned to 408fcb0ff1)
Solutions
- Remove StaticClosureRector::class from rector.php and all imported set files, then re-run vendor/bin/rector dry-run
- Mark only the closures that actually leak or hold references (long-lived listeners, queued callbacks) as static, manually
- For blanket enforcement use PHP-CS-Fixer's static_lambda fixer, which is the appropriate tool for this style rule
- grep -r StaticClosureRector across the repository to find every remaining registration
Example fix
// before (rector.php)
->withRules([
\Rector\CodingStyle\Rector\Closure\StaticClosureRector::class,
])
// after
// rule removed; annotate hot/leak-prone closures by hand:
$ids = array_map(static fn (Item $i) => $i->id, $items); Defensive patterns
Strategy: validation
Validate before calling
$deprecated = ['StaticClosureRector'];
foreach (glob(__DIR__ . '/rector*.php') as $config) {
$src = file_get_contents($config);
foreach ($deprecated as $rule) {
if (str_contains($src, $rule)) {
fwrite(STDERR, "Remove deprecated rule {$rule} from {$config}\n");
exit(1);
}
}
} Prevention
- Use PHP-CS-Fixer static_lambda for blanket static-closure style
- Annotate static manually on closures passed to long-lived consumers
- Remove deprecated rules from rector.php before composer update
- Keep a CI dry-run so stale registrations fail visibly
When it happens
Trigger: Registering rules/CodingStyle/Rector/Closure/StaticClosureRector.php in config and running process/dry-run over any file containing a closure. The throw comes from rules/CodingStyle/Rector/Closure/StaticClosureRector.php:52 on the first closure visited.
Common situations: This rule was part of widely-copied CodingStyle setups, so many rector.php files and tutorial-derived set bundles still list it. After upgrading rector/rector-prefixed, the first dry-run over callback-heavy code (Collections, event dispatchers, array_map) crashes with this exception.
Related errors
- "%s" is deprecated as depends on context and personal prefer
- "%s" rule is deprecated, as it is a personal preference that
- "%s" is deprecated, as simplifying regex ranges is a persona
- "%s" rule is deprecated, as it is a personal preference that
- "%s" is deprecated as noisy change with little value. Use ma
AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21).
Data as JSON: /api/errors/ae804755ab7a9abd.
Report an issue: GitHub.