-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathAbstractDrupalCoreRector.php
154 lines (130 loc) · 5.35 KB
/
AbstractDrupalCoreRector.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<?php
declare(strict_types=1);
namespace DrupalRector\Rector;
use Drupal\Component\Utility\DeprecationHelper;
use DrupalRector\Contract\VersionedConfigurationInterface;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrowFunction;
use PHPStan\Reflection\MethodReflection;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Rector\AbstractRector;
abstract class AbstractDrupalCoreRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var array|\DrupalRector\Contract\VersionedConfigurationInterface[]
*/
protected array $configuration = [];
public function configure(array $configuration): void
{
foreach ($configuration as $value) {
if (!($value instanceof VersionedConfigurationInterface)) {
throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', VersionedConfigurationInterface::class));
}
}
$this->configuration = $configuration;
}
protected function isInBackwardsCompatibleCall(Node $node): bool
{
if (!class_exists(DeprecationHelper::class)) {
return false;
}
$scope = $node->getAttribute(AttributeKey::SCOPE);
$callStack = $scope->getFunctionCallStackWithParameters();
if (count($callStack) === 0) {
return false;
}
[$function, $parameter] = $callStack[0];
if (!$function instanceof MethodReflection) {
return false;
}
if ($function->getName() !== 'backwardsCompatibleCall'
|| $function->getDeclaringClass()->getName() !== DeprecationHelper::class
) {
return false;
}
return $parameter !== null && $parameter->getName() === 'deprecatedCallable';
}
public function refactor(Node $node)
{
foreach ($this->configuration as $configuration) {
if ($this->rectorShouldApplyToDrupalVersion($configuration) === false) {
continue;
}
if ($this->isInBackwardsCompatibleCall($node)) {
continue;
}
$result = $this->refactorWithConfiguration($node, $configuration);
// Skip if no result.
if ($result === null) {
continue;
}
// Check if Drupal version and the introduced version support backward
// compatible calls. Although it was introduced in Drupal 10.1 we
// also supply these patches for changes introduced in Drupal 10.0.
// The reason for this is that will start supplying patches for
// Drupal 10 when 10.0 is already out of support. This means that
// we will not support running drupal-rector on Drupal 10.0.x.
if ($this->supportBackwardsCompatibility($configuration) === false) {
return $result;
}
// Create a backwards compatible call if the node is a call-like expression.
if ($node instanceof Node\Expr\CallLike && $result instanceof Node\Expr\CallLike) {
return $this->createBcCallOnCallLike($node, $result, $configuration->getIntroducedVersion());
}
return $result;
}
return null;
}
/**
* Process Node of matched type.
*
* @return Node|Node[]|null
*/
abstract protected function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration);
private function createBcCallOnCallLike(Node\Expr\CallLike $node, Node\Expr\CallLike $result, string $introducedVersion): Node\Expr\StaticCall
{
$clonedNode = clone $node;
return $this->nodeFactory->createStaticCall(DeprecationHelper::class, 'backwardsCompatibleCall', [
$this->nodeFactory->createClassConstFetch(\Drupal::class, 'VERSION'),
$introducedVersion,
new ArrowFunction(['expr' => $result]),
new ArrowFunction(['expr' => $clonedNode]),
]);
}
/**
* @param VersionedConfigurationInterface $configuration
*
* @return bool|int
*/
public function rectorShouldApplyToDrupalVersion(VersionedConfigurationInterface $configuration)
{
return version_compare($this->installedDrupalVersion(), $configuration->getIntroducedVersion(), '>=');
}
/**
* @phpstan-return non-empty-string
*/
public function installedDrupalVersion(): string
{
return str_replace([
'.x-dev',
'-dev',
], '.0', \Drupal::VERSION);
}
/**
* Check if Drupal version and the introduced version support backward
* compatible calls. Although it was introduced in Drupal 10.1 we
* also supply these patches for changes introduced in Drupal 10.0.
* The reason for this is that will start supplying patches for
* Drupal 10 when 10.0 is already out of support. This means that
* we will not support running drupal-rector on Drupal 10.0.x.
*
* @param VersionedConfigurationInterface $configuration
*
* @return bool
*/
public function supportBackwardsCompatibility(VersionedConfigurationInterface $configuration): bool
{
return !(version_compare($this->installedDrupalVersion(), '10.1.0', '<') || version_compare($configuration->getIntroducedVersion(), '10.0.0', '<'));
}
}