mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-18 22:08:00 +01:00
[PHP] Add MultiExceptionCatchRector
This commit is contained in:
parent
947f0f3e7d
commit
658cef5fc2
@ -2,3 +2,5 @@ services:
|
||||
Rector\Php\Rector\BinaryOp\IsIterableRector: ~
|
||||
Rector\Php\Rector\Name\ReservedObjectRector:
|
||||
Object: 'BaseObject'
|
||||
|
||||
Rector\Php\Rector\TryCatch\MultiExceptionCatchRector: ~
|
||||
|
125
packages/Php/src/Rector/TryCatch/MultiExceptionCatchRector.php
Normal file
125
packages/Php/src/Rector/TryCatch/MultiExceptionCatchRector.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php\Rector\TryCatch;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\TryCatch;
|
||||
use Rector\Printer\BetterStandardPrinter;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/multiple-catch
|
||||
*/
|
||||
final class MultiExceptionCatchRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var BetterStandardPrinter
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
public function __construct(BetterStandardPrinter $betterStandardPrinter)
|
||||
{
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Changes multi catch of same exception to single one | separated.',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1 $exception) {
|
||||
$sameCode;
|
||||
} catch (ExceptionType2 $exception) {
|
||||
$sameCode;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1 | ExceptionType2 $exception) {
|
||||
$sameCode;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [TryCatch::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TryCatch $tryCatchNode
|
||||
*/
|
||||
public function refactor(Node $tryCatchNode): ?Node
|
||||
{
|
||||
if (count($tryCatchNode->catches) < 2) {
|
||||
return $tryCatchNode;
|
||||
}
|
||||
|
||||
$catchKeysByContent = $this->collectCatchKeysByContent($tryCatchNode);
|
||||
|
||||
foreach ($catchKeysByContent as $keys) {
|
||||
// no duplicates
|
||||
if (count($keys) < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$collectedTypes = $this->collectTypesFromCatchedByIds($tryCatchNode, $keys);
|
||||
$firstTryKey = array_shift($keys);
|
||||
$tryCatchNode->catches[$firstTryKey]->types = $collectedTypes;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
unset($tryCatchNode->catches[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// reindex from 0 for printer
|
||||
$tryCatchNode->catches = array_values($tryCatchNode->catches);
|
||||
|
||||
return $tryCatchNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[][]
|
||||
*/
|
||||
private function collectCatchKeysByContent(TryCatch $tryCatchNode): array
|
||||
{
|
||||
$catchKeysByContent = [];
|
||||
foreach ($tryCatchNode->catches as $key => $catch) {
|
||||
$catchContent = $this->betterStandardPrinter->prettyPrint($catch->stmts);
|
||||
/** @var int $key */
|
||||
$catchKeysByContent[$catchContent][] = $key;
|
||||
}
|
||||
|
||||
return $catchKeysByContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $keys
|
||||
* @return Name[]
|
||||
*/
|
||||
private function collectTypesFromCatchedByIds(TryCatch $tryCatchNode, array $keys): array
|
||||
{
|
||||
$collectedTypes = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$collectedTypes = array_merge($collectedTypes, $tryCatchNode->catches[$key]->types);
|
||||
}
|
||||
|
||||
return $collectedTypes;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1|ExceptionType2 $e) {
|
||||
// Code to handle the exception
|
||||
} catch (Exception $e) {
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1 $e) {
|
||||
// Code to handle the exception
|
||||
} catch (ExceptionType2 $e) {
|
||||
$differentContent = 'hey';
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1|ExceptionType2 $e) {
|
||||
$e = 1;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php\Tests\Rector\TryCatch\MultiExceptionCatchRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
/**
|
||||
* @covers \Rector\Php\Rector\TryCatch\MultiExceptionCatchRector
|
||||
*/
|
||||
final class MultiExceptionCatchRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideWrongToFixedFiles()
|
||||
*/
|
||||
public function test(string $wrong, string $fixed): void
|
||||
{
|
||||
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
|
||||
}
|
||||
|
||||
public function provideWrongToFixedFiles(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
|
||||
}
|
||||
|
||||
protected function provideConfig(): string
|
||||
{
|
||||
return __DIR__ . '/config.yml';
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1 $e) {
|
||||
// Code to handle the exception
|
||||
} catch (ExceptionType2 $e) {
|
||||
// Code to handle the exception
|
||||
} catch (Exception $e) {
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1 $e) {
|
||||
// Code to handle the exception
|
||||
} catch (ExceptionType2 $e) {
|
||||
$differentContent = 'hey';
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Some code...
|
||||
} catch (ExceptionType1 $e) {
|
||||
$e = 1;
|
||||
} catch (ExceptionType2 $e2) {
|
||||
$e = 1;
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
services:
|
||||
Rector\Php\Rector\TryCatch\MultiExceptionCatchRector: ~
|
@ -41,6 +41,7 @@ parameters:
|
||||
# false postive - type is set by annotatoin above
|
||||
- '#Access to an undefined property PhpParser\\Node::\$(args|var)#'
|
||||
- '#Call to function is_string\(\) with PhpParser\\Node\\Expr\|PhpParser\\Node\\Identifier will always evaluate to false#'
|
||||
- '#Method Rector\\Php\\Rector\\TryCatch\\MultiExceptionCatchRector\:\:collectCatchKeysByContent\(\) should return array<array<int\>\> but returns array<string, array<int, int\|string\>\>#'
|
||||
|
||||
# irelevant
|
||||
- '#Call to function in_array\(\) with arguments string, array<array<string\|false>> and true will always evaluate to false#'
|
||||
|
Loading…
x
Reference in New Issue
Block a user