builderFactory = $builderFactory ?? new BuilderFactory(); } /** * Called by PhpParser. * Create an fqdn object from first makeInstance argument if it is a String * * @param Node $node Incoming node */ public function enterNode(Node $node) { if ($node instanceof StaticCall && $node->class instanceof FullyQualified && $node->class->toString() === GeneralUtility::class && $node->name->name === 'makeInstance' && isset($node->args[0]->value) && $node->args[0]->value instanceof Expr ) { $argValue = $node->args[0]->value; $argAlternative = $this->substituteClassString($argValue); if ($argAlternative !== null) { $node->args[0]->value = $argAlternative; $argValue = $argAlternative; } $nodeAlternative = $this->substituteMakeInstance($node, $argValue); if ($nodeAlternative !== null) { $node->setAttribute(AbstractCoreMatcher::NODE_RESOLVED_AS, $nodeAlternative); } } return null; } /** * Substitutes class-string values with their corresponding class constant * representation (`'Vendor\\ClassName'` -> `\Vendor\ClassName::class`). */ protected function substituteClassString(Expr $argValue): ?ClassConstFetch { // skip non-strings, and those starting with (invalid) namespace separator if (!$argValue instanceof String_ || $argValue->value[0] === '\\') { return null; } $classString = ltrim($argValue->value, '\\'); $className = new FullyQualified($classString); $classArg = $this->builderFactory->classConstFetch($className, 'class'); $this->duplicateNodeAttributes($argValue, $className, $classArg); return $classArg; } /** * Substitutes `makeInstance` invocations with proper `new` invocations. * `GeneralUtility(\Vendor\ClassName::class, 'a', 'b')` -> `new \Vendor\ClassName('a', 'b')` */ protected function substituteMakeInstance(StaticCall $node, Expr $argValue): ?New_ { if (!$argValue instanceof ClassConstFetch || !$argValue->class instanceof FullyQualified ) { return null; } $newExpr = $this->builderFactory->new( $argValue->class, array_slice($node->args, 1), ); $this->duplicateNodeAttributes($node, $newExpr); return $newExpr; } /** * Duplicates node positions in source file, based on the assumption * that only lines are relevant. In case this shall be used for * code-migration, real offset positions would be required. */ protected function duplicateNodeAttributes(Node $source, Node ...$targets): void { foreach ($targets as $target) { $target->setAttributes([ 'startLine' => $source->getStartLine(), 'endLine' => $source->getEndLine(), ]); } } }