ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
DICDependencyManipulator.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
28 
30 {
31  public const DIC = 'DIC';
32  private array $duplicate_checker = [];
33  private array $added_constructors = [];
34 
35  public function __construct(
36  private \Rector\Core\PhpParser\Node\NodeFactory $nodeFactory,
37  private \Rector\Core\NodeManipulator\StmtsManipulator $stmtsManipulator,
38  private \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator,
39  private NodesToAddCollector $nodesToAddCollector,
40  private \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder,
41  private \Rector\Core\Contract\Console\OutputStyleInterface $outputStyle
42  ) {
43  }
44 
45  private function getDICVariable(): Variable
46  {
47  return new Variable(self::DIC);
48  }
49 
50  private function getGlobalDIC(): Stmt\Global_
51  {
52  return new Stmt\Global_([$this->getDICVariable()]);
53  }
54 
56  ClassMethod $classMethod,
57  Stmt\Class_ $class,
58  Stmt $stmt
59  ): void {
60  $class_method_string = $class->name->name . '::' . $classMethod->name->name;
61  $stmt_string = $this->nodeComparator->printWithoutComments($stmt);
62  if (isset($this->duplicate_checker[$class_method_string][$stmt_string])
63  && $this->duplicate_checker[$class_method_string][$stmt_string] === true) {
64  return;
65  }
66  $stmts = $this->stmtsManipulator->filterOutExistingStmts(
67  $classMethod,
68  [$stmt]
69  );
70  // all stmts are already there → skip
71  if ($stmts === []) {
72  return;
73  }
74  $first = null;
75  foreach ($classMethod->getStmts() as $inner_statement) {
76  if ($inner_statement->getAttributes() === []) {
77  continue;
78  }
79  $first = $inner_statement;
80  break;
81  }
82  if ($first !== null) {
83  $this->nodesToAddCollector->addNodeBeforeNode($stmt, $first);
84  } else {
85  $classMethod->stmts[] = $stmt;
86  }
87  $this->duplicate_checker[$class_method_string][$stmt_string] = true;
88  }
89 
90  private function createConstructor(
91  \PhpParser\Node\Stmt\Class_ $class
92  ): ClassMethod {
93  if (isset($this->added_constructors[$class->name->name])) {
94  return $this->added_constructors[$class->name->name];
95  }
96  $classMethod = $this->nodeFactory->createPublicMethod(
97  \Rector\Core\ValueObject\MethodName::CONSTRUCT
98  );
99  // implement parent constructor call
100  if ($this->hasClassParentClassMethod(
101  $class,
102  \Rector\Core\ValueObject\MethodName::CONSTRUCT
103  )) {
104  $classMethod->stmts[] = $this->createParentClassMethodCall(
105  \Rector\Core\ValueObject\MethodName::CONSTRUCT
106  );
107  }
108  $first_class_method = array_filter($class->stmts, function (\PhpParser\Node $node): bool {
109  return $node instanceof ClassMethod;
110  });
111  $first_class_method = array_shift($first_class_method);
112  if ($first_class_method !== null) {
113  $this->nodesToAddCollector->addNodeBeforeNode($classMethod, $first_class_method);
114  } else {
115  array_unshift($class->stmts, $classMethod);
116  }
117  $this->outputStyle->newline();
118  $this->outputStyle->warning(
119  'created constructor for ' . $class->name->name . '. Please check the parent-call for missing parameters!'
120  );
121  $this->outputStyle->newline();
122 
123  $this->added_constructors[$class->name->name] = $classMethod;
124 
125  return $classMethod;
126  }
127 
129  \PhpParser\Node\Stmt\Class_ $class,
130  Stmt $stmt
131  ): void {
132  $classMethod = $class->getMethod(
133  \Rector\Core\ValueObject\MethodName::CONSTRUCT
134  );
135  if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
136  $classMethod = $this->createConstructor($class);
137  }
139  $classMethod,
140  $class,
141  $stmt
142  );
143  }
144 
145  public function ensureGlobalDICinConstructor(Stmt\Class_ $class): void
146  {
147  $stmt = $this->getGlobalDIC();
149  $class,
150  $stmt
151  );
152  $this->duplicate_checker[$class->name->name][$this->nodeComparator->printWithoutComments($stmt)] = true;
153  }
154 
155  public function ensureGlobalDICinMethod(ClassMethod $classMethod, Stmt\Class_ $class): Variable
156  {
158  $classMethod,
159  $class,
160  $this->getGlobalDIC()
161  );
162  return $this->getDICVariable();
163  }
164 
166  ClassMethod $classMethod,
167  Stmt\Class_ $class,
168  Stmt $stmt
169  ): void {
170  $class_method_string = $class->name->name . '::' . $classMethod->name->name;
171  $statement_string = $this->nodeComparator->printWithoutComments($stmt);
172  if (isset($this->duplicate_checker[$class_method_string][$statement_string])
173  && $this->duplicate_checker[$class_method_string][$statement_string] === true) {
174  return;
175  }
176  $stmts = $this->stmtsManipulator->filterOutExistingStmts(
177  $classMethod,
178  [$stmt]
179  );
180  // all stmts are already there → skip
181  if ($stmts === []) {
182  return;
183  }
184 
185  $node = $this->betterNodeFinder->findFirst($classMethod->stmts, function (\PhpParser\Node $node): bool {
186  if (!$node instanceof Stmt\Global_) {
187  return false;
188  }
189  foreach ($node->vars as $var) {
190  if (!(property_exists($var, 'name') && $var->name !== null)) {
191  continue;
192  }
193  if ($var->name !== self::DIC) {
194  continue;
195  }
196  return true;
197  }
198  return false;
199  });
200  $dic_statement_string = $this->nodeComparator->printWithoutComments($this->getGlobalDIC());
201  if (!$node instanceof \PhpParser\Node
202  && !isset($this->duplicate_checker[$class_method_string][$dic_statement_string]) // we already added global $DIC in this run
203  && !$this->duplicate_checker[$class_method_string][$dic_statement_string]
204  ) {
205  throw new ShouldNotHappenException(
206  'no dic found: ' . $class_method_string . ' (' . $statement_string . ') '
207  );
208  }
209 
210  // get first existing statement
211  $first_existing = array_filter($classMethod->stmts, function (\PhpParser\Node $node): bool {
212  if ($node->getAttributes() === []) {
213  return false;
214  }
215  return !$node instanceof Stmt\Global_;
216  });
217  $first_existing = array_shift($first_existing);
218  if ($first_existing !== null) {
219  $this->nodesToAddCollector->addNodeBeforeNode($stmt, $first_existing);
220  } else {
221  // we use a fallback to add the element in first place.
222  // the nodesToAddCollector does not work here, becaue there are only
223  // "new" nodes without position
224  $classMethod->stmts[] = $stmt;
225  }
226  $this->duplicate_checker[$class_method_string][$statement_string] = true;
227  }
228 
230  \PhpParser\Node\Stmt\Class_ $class,
231  Stmt $stmt
232  ): void {
233  $classMethod = $class->getMethod(
234  \Rector\Core\ValueObject\MethodName::CONSTRUCT
235  );
236  if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
237  $classMethod = $this->createConstructor($class);
238  }
240  $classMethod,
241  $class,
242  $stmt
243  );
244  }
245 
246  private function hasClassParentClassMethod(
247  \PhpParser\Node\Stmt\Class_ $class,
248  string $methodName
249  ): bool {
250  $scope = $class->getAttribute(
251  \Rector\NodeTypeResolver\Node\AttributeKey::SCOPE
252  );
253  if (!$scope instanceof \PHPStan\Analyser\Scope) {
254  return \false;
255  }
256  $classReflection = $scope->getClassReflection();
257  if (!$classReflection instanceof \PHPStan\Reflection\ClassReflection) {
258  return \false;
259  }
260  foreach ($classReflection->getParents() as $parentClassReflection) {
261  if ($parentClassReflection->hasMethod($methodName)) {
262  return \true;
263  }
264  }
265  return \false;
266  }
267 
268  private function createParentClassMethodCall(
269  string $methodName
270  ): \PhpParser\Node\Stmt\Expression {
271  $staticCall = new \PhpParser\Node\Expr\StaticCall(
272  new \PhpParser\Node\Name(
273  \Rector\Core\Enum\ObjectReference::PARENT()->getValue()
274  ),
275  $methodName
276  );
277 
278  // append arguments
279 
280  return new \PhpParser\Node\Stmt\Expression($staticCall);
281  }
282 }
$scope
Definition: ltiregstart.php:53
getValue()
Get the value that is displayed in the input client side.
Definition: Group.php:46
hasClassParentClassMethod(\PhpParser\Node\Stmt\Class_ $class, string $methodName)
__construct(private \Rector\Core\PhpParser\Node\NodeFactory $nodeFactory, private \Rector\Core\NodeManipulator\StmtsManipulator $stmtsManipulator, private \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator, private NodesToAddCollector $nodesToAddCollector, private \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, private \Rector\Core\Contract\Console\OutputStyleInterface $outputStyle)
addStmtToMethodIfNotThereAfterGlobalDIC(ClassMethod $classMethod, Stmt\Class_ $class, Stmt $stmt)
if(!file_exists(getcwd() . '/ilias.ini.php'))
Definition: confirmReg.php:21
addStmtToConstructorIfNotThereAfterGlobalDIC(\PhpParser\Node\Stmt\Class_ $class, Stmt $stmt)
createConstructor(\PhpParser\Node\Stmt\Class_ $class)
addStmtToConstructorIfNotThereYetAtFirstPosition(\PhpParser\Node\Stmt\Class_ $class, Stmt $stmt)
ensureGlobalDICinMethod(ClassMethod $classMethod, Stmt\Class_ $class)
addStmtToMethodIfNotThereYetAtFirstPosition(ClassMethod $classMethod, Stmt\Class_ $class, Stmt $stmt)