TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Install\ExtensionScanner\Php;
|
||||
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Install\ExtensionScanner\Php\Matcher\AbstractCoreMatcher;
|
||||
|
||||
/**
|
||||
* Create a fully qualified class name object from first argument of
|
||||
* GeneralUtility::makeInstance('My\\Package\\Class\\Name') if given as string
|
||||
* and not as My\Package\Class\Name::class language construct.
|
||||
*
|
||||
* This resolver is to be called after generic NameResolver::class, but before
|
||||
* other search and find visitors that implement CodeScannerInterface::class
|
||||
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
|
||||
*/
|
||||
class GeneratorClassesResolver extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var BuilderFactory
|
||||
*/
|
||||
protected $builderFactory;
|
||||
|
||||
public function __construct(?BuilderFactory $builderFactory = null)
|
||||
{
|
||||
$this->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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user