Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,5 @@ parameters:
- '#Call to function method_exists\(\) with (.*?) will always evaluate to false#'

- '#Parameter \#1 \$rule of method Rector\\Configuration\\Configuration\:\:setRule\(\) expects string\|null, array<string\>\|bool\|string\|null given#'
- '#In method "Rector\\Rector\\Property\\InjectAnnotationClassRector\:\:resolveType", caught "Throwable" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud#'
- '#Empty catch block\. If you are sure this is meant to be empty, please add a "// @ignoreException" comment in the catch block#'
107 changes: 62 additions & 45 deletions src/Rector/Property/InjectAnnotationClassRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Rector\RectorDefinition\ConfiguredCodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
use Throwable;

/**
* @see https://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations#inject
Expand All @@ -38,20 +39,23 @@ final class InjectAnnotationClassRector extends AbstractRector
private $errorAndDiffCollector;

/**
* @var string
* @var string[]
*/
private $annotationClass;
private $annotationClasses = [];

/**
* @param string[] $annotationClasses
*/
public function __construct(
DocBlockManipulator $docBlockManipulator,
AnalyzedApplicationContainerInterface $analyzedApplicationContainer,
ErrorAndDiffCollector $errorAndDiffCollector,
string $annotationClass = ''
array $annotationClasses = []
) {
$this->docBlockManipulator = $docBlockManipulator;
$this->analyzedApplicationContainer = $analyzedApplicationContainer;
$this->errorAndDiffCollector = $errorAndDiffCollector;
$this->annotationClass = $annotationClass;
$this->annotationClasses = $annotationClasses;
}

public function getDefinition(): RectorDefinition
Expand Down Expand Up @@ -90,7 +94,7 @@ public function __construct(EntityManager $entityManager)
CODE_SAMPLE
,
[
'$annotationClass' => 'JMS\DiExtraBundle\Annotation\Inject',
'$annotationClasses' => ['JMS\DiExtraBundle\Annotation\Inject'],
]
),
]
Expand All @@ -110,51 +114,39 @@ public function getNodeTypes(): array
*/
public function refactor(Node $node): ?Node
{
if (! $this->docBlockManipulator->hasTag($node, $this->annotationClass)) {
return null;
}

$type = $this->resolveType($node);
if ($type === null) {
return null;
}

$name = $this->getName($node);
if ($name === null) {
return null;
}

if (! $this->docBlockManipulator->hasTag($node, 'var')) {
$this->docBlockManipulator->changeVarTag($node, $type);
}

$this->docBlockManipulator->removeTagFromNode($node, $this->annotationClass);

// set to private
$node->flags = Class_::MODIFIER_PRIVATE;
foreach ($this->annotationClasses as $annotationClass) {
if (! $this->docBlockManipulator->hasTag($node, $annotationClass)) {
continue;
}

$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $classNode instanceof Class_) {
throw new ShouldNotHappenException();
return $this->refactorPropertyWithAnnotation($node, $annotationClass);
}

$this->addPropertyToClass($classNode, $type, $name);

return $node;
return null;
}

private function resolveType(Node $node): ?string
private function resolveType(Node $node, string $annotationClass): ?string
{
$injectTagNode = $this->docBlockManipulator->getTagByName($node, $this->annotationClass);
$injectTagNode = $this->docBlockManipulator->getTagByName($node, $annotationClass);

$serviceName = $this->resolveServiceName($injectTagNode, $node);
if ($serviceName) {
if ($this->analyzedApplicationContainer->hasService($serviceName)) {
return $this->analyzedApplicationContainer->getTypeForName($serviceName);
try {
if ($this->analyzedApplicationContainer->hasService($serviceName)) {
return $this->analyzedApplicationContainer->getTypeForName($serviceName);
}
} catch (Throwable $throwable) {
// resolve later in errorAndDiffCollector if @var not found
}
}

// collect error
$varTypeInfo = $this->docBlockManipulator->getVarTypeInfo($node);
if ($varTypeInfo !== null && $varTypeInfo->getFqnType() !== null) {
return ltrim($varTypeInfo->getFqnType(), '\\');
}

// the @var is missing and service name was not found → report it
if ($serviceName) {
/** @var SmartFileInfo $fileInfo */
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);

Expand All @@ -165,19 +157,13 @@ private function resolveType(Node $node): ?string
);
}

$varTypeInfo = $this->docBlockManipulator->getVarTypeInfo($node);
if ($varTypeInfo === null) {
return null;
}

return $varTypeInfo->getFqnType();
return null;
}

private function resolveServiceName(PhpDocTagNode $phpDocTagNode, Node $node): ?string
{
$injectTagContent = (string) $phpDocTagNode->value;
$match = Strings::match($injectTagContent, '#(\'|")(?<serviceName>.*?)(\'|")#');

if ($match['serviceName']) {
return $match['serviceName'];
}
Expand All @@ -190,4 +176,35 @@ private function resolveServiceName(PhpDocTagNode $phpDocTagNode, Node $node): ?

return $this->getName($node);
}

private function refactorPropertyWithAnnotation(Property $property, string $annotationClass): ?Property
{
$type = $this->resolveType($property, $annotationClass);
if ($type === null) {
return null;
}

$name = $this->getName($property);
if ($name === null) {
return null;
}

if (! $this->docBlockManipulator->hasTag($property, 'var')) {
$this->docBlockManipulator->changeVarTag($property, $type);
}

$this->docBlockManipulator->removeTagFromNode($property, $annotationClass);

// set to private
$property->flags = Class_::MODIFIER_PRIVATE;

$classNode = $property->getAttribute(AttributeKey::CLASS_NODE);
if (! $classNode instanceof Class_) {
throw new ShouldNotHappenException();
}

$this->addPropertyToClass($classNode, $type, $name);

return $property;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Rector\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;

class InjectFromVar
{
/**
* @Inject
* @var \Fully\Qualified\ClassName\To\Dependency
*/
private $someDependency;
}

?>
-----
<?php

namespace Rector\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;

class InjectFromVar
{
/**
* @var \Fully\Qualified\ClassName\To\Dependency
*/
private $someDependency;
public function __construct(\Fully\Qualified\ClassName\To\Dependency $someDependency)
{
$this->someDependency = $someDependency;
}
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function test(): void
__DIR__ . '/Fixture/fixture3.php.inc',
__DIR__ . '/Fixture/fixture4.php.inc',
__DIR__ . '/Fixture/fixture5.php.inc',
__DIR__ . '/Fixture/inject_from_var.php.inc',
]);
}

Expand All @@ -36,7 +37,7 @@ protected function getRectorsWithConfiguration(): array
{
return [
InjectAnnotationClassRector::class => [
'$annotationClass' => 'JMS\DiExtraBundle\Annotation\Inject',
'$annotationClasses' => ['JMS\DiExtraBundle\Annotation\Inject', 'Inject'],
],
];
}
Expand Down