How can I get the use imports for a given class? #7324
|
Hi, I'm trying to write a rule that moved Now, I got almost everything figured out except how to check if a class is already imported into the file and how to use this information. Right now my rule outputs \FooBar instead of FooBar as the type for the type that gets imported. I guess I need to expand the import to ensure that the type becomes correct (i.e. that I get rid of the \ at the end). My rule looks like this: <?php
declare (strict_types=1);
namespace Rector\ParameterAnnotation\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\Type\IntegerType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\UnionType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\PhpDocManipulator\VarAnnotationManipulator;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\Core\Rector\AbstractRector;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\ParameterAnnotation\Rector\Class_\MovePropertyAnnotationToClassPropertyRector\MovePropertyAnnotationToClassPropertyRectorTest
*/
final class MovePropertyAnnotationToClassPropertyRector extends AbstractRector
{
/**
* @readonly
*/
private PHPStanStaticTypeMapper $phpStanStaticTypeMapper;
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover
*/
private $phpDocTagRemover;
private VarAnnotationManipulator $varAnnotationManipulator;
public function __construct(PhpDocTagRemover $phpDocTagRemover, VarAnnotationManipulator $varAnnotationManipulator, PHPStanStaticTypeMapper $phpStanStaticTypeMapper)
{
$this->phpDocTagRemover = $phpDocTagRemover;
$this->varAnnotationManipulator = $varAnnotationManipulator;
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [\PhpParser\Node\Stmt\Class_::class];
}
/**
* @param \PhpParser\Node\Stmt\Class_ $node
*/
public function refactor(Node $node): ?Node
{
$classAnalyzer = new ClassAnalyzer($this->nodeNameResolver);
// change the node
if ($classAnalyzer->isAnonymousClass($node)) {
return null;
}
/** @var PhpDocInfoFactory $phpDocInfoFactory */
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$annotationToRemove = 'property';
if (!$phpDocInfo->hasByName($annotationToRemove)) {
return $node;
}
$propertyTags =$phpDocInfo->getTagsByName($annotationToRemove);
$key = 0;
/** @var PhpDocTagNode $value */
foreach($propertyTags as $phpDocTagNode) {
$val = $phpDocTagNode->value;
$docBlockType = null;
if ($val instanceof InvalidTagValueNode) {
print "INVALID: " . $val->value . "\n";
$parts = preg_split('/ /', $val->value, -1, PREG_SPLIT_NO_EMPTY);
if (count($parts) !== 2) {
throw new \Exception("Invalid parts: ".json_encode($parts));
}
list($type, $name) = $parts;
if (strpos($type, '|')) {
$typeStrings = preg_split('/\\|/', $type, -1, PREG_SPLIT_NO_EMPTY );
$types = [];
foreach($typeStrings as $typeString) {
if (strpos($typeString,'\\' ) !== false) {
$types[] = new ObjectType($typeString);
} elseif ($typeString=='string') {
$types[] = new StringType();
printf("Create non object type: $typeString\n");
} elseif($typeString == 'int'|| $typeStrings == 'integer') {
$types[] = new IntegerType();
} elseif($typeString == 'null') {
$types[] = new NullType();
} else {
$objectType = new ObjectType($typeString);
$types[] = $objectType;
}
}
if (count($types) > 1) {
$docBlockType = new UnionType($types);
$type = null;
} else {
$type = $types[0];
}
}
} else {
$type = $val->type->name;
$name = $val->propertyName;
}
if (strpos($name, '$') === 0) {
$name = substr($name, 1);
}
printf("Name: $name\ntype: $type\n" );
$newClassProperty = $this->nodeFactory->createPrivateProperty($name);
if ($type) {
$newClassProperty->type = new Identifier($type);
}
if ($docBlockType) {
$phpDocTagNode = $this->phpDocInfoFactory->createFromNodeOrEmpty($newClassProperty);
$varType = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($docBlockType, TypeKind::PROPERTY);
$varTagValueNode = new VarTagValueNode($varType, $name, '');
$phpDocTagNode->addTagValueNode($varTagValueNode);
}
// $this->varAnnotationManipulator->decorateNodeWithType($newClassProperty, );
$node->stmts = $this->insertBefore($node->stmts, $newClassProperty, $key);
$key++;
}
$this->phpDocTagRemover->removeByName($phpDocInfo, $annotationToRemove);
$phpDocInfo->removeByType($annotationToRemove);
if ($phpDocInfo->hasChanged()) {
return $node;
}
return $node;
}
/**
* @param Stmt[] $stmts
* @return Stmt[]
*/
private function insertBefore(array $stmts, Stmt $stmt, int $key) : array
{
\array_splice($stmts, $key, 0, [$stmt]);
return $stmts;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('"move @property to private $property"', [
new CodeSample(
<<<'CODE_SAMPLE'
namespace Test;
use \Test\FooBar;
/**
* User: tarjei
* Date: 28.07.2022 / 09:55
* @property string $foo
* @property FooBar $fooBar
*/
class MinimalClass
{
public $bar;
public function __construct()
{
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
namespace Test;
/**
* User: tarjei
* Date: 28.07.2022 / 09:55
*/
class MinimalClass
{
private string $foo;
private FooBar $fooBar;
public $bar;
public function __construct()
{
}
}
CODE_SAMPLE
),
]);
}
} |
Replies: 3 comments 2 replies
|
Hi, if you need to read use import only, you can get the parent node of |
|
There is For note: If you're using you may need to use |
|
Than you for your help! Here's the finished code for anyone interested: |
There is
UseImportsResolverservice for thathttps://github.com/rectorphp/rector-src/blob/89bd84eafaab9ab66a4fcd6a1a4df67d15040732/rules/Naming/Naming/UseImportsResolver.php#L15
For note: If you're using
resolveForNode()method for get bothUse_andGroupUse_you may need to use
resolvePrefix()in case for handling with GroupUse_ usage, for example:https://github.com/rectorphp/rector-src/blob/1a84313089f670496a3313ade850d1650df47741/packages/BetterPhpDocParser/PhpDocParser/ClassAnnotationMatcher.php#L112