Modernizing PHPDoc Array annotations with Rector
In one of my project, Iβm working with Symfony Serializer, phpstan/phpdoc-parser and phpdocumentor/reflection-docblock to handle serialization of complex objects.
These tools rely on PHPDoc annotations to understand data types, particularly for arrays of objects.
In PHPDocs, there are (at least) 2 possible syntaxes for array values:
/**
* @param Foo[] $arrayOfFoos
*//**
* @param array<Foo> $arrayOfFoos
*/Since 8.1 we have the concept of list in PHP.
That makes Foo[] ambiguous because is it could be:
- a list
- an array where the key is important
list<Foo>is almost the same asarray<Foo>(implicitintkeys) orarray<int, Foo>with a slight difference you can be sure that integers in keys consecutive numbers from 0 up.
But hereβs where things get interesting: with recent updates to tools like Psalm, PHPStan and Symfonyβs Reflection-based Serializer handler, this Type[]|array syntax started causing issues. These tools began struggling with the ambiguity - they couldn't reliably determine whether we meant a strict list or a more flexible array structure.
Exploring Options
I therefore had many solution to resolve my issue:
Faced with hundreds of annotations needing updates across my codebase, I had three potential paths:
1. Manual transformation β Copy-paste and find-replace across hundreds of files. Letβs be honest, this would be error-prone and mind-numbing work. Hard pass.
2. AI-assisted refactoring β Ask Claude or another AI to parse my entire codebase. While tempting, my project has thousands of files that would exceed token limits, and I wanted a repeatable, version-controllable solution.
3. Rector rule β Write once, run everywhere, with the confidence that comes from automated refactoring tools. This felt like the best approach.
The choice was obvious. Plus, creating a Rector rule means other developers facing the same challenge can benefit from the work.
Plot twist: While writing this article, I discovered that PHP CS Fixer already provides a similar rule called phpdoc_array_type that handles these transformations! Sometimes reinventing the wheel teaches you how wheels work β¨
A custom Rector Rule
So, instead of manually transforming hundreds of annotations in my project, I created a custom Rector rule that automates this task.
Hereβs how it works:
The TransformArrayTypeAnnotationRector Rule
final class TransformArrayTypeAnnotationRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Transform array type annotations from "Type[]|array" to "array<int, Type>"',
[
new CodeSample(
'/**
* @var \App\Entity\Environment[]|array
*/
private $environments;',
'/**
* @var array<int, \App\Entity\Environment>
*/
private $environments;'
),
]
);
}
public function getNodeTypes(): array
{
return [Property::class, ClassMethod::class, Class_::class];
}
public function refactor(Node $node): ?Node
{
$docComment = $node->getDocComment();
if (null === $docComment) {
return null;
}
$originalText = $docComment->getText();
$transformedText = $this->transformDocComment($originalText);
if ($originalText === $transformedText) {
return null;
}
$node->setDocComment(new Doc($transformedText));
return $node;
}
}Configuration and Usage
In my rector.php file, I registered my custom rule:
<?php
use App\Utils\Rector\TransformArrayTypeAnnotationRector;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__.'/src',
__DIR__.'/tests',
]);
$rectorConfig->rule(TransformArrayTypeAnnotationRector::class);
};Results
Running this rule on my project automatically transformed 243 files in seconds:
./vendor/bin/rector
243/243 [ββββββββββββββββββββββββββββ] 100%
243 files with changes
Applied rules:
* TransformArrayTypeAnnotationRector
[OK] 243 files have been changed by RectorEach transformation follows this pattern:
/**
- * @var \App\Dto\Output\Category[]|array
+ * @var array<int, \App\Dto\Output\Category>
*/β€οΈ Love on your keyboards.
Sources
PHPStan (Sept 2025 ) PHPDoc Types.
https://phpstan.org/writing-php-code/phpdoc-types
PHP (Feb 2022) array_is_list.
https://www.php.net/manual/en/function.array-is-list.php
PHP Coding Standards Fixer (Feb 2019) PHPDoc array<T> type must be used instead of T[].
https://cs.symfony.com/doc/rules/phpdoc/phpdoc_array_type.html
Resources
Claude AI, https://claude.ai
Helped with writing and text refinement.
Imagen, https://deepmind.google/models/imagen/
Generated the very accurate articleβs cover illustration.
