-
Notifications
You must be signed in to change notification settings - Fork 1
/
ApiHandlerParametersRector.php
128 lines (113 loc) · 5.32 KB
/
ApiHandlerParametersRector.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?php
declare(strict_types=1);
namespace Crm\Utils\Rector\UpgradeToCrm1;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class ApiHandlerParametersRector extends AbstractRector
{
private \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover $phpDocTagRemover;
public function __construct(\Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover $phpDocTagRemover)
{
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Change signature of ApiHandlerInterface::handle() and IdempotentHandlerInterface::idempotentHandle()',
[
new CodeSample(
// code before
'
public function handle(\Crm\ApiModule\Authorization\ApiAuthorizationInterface $authorization): ResponseInterface
{
$data = $authorization->getAuthorizedData();
$params = $paramsProcessor->getValues();
}
',
// code after
'
public function handle(array $params): ResponseInterface
{
$authorization = $this->getAuthorization();
$data = $authorization->getAuthorizedData();
}
'
),
]
);
}
/**
* @return array<class-string<\PhpParser\Node>>
*/
public function getNodeTypes(): array
{
return [\PhpParser\Node\Stmt\ClassMethod::class];
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod $node
*/
public function refactor(\PhpParser\Node $node): ?\PhpParser\Node
{
// process only API handlers
$parentNode = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
if (!$parentNode || !$this->isObjectType($parentNode, new \PHPStan\Type\ObjectType('Crm\ApiModule\Api\ApiHandlerInterface'))) {
return null;
}
// process only handle() and idempotentHandle() methods
if (!$this->isNames($node->name, ['handle', 'idempotentHandle'])) {
return null;
}
// remove old "ApiAuthorizationInterface $authorization"
// - there was only one parameter; position === 0
// - checking against \PhpParser\Node\Name type (if it was already refactored, it would be non-namespaced array -> \PhpParser\Node\Identifier
if (isset($node->params[0]) &&
$node->params[0]->type instanceof \PhpParser\Node\Name &&
$node->params[0]->type->toCodeString() === '\Crm\ApiModule\Authorization\ApiAuthorizationInterface'
) {
// store variable name for initialization
$authorizationVarName = $node->params[0]->var->name ?? 'authorization';
$this->nodeRemover->removeParam($node, 0);
}
// add new "array $params"
$param = new \PhpParser\Node\Param(new \PhpParser\Node\Expr\Variable('params'));
$param->type = new \PhpParser\Node\Name('array');
$node->params[0] = $param;
// reinitialize $authorization from ApiHandler getter if it is used in method
// (initialized is variable with name used in method declaration for ApiAuthorizationInterface)
if (isset($authorizationVarName) && $this->betterNodeFinder->findVariableOfName($node->stmts, $authorizationVarName)) {
// check if $authorization is initialized (in case method param was overridden)
$authorizationAlreadyAssigned = false;
foreach ((array) $node->stmts as $stmt) {
if ($stmt instanceof \PhpParser\Node\Stmt\Expression &&
isset($stmt->expr->var->name) &&
$stmt->expr->var->name === $authorizationVarName
) {
$authorizationAlreadyAssigned = true;
break;
}
}
// initialize $authorization variable
if (!$authorizationAlreadyAssigned) {
$assign = new \PhpParser\Node\Expr\Assign(
new \PhpParser\Node\Expr\Variable($authorizationVarName),
$this->nodeFactory->createMethodCall('this', 'getAuthorization')
);
$assignStmt = new \PhpParser\Node\Stmt\Expression($assign);
// TODO: addNodeAfterNode works; check why before does not?
// $this->nodesToAddCollector->addNodeBeforeNode($assignStmt, $node->getStmts()[0]);
$node->stmts = array_merge([$assignStmt], (array) $node->stmts);
}
}
// remove phpDoc annotations for changed param and return
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
foreach (['param', 'return'] as $annotationToRemove) {
$this->phpDocTagRemover->removeByName($phpDocInfo, $annotationToRemove);
if (!\is_a($annotationToRemove, \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode::class, \true)) {
continue;
}
$phpDocInfo->removeByType($annotationToRemove);
}
return $node;
}
}