ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
DICDependencyManipulator.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
32 
34 {
35  public const DIC = 'DIC';
36  private \Rector\Core\PhpParser\Node\NodeFactory $nodeFactory;
37  private \Rector\Core\NodeManipulator\StmtsManipulator $stmtsManipulator;
38  private \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver;
39  private \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator;
40  private NodesToAddCollector $nodesToAddCollector;
41  private \Rector\Core\NodeDecorator\CreatedByRuleDecorator $createdByRuleDecorator;
42  private array $duplicate_checker = [];
43  private \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder;
44  private array $added_constructors = [];
45  private \Rector\Core\Contract\Console\OutputStyleInterface $outputStyle;
46 
47  public function __construct(
48  \Rector\Core\PhpParser\Node\NodeFactory $nodeFactory,
49  \Rector\Core\NodeManipulator\StmtsManipulator $stmtsManipulator,
50  \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver,
51  \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator,
52  NodesToAddCollector $nodesToAddCollector,
53  \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder,
54  \Rector\Core\Contract\Console\OutputStyleInterface $outputStyle
55  ) {
56  $this->nodeFactory = $nodeFactory;
57  $this->stmtsManipulator = $stmtsManipulator;
58  $this->nodeNameResolver = $nodeNameResolver;
59  $this->nodeComparator = $nodeComparator;
60  $this->nodesToAddCollector = $nodesToAddCollector;
61  $this->betterNodeFinder = $betterNodeFinder;
62  $this->outputStyle = $outputStyle;
63  }
64 
68  private function getDICVariable(): Variable
69  {
70  return new Variable(self::DIC);
71  }
72 
76  private function getGlobalDIC(): Stmt\Global_
77  {
78  return new Stmt\Global_([$this->getDICVariable()]);
79  }
80 
82  ClassMethod $classMethod,
83  Stmt\Class_ $class,
84  Stmt $stmt
85  ): void {
86  $class_method_string = $class->name->name . '::' . $classMethod->name->name;
87  $stmt_string = $this->nodeComparator->printWithoutComments($stmt);
88  if (isset($this->duplicate_checker[$class_method_string][$stmt_string])
89  && $this->duplicate_checker[$class_method_string][$stmt_string] === true) {
90  return;
91  }
92  $stmts = $this->stmtsManipulator->filterOutExistingStmts(
93  $classMethod,
94  [$stmt]
95  );
96  // all stmts are already there → skip
97  if ($stmts === []) {
98  return;
99  }
100  $first = null;
101  foreach ($classMethod->getStmts() as $inner_statement) {
102  if ($inner_statement->getAttributes() === []) {
103  continue;
104  }
105  $first = $inner_statement;
106  break;
107  }
108  if ($first !== null) {
109  $this->nodesToAddCollector->addNodeBeforeNode($stmt, $first);
110  } else {
111  $classMethod->stmts[] = $stmt;
112  }
113  $this->duplicate_checker[$class_method_string][$stmt_string] = true;
114  }
115 
116  private function createConstructor(
117  \PhpParser\Node\Stmt\Class_ $class
118  ): ClassMethod {
119  if (isset($this->added_constructors[$class->name->name])) {
120  return $this->added_constructors[$class->name->name];
121  }
122  $classMethod = $this->nodeFactory->createPublicMethod(
123  \Rector\Core\ValueObject\MethodName::CONSTRUCT
124  );
125  // implement parent constructor call
126  if ($this->hasClassParentClassMethod(
127  $class,
128  \Rector\Core\ValueObject\MethodName::CONSTRUCT
129  )) {
130  $classMethod->stmts[] = $this->createParentClassMethodCall(
131  \Rector\Core\ValueObject\MethodName::CONSTRUCT
132  );
133  }
134  $first_class_method = array_filter($class->stmts, function (\PhpParser\Node $n): bool {
135  return $n instanceof ClassMethod;
136  });
137  $first_class_method = array_shift($first_class_method);
138  if ($first_class_method !== null) {
139  $this->nodesToAddCollector->addNodeBeforeNode($classMethod, $first_class_method);
140  } else {
141  array_unshift($class->stmts, $classMethod);
142  }
143  $this->outputStyle->newline();
144  $this->outputStyle->warning(
145  'created constructor for ' . $class->name->name . '. Please check the parent-call for missing parameters!'
146  );
147  $this->outputStyle->newline();
148 
149 
150  $this->added_constructors[$class->name->name] = $classMethod;
151 
152  return $classMethod;
153  }
154 
156  \PhpParser\Node\Stmt\Class_ $class,
157  Stmt $stmt
158  ): void {
159  $classMethod = $class->getMethod(
160  \Rector\Core\ValueObject\MethodName::CONSTRUCT
161  );
162  if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
163  $classMethod = $this->createConstructor($class);
164  }
166  $classMethod,
167  $class,
168  $stmt
169  );
170  }
171 
172  public function ensureGlobalDICinConstructor(Stmt\Class_ $class): void
173  {
174  $stmt = $this->getGlobalDIC();
176  $class,
177  $stmt
178  );
179  $this->duplicate_checker[$class->name->name][$this->nodeComparator->printWithoutComments($stmt)] = true;
180  }
181 
182  public function ensureGlobalDICinMethod(ClassMethod $classMethod, Stmt\Class_ $class): Variable
183  {
185  $classMethod,
186  $class,
187  $this->getGlobalDIC()
188  );
189  return $this->getDICVariable();
190  }
191 
193  ClassMethod $classMethod,
194  Stmt\Class_ $class,
195  Stmt $stmt
196  ) {
197  $class_method_string = $class->name->name . '::' . $classMethod->name->name;
198  $statement_string = $this->nodeComparator->printWithoutComments($stmt);
199  if (isset($this->duplicate_checker[$class_method_string][$statement_string])
200  && $this->duplicate_checker[$class_method_string][$statement_string] === true) {
201  return;
202  }
203  $stmts = $this->stmtsManipulator->filterOutExistingStmts(
204  $classMethod,
205  [$stmt]
206  );
207  // all stmts are already there → skip
208  if ($stmts === []) {
209  return;
210  }
211 
212  $existing_dic = $this->betterNodeFinder->findFirst($classMethod->stmts, function (\PhpParser\Node $n): bool {
213  if (!$n instanceof Stmt\Global_) {
214  return false;
215  }
216  foreach ($n->vars as $var) {
217  if (isset($var->name) && $var->name === self::DIC) {
218  return true;
219  }
220  }
221  return false;
222  });
223  $dic_statement_string = $this->nodeComparator->printWithoutComments($this->getGlobalDIC());
224  if ($existing_dic === null
225  && !isset($this->duplicate_checker[$class_method_string][$dic_statement_string]) // we already added global $DIC in this run
226  && !$this->duplicate_checker[$class_method_string][$dic_statement_string] === true
227  ) {
228  throw new ShouldNotHappenException(
229  'no dic found: ' . $class_method_string . ' (' . $statement_string . ') '
230  );
231  }
232 
233  // get first existing statement
234  $first_existing = array_filter($classMethod->stmts, function (\PhpParser\Node $n): bool {
235  if ($n->getAttributes() === []) {
236  return false;
237  }
238  return !$n instanceof Stmt\Global_;
239  });
240  $first_existing = array_shift($first_existing);
241  if ($first_existing !== null) {
242  $this->nodesToAddCollector->addNodeBeforeNode($stmt, $first_existing);
243  } else {
244  // we use a fallback to add the element in first place.
245  // the nodesToAddCollector does not work here, becaue there are only
246  // "new" nodes without position
247  $classMethod->stmts[] = $stmt;
248  }
249  $this->duplicate_checker[$class_method_string][$statement_string] = true;
250  }
251 
253  \PhpParser\Node\Stmt\Class_ $class,
254  Stmt $stmt
255  ): void {
256  $classMethod = $class->getMethod(
257  \Rector\Core\ValueObject\MethodName::CONSTRUCT
258  );
259  if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
260  $classMethod = $this->createConstructor($class);
261  }
263  $classMethod,
264  $class,
265  $stmt
266  );
267  }
268 
269  private function hasClassParentClassMethod(
270  \PhpParser\Node\Stmt\Class_ $class,
271  string $methodName
272  ): bool {
273  $scope = $class->getAttribute(
274  \Rector\NodeTypeResolver\Node\AttributeKey::SCOPE
275  );
276  if (!$scope instanceof \PHPStan\Analyser\Scope) {
277  return \false;
278  }
279  $classReflection = $scope->getClassReflection();
280  if (!$classReflection instanceof \PHPStan\Reflection\ClassReflection) {
281  return \false;
282  }
283  foreach ($classReflection->getParents() as $parentClassReflection) {
284  if ($parentClassReflection->hasMethod($methodName)) {
285  return \true;
286  }
287  }
288  return \false;
289  }
290 
291  private function createParentClassMethodCall(
292  string $methodName
293  ): \PhpParser\Node\Stmt\Expression {
294  $staticCall = new \PhpParser\Node\Expr\StaticCall(
295  new \PhpParser\Node\Name(
296  \Rector\Core\Enum\ObjectReference::PARENT()->getValue()
297  ),
298  $methodName
299  );
300 
301  // append arguments
302 
303 
304  return new \PhpParser\Node\Stmt\Expression($staticCall);
305  }
306 
307  private function isParamInConstructor(
308  \PhpParser\Node\Stmt\Class_ $class,
309  string $propertyName
310  ): bool {
311  $constructClassMethod = $class->getMethod(
312  \Rector\Core\ValueObject\MethodName::CONSTRUCT
313  );
314  if (!$constructClassMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
315  return \false;
316  }
317  foreach ($constructClassMethod->params as $param) {
318  if ($this->nodeNameResolver->isName($param, $propertyName)) {
319  return \true;
320  }
321  }
322  return \false;
323  }
324 
325  private function hasMethodParameter(
326  \PhpParser\Node\Stmt\ClassMethod $classMethod,
327  string $name
328  ): bool {
329  foreach ($classMethod->params as $param) {
330  if ($this->nodeNameResolver->isName($param->var, $name)) {
331  return \true;
332  }
333  }
334  return \false;
335  }
336 }
isParamInConstructor(\PhpParser\Node\Stmt\Class_ $class, string $propertyName)
Rector Core PhpParser Node BetterNodeFinder $betterNodeFinder
$scope
Definition: ltiregstart.php:53
getValue()
Get the value that is displayed in the input client side.
Definition: Group.php:47
__construct(\Rector\Core\PhpParser\Node\NodeFactory $nodeFactory, \Rector\Core\NodeManipulator\StmtsManipulator $stmtsManipulator, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator, NodesToAddCollector $nodesToAddCollector, \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\Core\Contract\Console\OutputStyleInterface $outputStyle)
hasClassParentClassMethod(\PhpParser\Node\Stmt\Class_ $class, string $methodName)
Rector Core NodeManipulator StmtsManipulator $stmtsManipulator
Rector NodeNameResolver NodeNameResolver $nodeNameResolver
hasMethodParameter(\PhpParser\Node\Stmt\ClassMethod $classMethod, string $name)
if($format !==null) $name
Definition: metadata.php:247
addStmtToMethodIfNotThereAfterGlobalDIC(ClassMethod $classMethod, Stmt\Class_ $class, Stmt $stmt)
addStmtToConstructorIfNotThereAfterGlobalDIC(\PhpParser\Node\Stmt\Class_ $class, Stmt $stmt)
$param
Definition: xapitoken.php:46
createConstructor(\PhpParser\Node\Stmt\Class_ $class)
Rector Core NodeDecorator CreatedByRuleDecorator $createdByRuleDecorator
Rector Core PhpParser Comparing NodeComparator $nodeComparator
Rector Core Contract Console OutputStyleInterface $outputStyle
addStmtToConstructorIfNotThereYetAtFirstPosition(\PhpParser\Node\Stmt\Class_ $class, Stmt $stmt)
ensureGlobalDICinMethod(ClassMethod $classMethod, Stmt\Class_ $class)
addStmtToMethodIfNotThereYetAtFirstPosition(ClassMethod $classMethod, Stmt\Class_ $class, Stmt $stmt)
Rector Core PhpParser Node NodeFactory $nodeFactory