Skip to content

Commit 178aefc

Browse files
committed
Add rectors
1 parent ec3c70e commit 178aefc

12 files changed

Lines changed: 548 additions & 0 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Tempest\Upgrade\Set\TempestSetList;
7+
8+
return static function (RectorConfig $config): void {
9+
$config->sets([
10+
TempestSetList::TEMPEST_20,
11+
TempestSetList::TEMPEST_28,
12+
TempestSetList::TEMPEST_30,
13+
TempestSetList::TEMPEST_34,
14+
TempestSetList::TEMPEST_310,
15+
TempestSetList::TEMPEST_312,
16+
]);
17+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
use Rector\Config\RectorConfig;
4+
use Rector\Configuration\Option;
5+
use Rector\Configuration\Parameter\SimpleParameterProvider;
6+
use Tempest\Upgrade\Tempest312\UpdateConnectionImplementationsRector;
7+
use Tempest\Upgrade\Tempest312\UpdateKernelImplementationsRector;
8+
9+
return static function (RectorConfig $config): void {
10+
SimpleParameterProvider::setParameter(Option::AUTO_IMPORT_NAMES, value: true);
11+
SimpleParameterProvider::setParameter(Option::IMPORT_SHORT_CLASSES, value: true);
12+
13+
$config->rule(UpdateConnectionImplementationsRector::class);
14+
$config->rule(UpdateKernelImplementationsRector::class);
15+
};

packages/upgrade/src/Set/TempestLevelSetList.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ final class TempestLevelSetList
1515
public const string UP_TO_TEMPEST_34 = __DIR__ . '/../../config/sets/level/up-to-tempest-34.php';
1616

1717
public const string UP_TO_TEMPEST_310 = __DIR__ . '/../../config/sets/level/up-to-tempest-310.php';
18+
19+
public const string UP_TO_TEMPEST_312 = __DIR__ . '/../../config/sets/level/up-to-tempest-312.php';
1820
}

packages/upgrade/src/Set/TempestSetList.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ final class TempestSetList
1515
public const string TEMPEST_34 = __DIR__ . '/../../config/sets/tempest34.php';
1616

