Use a custom Rector rule to automatically strip noisy inline comments and empty docblocks across your codebase while preserving essential PHPDoc annotations.
Codebases often accumulate redundant comments over time: commented-out legacy code, obvious explanations (// set user name), or empty boilerplate docblocks generated by older IDE templates.
Manually reviewing and removing thousands of stale comments across hundreds of files is time-consuming. Using Rector, you can define an AbstractRector rule that strips non-essential comments while retaining critical PHPDoc annotations such as @param, @return, and @throws.
The Rector Implementation
namespace App\Rector;
use PhpParser\Comment;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class RemoveUnnecessaryCommentsRector extends AbstractRector
{
// Whitelist specific comment strings or tags you want to keep
private const ALLOWED_PATTERNS = [
'@todo',
'@var',
];
public function getNodeTypes(): array
{
return [Node::class];
}
public function refactor(Node $node): ?Node
{
$comments = $node->getComments();
if ($comments === []) {
return null;
}
$filtered = [];
$changed = false;
foreach ($comments as $comment) {
if ($comment instanceof Doc) {
$cleaned = $this->cleanDocBlock($comment);
if ($cleaned !== $comment->getText()) {
if ($cleaned !== '') {
$filtered[] = new Doc($cleaned);
}
$changed = true;
} else {
$filtered[] = $comment;
}
continue;
}
// Check if regular comment contains allowed keywords
if ($this->isAllowed($comment->getText())) {
$filtered[] = $comment;
} else {
$changed = true;
}
}
if (! $changed) {
return null;
}
$node->setAttribute('comments', $filtered);
return $node;
}
private function cleanDocBlock(Doc $doc): string
{
$lines = preg_split('/\R/', $doc->getText());
$tags = [];
foreach ($lines as $line) {
$line = trim(preg_replace('/^\s*\*\s?/', '', $line));
if ($line === '') {
continue;
}
// Preserve standard PHPDoc annotations (@param, @return, @throws, etc.)
if (str_starts_with($line, '@')) {
$tags[] = $line;
}
}
if ($tags === []) {
return '';
}
return "/**\n * " . implode("\n * ", $tags) . "\n */";
}
private function isAllowed(string $text): bool
{
foreach (self::ALLOWED_PATTERNS as $allowed) {
if (str_contains($text, $allowed)) {
return true;
}
}
return false;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove redundant inline comments while retaining essential PHPDoc tags', [
new CodeSample(
<<<'CODE'
// Initialize user model
$user = new User();
CODE
,
<<<'CODE'
$user = new User();
CODE
),
]);
}
}
Registering in rector.php
Add the custom rule to your project's Rector configuration:
use App\Rector\RemoveUnnecessaryCommentsRector;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withRules([
RemoveUnnecessaryCommentsRector::class,
]);
Run the refactoring dry-run to preview changes:
vendor/bin/rector process --dry-run
Summary
- Use AST-based comment manipulation in Rector to systematically clean comment noise across large codebases.
- The rule strips redundant plain comments (
// comment) while keeping meaningful docblock tags (@param,@return,@throws). - Easily extensible to whitelist specific tags or maintenance markers such as
@todo.
Related Tips
View all tips →Refactor PHP Enum Cases from snake_case to PascalCase with Rector
Automate renaming PHP 8.1+ enum cases from SNAKE_CASE to PascalCase across PHP classes and Blade templates using Rector and an Artisan refactoring script.
Automate PHP Readonly Class Refactoring with Rector
Use Rector rules to automatically convert immutable DTOs and value objects into native PHP 8.2 readonly classes.