Drupal Code Optimizer
Optimize selected PHP code for the Drupal and PHP versions active in the current project.
Step 1 — Detect versions
Do not assume versions. Detect them from the project files before touching any code.
Drupal version — check in order, stop at first match:
grep -A1 '"name": "drupal/core"' composer.lock | grep version
cat web/core/lib/Drupal.php | grep "const VERSION"
drush status --field=drupal-version 2>/dev/null
PHP version — check in order, stop at first match:
php --version | head -1
grep '"php"' composer.json
cat .php-version 2>/dev/null || grep php .tool-versions 2>/dev/null
If either version cannot be determined, ask the user before proceeding.
Step 2 — Analyse the selection
Identify what the selected code is:
| Type | Examples |
|---|---|
| Hook implementation | my_module_entity_presave(), my_module_form_alter() |
| Service / injectable class | Constructor, methods using \Drupal::* |
| Plugin | Block, FieldFormatter, QueueWorker |
| Twig preprocess | my_module_preprocess_node() |
| Controller / form class | Extending ControllerBase, FormBase |
| Procedural utility | Standalone functions, static helpers |
| Other | Route subscriber, access checker, event subscriber |
Only apply optimizations available in the detected versions. Do not suggest features from newer versions.
Step 3 — Apply optimizations
PHP version-gated improvements
| Available from | Optimization |
|---|---|
| PHP 8.0 | Union types, match expression, named arguments, nullsafe operator ?->, constructor property promotion |
| PHP 8.1 | Enums, readonly properties, never return type, array unpacking with string keys |
| PHP 8.2 | Readonly classes, true/false/null as standalone types, DNF types |
| PHP 8.3 | Typed class constants, json_validate(), #[Override] attribute |
Always add declare(strict_types=1) if absent.
Drupal version-gated improvements
| Available from | Optimization |
|---|---|
| Drupal 10.0 | Symfony 6 compatibility; verify no removed D9 APIs are used |
| Drupal 10.2 | Single-file component (SDC) support |
| Drupal 11.0 | #[Hook] attributes stable (\Drupal\Core\Hook\Attribute\Hook); jQuery UI removed; CKEditor 5 only |
Do not suggest #[Hook] attributes for projects on Drupal 9.x or 10.x — they were experimental and not stable before D11.
Always apply (version-independent)
- Replace
\Drupal::service()/\Drupal::*()inside injectable classes with constructor injection. - Keep
\Drupal::service()only at hook entry points as a bridge to the service layer. - Replace procedural hook bodies with thin wrappers delegating to a service class.
- Replace deprecated
@Annotationplugin discovery with PHP 8#[Attribute]where the Drupal version supports it. - Add missing return types and parameter types.
- Use interface type hints, not concrete class type hints.
- Remove unused
usestatements; add missing ones. - Replace
array()with[]. - Replace
strpos() !== falsewithstr_contains()(PHP 8.0+). - Replace
substr()prefix/suffix checks withstr_starts_with()/str_ends_with()(PHP 8.0+). - Flag any calls to APIs removed or deprecated in the detected Drupal version.
Step 4 — Output format
Versions detected
- Drupal: X.Y.Z
- PHP: X.Y.Z
Changes made — one bullet per change, stating what changed and why.
Optimized code — complete rewritten code, ready to drop in. No fragments.
Remaining notes — anything requiring human review: deprecations with no direct replacement, schema/routing/YAML changes needed alongside the PHP change.
Step 5 — Flag but do not auto-change
Flag these separately rather than silently rewriting them:
- Business logic that may have unintended side effects if restructured.
- Hooks touching entity saves where infinite resave loops are possible.
- Any change requiring a cache rebuild, config import, or schema update.
- Backwards-compatibility code that may still be needed for older environments.