1717
public const string TEMPEST_310 = __DIR__ . '/../../config/sets/tempest310.php';
18+
19+
public const string TEMPEST_312 = __DIR__ . '/../../config/sets/tempest312.php';
1820
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Tempest\Upgrade\Tempest312;
4+
5+
use PhpParser\Modifiers;
6+
use PhpParser\Node;
7+
use PhpParser\Node\Expr\ConstFetch;
8+
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Name;
10+
use PhpParser\Node\Stmt\Class_;
11+
use PhpParser\Node\Stmt\ClassMethod;
12+
use PhpParser\Node\Stmt\Return_;
13+
use Rector\Rector\AbstractRector;
14+
use Tempest\Database\Connection\Connection;
15+
16+
final class UpdateConnectionImplementationsRector extends AbstractRector
17+
{
18+
private const array METHODS = [
19+
'inTransaction' => 'bool',
20+
'ping' => 'bool',
21+
'reconnect' => 'void',
22+
];
23+
24+
public function getNodeTypes(): array
25+
{
26+
return [
27+
Class_::class,
28+
];
29+
}
30+
31+
public function refactor(Node $node): ?Node
32+
{
33+
if (! $node instanceof Class_) {
34+
return null;
35+
}
36+
37+
if (! $this->implementsConnection($node)) {
38+
return null;
39+
}
40+
41+
$hasChanged = false;
42+
43+
foreach (self::METHODS as $methodName => $returnType) {
44+
if ($node->getMethod($methodName) instanceof ClassMethod) {
45+
continue;
46+
}
47+
48+
$node->stmts[] = $this->createMethod($methodName, $returnType);
49+
$hasChanged = true;
50+
}
51+
52+
return $hasChanged ? $node : null;
53+
}
54+
55+
private function implementsConnection(Class_ $class): bool
56+
{
57+
return array_any(
58+
$class->implements,
59+
fn (Name $name) => $this->isInterfaceName($name, Connection::class, 'Connection'),
60+
);
61+
}
62+
63+
private function isInterfaceName(Name $name, string $interfaceName, string $shortName): bool
64+
{
65+
$names = [
66+
ltrim($name->toString(), '\\'),
67+
];
68+
69+
$resolvedName = $name->getAttribute('resolvedName');
70+
71+
if ($resolvedName instanceof Name) {
72+
$names[] = ltrim($resolvedName->toString(), '\\');
73+
}
74+
75+
return array_any(
76+
$names,
77+
static fn (string $name) => in_array($name, [$interfaceName, $shortName], strict: true),
78+
);
79+
}
80+
81+
private function createMethod(string $methodName, string $returnType): ClassMethod
82+
{
83+
$statements = $returnType === 'bool'
84+
? [new Return_(new ConstFetch(new Name('false')))]
85+
: [];
86+
87+
return new ClassMethod($methodName, [
88+
'flags' => Modifiers::PUBLIC,
89+
'returnType' => new Identifier($returnType),
90+
'stmts' => $statements,
91+
]);
92+
}
93+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
namespace Tempest\Upgrade\Tempest312;
4+
5+
use PhpParser\Modifiers;
6+
use PhpParser\Node;
7+
use PhpParser\Node\Expr;
8+
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Name;
10+
use PhpParser\Node\Stmt\Class_;
11+
use PhpParser\Node\Stmt\ClassMethod;
12+
use PhpParser\Node\Stmt\Expression;
13+
use PhpParser\Node\Stmt\Return_;
14+
use PhpParser\NodeTraverser;
15+
use PhpParser\NodeVisitorAbstract;
16+
use Rector\Rector\AbstractRector;
17+
use Tempest\Core\Kernel;
18+
19+
final class UpdateKernelImplementationsRector extends AbstractRector
20+
{
21+
public function getNodeTypes(): array
22+
{
23+
return [
24+
Class_::class,
25+
];
26+
}
27+
28+
public function refactor(Node $node): ?Node
29+
{
30+
if (! $node instanceof Class_) {
31+
return null;
32+
}
33+
34+
if (! $this->implementsKernel($node)) {
35+
return null;
36+
}
37+
38+
$hasChanged = false;
39+
$shutdown = $node->getMethod('shutdown');
40+
41+
if ($shutdown instanceof ClassMethod && ! $this->isVoidReturnType($shutdown)) {
42+
$shutdown->returnType = new Identifier('void');
43+
$this->removeReturnValues($shutdown);
44+
$hasChanged = true;
45+
}
46+
47+
if (!$node->getMethod('reset') instanceof ClassMethod) {
48+
$node->stmts[] = new ClassMethod('reset', [
49+
'flags' => Modifiers::PUBLIC,
50+
'returnType' => new Identifier('void'),
51+
'stmts' => [],
52+
]);
53+
54+
$hasChanged = true;
55+
}
56+
57+
return $hasChanged ? $node : null;
58+
}
59+
60+
private function implementsKernel(Class_ $class): bool
61+
{
62+
return array_any(
63+
$class->implements,
64+
fn (Name $name) => $this->isInterfaceName($name, Kernel::class, 'Kernel'),
65+
);
66+
}
67+
68+
private function isInterfaceName(Name $name, string $interfaceName, string $shortName): bool
69+
{
70+
$names = [
71+
ltrim($name->toString(), '\\'),
72+
];
73+
74+
$resolvedName = $name->getAttribute('resolvedName');
75+
76+
if ($resolvedName instanceof Name) {
77+
$names[] = ltrim($resolvedName->toString(), '\\');
78+
}
79+
80+
return array_any(
81+
$names,
82+
static fn (string $name) => in_array($name, [$interfaceName, $shortName], strict: true),
83+
);
84+
}
85+
86+
private function isVoidReturnType(ClassMethod $method): bool
87+
{
88+
return $method->returnType instanceof Identifier && $method->returnType->toString() === 'void';
89+
}
90+
91+
private function removeReturnValues(ClassMethod $method): void
92+
{
93+
if ($method->stmts === null) {
94+
return;
95+
}
96+
97+
$traverser = new NodeTraverser();
98+
$traverser->addVisitor(new class extends NodeVisitorAbstract {
99+
public function leaveNode(Node $node): ?array
100+
{
101+
if (! $node instanceof Return_ || ! $node->expr instanceof Expr) {
102+
return null;
103+
}
104+
105+
return [
106+
new Expression($node->expr),
107+
new Return_(),
108+
];
109+
}
110+
});
111+
112+
$method->stmts = $traverser->traverse($method->stmts);
113+
}
114+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Tempest\Upgrade\Tests\Tempest312\Fixtures;
4+
5+
use PDOStatement;
6+
use Tempest\Container\Container;
7+
use Tempest\Core\Kernel as TempestKernel;
8+
use Tempest\Database\Connection\Connection as TempestConnection;
9+
10+
final class AliasedKernel implements TempestKernel
11+
{
12+
public string $root;
13+
14+
public string $internalStorage;
15+
16+
public Container $container;
17+
18+
public static function boot(
19+
string $root,
20+
array $discoveryLocations = [],
21+
?Container $container = null,
22+
?string $internalStorage = null,
23+
): self {
24+
return new self();
25+
}
26+
27+
public function shutdown(): self
28+
{
29+
return $this;
30+
}
31+
}
32+
33+
final class AliasedConnection implements TempestConnection
34+
{
35+
public function beginTransaction(): bool
36+
{
37+
return false;
38+
}
39+
40+
public function commit(): bool
41+
{
42+
return false;
43+
}
44+
45+
public function rollback(): bool
46+
{
47+
return false;
48+
}
49+
50+
public function lastInsertId(): false|string
51+
{
52+
return false;
53+
}
54+
55+
public function prepare(string $sql): PDOStatement
56+
{
57+
return new PDOStatement();
58+
}
59+
60+
public function close(): void
61+
{
62+
}
63+
64+
public function connect(): void
65+
{
66+
}
67+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Tempest\Upgrade\Tests\Tempest312\Fixtures;
4+
5+
use PDOStatement;
6+
use Tempest\Database\Connection\Connection;
7+
8+
final class CustomConnection implements Connection
9+
{
10+
public function beginTransaction(): bool
11+
{
12+
return false;
13+
}
14+
15+
public function commit(): bool
16+
{
17+
return false;
18+
}
19+
20+
public function rollback(): bool
21+
{
22+
return false;
23+
}
24+
25+
public function lastInsertId(): false|string
26+
{
27+
return false;
28+
}
29+
30+
public function prepare(string $sql): PDOStatement
31+
{
32+
return new PDOStatement();
33+
}
34+
35+
public function close(): void
36+
{
37+
}
38+
39+
public function connect(): void
40+
{
41+
}
42+
}

0 commit comments

Comments
 (0)