Bug Report
| Subject |
Details |
| Rector version |
2.5.4; also reproduced with 2.4.6, 2.5.0, 2.5.2 |
| PHP version |
8.3.32 CLI |
| Responsible rule |
Rector\CodeQuality\Rector\If_\ShortenElseIfRector |
ShortenElseIfRector can produce invalid PHP when rewriting a pure-PHP file that uses alternative control syntax.
This is related to #9520 / rector-src#7686, but not fixed by them: that fix only skips files where $file->containsHTML() is true. This reproducer has no inline HTML, so HTMLAverseRectorInterface does not protect it.
Rector reports success, exits 0, and writes an unparseable file.
Minimal PHP Code Causing Issue
repro.php
<?php
for ($n = 1; $n <= $t; $n++) :
if ($n == $c) :
$a = 1;
else :
if ($x || $n <= $e) :
$a = 2;
elseif ($d && ! $x) :
$a = 3;
endif;
endif;
endfor;
rector.php
<?php
declare(strict_types=1);
use Rector\CodeQuality\Rector\If_\ShortenElseIfRector;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([__DIR__ . '/repro.php']);
$rectorConfig->rule(ShortenElseIfRector::class);
};
Run:
rector process repro.php
php -l repro.php
Actual result written by Rector:
<?php
for ($n = 1; $n <= $t; $n++) :
if ($n == $c) {
$a = 1;
} elseif ($x || $n <= $e) {
$a = 2;
} elseif ($d && ! $x) :
$a = 3;
endfor;
php -l:
PHP Parse error: syntax error, unexpected token ":" in repro.php on line 8
Errors parsing repro.php
The invalid construct is:
Expected Behaviour
Either skip this transformation for alternative control syntax, or emit a syntactically consistent rewrite.
No change would be acceptable.
A full brace-style rewrite would also be acceptable:
<?php
for ($n = 1; $n <= $t; $n++) {
if ($n == $c) {
$a = 1;
} elseif ($x || $n <= $e) {
$a = 2;
} elseif ($d && ! $x) {
$a = 3;
}
}
Likely cause:
rules/CodeQuality/Rector/If_/ShortenElseIfRector.php#L94-L96
$node->elseifs[] = new ElseIf_($if->cond, $if->stmts);
$node->else = $if->else;
$node->elseifs = array_merge($node->elseifs, $if->elseifs);
The newly created ElseIf_ prints as brace syntax, while the nested if's pre-existing elseif nodes can retain alternative-syntax format-preserving attributes. The result is one if/elseif chain with mixed brace/colon syntax, and the required endif; is gone.
Bug Report
Rector\CodeQuality\Rector\If_\ShortenElseIfRectorShortenElseIfRectorcan produce invalid PHP when rewriting a pure-PHP file that uses alternative control syntax.This is related to #9520 / rector-src#7686, but not fixed by them: that fix only skips files where
$file->containsHTML()is true. This reproducer has no inline HTML, soHTMLAverseRectorInterfacedoes not protect it.Rector reports success, exits 0, and writes an unparseable file.
Minimal PHP Code Causing Issue
repro.phprector.phpRun:
Actual result written by Rector:
php -l:The invalid construct is:
Expected Behaviour
Either skip this transformation for alternative control syntax, or emit a syntactically consistent rewrite.
No change would be acceptable.
A full brace-style rewrite would also be acceptable:
Likely cause:
rules/CodeQuality/Rector/If_/ShortenElseIfRector.php#L94-L96
The newly created
ElseIf_prints as brace syntax, while the nestedif's pre-existingelseifnodes can retain alternative-syntax format-preserving attributes. The result is oneif/elseifchain with mixed brace/colon syntax, and the requiredendif;is gone.