mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 13:28:18 +01:00
[JMS] Add JmsInjectAnnotationRector
This commit is contained in:
parent
4b1e61ed58
commit
70ea05352d
@ -45,6 +45,7 @@
|
||||
"Rector\\Symfony\\": "packages/Symfony/src",
|
||||
"Rector\\CakePHP\\": "packages/CakePHP/src",
|
||||
"Rector\\Php\\": "packages/Php/src",
|
||||
"Rector\\Jms\\": "packages/Jms/src",
|
||||
"Rector\\Silverstripe\\": "packages/Silverstripe/src",
|
||||
"Rector\\Sensio\\": "packages/Sensio/src",
|
||||
"Rector\\Sylius\\": "packages/Sylius/src",
|
||||
@ -65,6 +66,7 @@
|
||||
"Rector\\CodeQuality\\Tests\\": "packages/CodeQuality/tests",
|
||||
"Rector\\DomainDrivenDesign\\Tests\\": "packages/DomainDrivenDesign/tests",
|
||||
"Rector\\Php\\Tests\\": "packages/Php/tests",
|
||||
"Rector\\Jms\\Tests\\": "packages/Jms/tests",
|
||||
"Rector\\Symfony\\Tests\\": "packages/Symfony/tests",
|
||||
"Rector\\Silverstripe\\Tests\\": "packages/Silverstripe/tests",
|
||||
"Rector\\Sensio\\Tests\\": "packages/Sensio/tests",
|
||||
@ -176,6 +178,7 @@
|
||||
"packages/Symfony/tests/Rector/Process/ProcessBuilderGetProcessRector/Wrong",
|
||||
"packages/Symfony/tests/Rector/Process/ProcessBuilderInstanceRector/Wrong",
|
||||
"packages/Twig/tests/Rector/SimpleFunctionAndFilterRector/Wrong",
|
||||
"packages/Jms/tests/Rector/Property/JmsInjectAnnotationRector/Wrong",
|
||||
"packages/Doctrine/tests/Rector/AliasToClassRector/Wrong",
|
||||
"packages/PhpParser/tests/Rector/RemoveNodeRector/Wrong",
|
||||
"packages/PhpParser/tests/Rector/IdentifierRector/Wrong",
|
||||
|
131
packages/Jms/src/Rector/Property/JmsInjectAnnotationRector.php
Normal file
131
packages/Jms/src/Rector/Property/JmsInjectAnnotationRector.php
Normal file
@ -0,0 +1,131 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Jms\Rector\Property;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockAnalyzer;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
/**
|
||||
* @see https://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations#inject
|
||||
*/
|
||||
final class JmsInjectAnnotationRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const INJECT_ANNOTATION = 'JMS\DiExtraBundle\Annotation\Inject';
|
||||
|
||||
/**
|
||||
* @var DocBlockAnalyzer
|
||||
*/
|
||||
private $docBlockAnalyzer;
|
||||
|
||||
/**
|
||||
* @var AnalyzedApplicationContainerInterface
|
||||
*/
|
||||
private $analyzedApplicationContainer;
|
||||
|
||||
public function __construct(
|
||||
DocBlockAnalyzer $docBlockAnalyzer,
|
||||
AnalyzedApplicationContainerInterface $analyzedApplicationContainer
|
||||
) {
|
||||
$this->docBlockAnalyzer = $docBlockAnalyzer;
|
||||
$this->analyzedApplicationContainer = $analyzedApplicationContainer;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Changes properties with @JMS\DiExtraBundle\Annotation\Inject to constructor injection',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
use JMS\DiExtraBundle\Annotation as DI;
|
||||
|
||||
class SomeController
|
||||
{
|
||||
/**
|
||||
* @DI\Inject("entity.manager")
|
||||
*/
|
||||
private $entityManager;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
use JMS\DiExtraBundle\Annotation as DI;
|
||||
|
||||
class SomeController
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Property::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->docBlockAnalyzer->hasTag($node, self::INJECT_ANNOTATION)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var PhpDocTagNode $injectTagNode */
|
||||
$injectTagNode = $this->docBlockAnalyzer->getTagByName($node, self::INJECT_ANNOTATION);
|
||||
|
||||
$serviceName = $this->resolveServiceNameFromInjectTag($injectTagNode);
|
||||
if ($serviceName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->analyzedApplicationContainer->hasService($serviceName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $this->analyzedApplicationContainer->getTypeForName($serviceName);
|
||||
|
||||
if (! $this->docBlockAnalyzer->hasTag($node, 'var')) {
|
||||
$this->docBlockAnalyzer->addVarTag($node, $type);
|
||||
}
|
||||
|
||||
$this->docBlockAnalyzer->removeTagFromNode($node, self::INJECT_ANNOTATION);
|
||||
|
||||
$this->addPropertyToClass(
|
||||
(string) $node->getAttribute(Attribute::CLASS_NAME),
|
||||
$type,
|
||||
(string) $node->props[0]->name
|
||||
);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function resolveServiceNameFromInjectTag(PhpDocTagNode $phpDocTagNode): ?string
|
||||
{
|
||||
$injectTagContent = (string) $phpDocTagNode->value;
|
||||
$match = Strings::match($injectTagContent, '#(\'|")(?<serviceName>.*?)(\'|")#');
|
||||
|
||||
return $match['serviceName'] ?? null;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Jms\Tests\Rector\Property\JmsInjectAnnotationRector\Wrong;
|
||||
|
||||
use JMS\DiExtraBundle\Annotation as DI;
|
||||
|
||||
class SomeController
|
||||
{
|
||||
/**
|
||||
* @var \Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
public function __construct(\Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Architecture\DependencyInjection\AnnotatedPropertyInjectToConstructorInjectionRector;
|
||||
namespace Rector\Jms\Tests\Rector\Property\JmsInjectAnnotationRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
/**
|
||||
* @covers \Rector\Rector\Architecture\DependencyInjection\AnnotatedPropertyInjectToConstructorInjectionRector
|
||||
* @covers \Rector\Jms\Rector\Property\JmsInjectAnnotationRector
|
||||
*/
|
||||
final class JmsRectorTest extends AbstractRectorTestCase
|
||||
final class JmsInjectAnnotationRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideWrongToFixedFiles()
|
||||
@ -20,11 +20,12 @@ final class JmsRectorTest extends AbstractRectorTestCase
|
||||
|
||||
public function provideWrongToFixedFiles(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Wrong/wrong7.php.inc', __DIR__ . '/Correct/correct7.php.inc'];
|
||||
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
|
||||
yield [__DIR__ . '/Wrong/wrong2.php.inc', __DIR__ . '/Correct/correct2.php.inc'];
|
||||
}
|
||||
|
||||
protected function provideConfig(): string
|
||||
{
|
||||
return __DIR__ . '/jms-config.yml';
|
||||
return __DIR__ . '/config.yml';
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Jms\Tests\Rector\Property\JmsInjectAnnotationRector\Wrong;
|
||||
|
||||
use JMS\DiExtraBundle\Annotation as DI;
|
||||
|
||||
class SomeController
|
||||
{
|
||||
/**
|
||||
* @DI\Inject("entity.manager")
|
||||
*/
|
||||
private $entityManager;
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
parameters:
|
||||
kernel_class: 'Rector\Symfony\Tests\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeKernelClass'
|
||||
|
||||
services:
|
||||
Rector\Jms\Rector\Property\JmsInjectAnnotationRector: ~
|
@ -6,6 +6,8 @@ use Nette\Utils\Strings;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Exception\MissingTagException;
|
||||
use Rector\PhpParser\CurrentNodeProvider;
|
||||
@ -71,7 +73,7 @@ final class DocBlockAnalyzer
|
||||
$phpDocInfo = $this->createPhpDocInfoWithFqnTypesFromNode($node);
|
||||
|
||||
// is namespaced annotation?
|
||||
if (Strings::contains($name, '\\')) {
|
||||
if ($this->isNamespaced($name)) {
|
||||
$this->fqnAnnotationTypeDecorator->decorate($phpDocInfo, $node);
|
||||
}
|
||||
|
||||
@ -98,8 +100,7 @@ final class DocBlockAnalyzer
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
|
||||
// is namespaced annotation?
|
||||
if (Strings::contains($name, '\\')) {
|
||||
if ($this->isNamespaced($name)) {
|
||||
$this->fqnAnnotationTypeDecorator->decorate($phpDocInfo, $node);
|
||||
}
|
||||
|
||||
@ -186,9 +187,24 @@ final class DocBlockAnalyzer
|
||||
|
||||
$phpDocInfo = $this->createPhpDocInfoWithFqnTypesFromNode($node);
|
||||
|
||||
if ($this->isNamespaced($name)) {
|
||||
$this->fqnAnnotationTypeDecorator->decorate($phpDocInfo, $node);
|
||||
}
|
||||
|
||||
return $phpDocInfo->getTagsByName($name);
|
||||
}
|
||||
|
||||
public function addVarTag(Node $node, string $type): void
|
||||
{
|
||||
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
|
||||
$phpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
|
||||
$varTagValueNode = new VarTagValueNode(new IdentifierTypeNode('\\' . $type), '', '');
|
||||
$phpDocNode->children[] = new PhpDocTagNode('@var', $varTagValueNode);
|
||||
|
||||
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
@ -244,4 +260,9 @@ final class DocBlockAnalyzer
|
||||
|
||||
return $phpDocInfo;
|
||||
}
|
||||
|
||||
private function isNamespaced(string $name): bool
|
||||
{
|
||||
return Strings::contains($name, '\\');
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
|
||||
use Nette\Utils\Reflection;
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use ReflectionClass;
|
||||
@ -23,6 +22,7 @@ final class FqnAnnotationTypeDecorator
|
||||
}
|
||||
|
||||
$tagShortName = ltrim($phpDocChildNode->name, '@');
|
||||
|
||||
// probably not a class like type
|
||||
if (ctype_lower($tagShortName[0])) {
|
||||
continue;
|
||||
@ -36,13 +36,11 @@ final class FqnAnnotationTypeDecorator
|
||||
|
||||
private function resolveTagFqnName(Node $node, string $tagShortName): string
|
||||
{
|
||||
$classNode = $node->getAttribute(Attribute::CLASS_NODE);
|
||||
if (! $classNode instanceof Class_) {
|
||||
$className = $node->getAttribute(Attribute::CLASS_NAME);
|
||||
if (! $className) {
|
||||
return $tagShortName;
|
||||
}
|
||||
|
||||
$className = (string) $classNode->name;
|
||||
|
||||
return Reflection::expandClassName($tagShortName, new ReflectionClass($className));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource;
|
||||
|
||||
final class SomeEntityManager
|
||||
{
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Rector\Symfony\Tests\FrameworkBundle\AbstractToConstructorInjectionRectorSource;
|
||||
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeEntityManager;
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslator;
|
||||
use Rector\Symfony\Tests\Rector\FrameworkBundle\AbstractToConstructorInjectionRectorSource\SomeTranslatorInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
@ -27,6 +28,8 @@ final class SomeKernelClass extends Kernel
|
||||
$containerBuilder->register('translator.data_collector', SomeTranslator::class);
|
||||
$containerBuilder->setAlias('translator', 'translator.data_collector');
|
||||
$containerBuilder->setAlias(SomeTranslatorInterface::class, 'translator.data_collector');
|
||||
|
||||
$containerBuilder->register('entity.manager', SomeEntityManager::class);
|
||||
}
|
||||
|
||||
public function getCacheDir()
|
||||
|
@ -1,7 +0,0 @@
|
||||
services:
|
||||
Rector\Symfony\Rector\HttpKernel\GetterToPropertyRector: ~
|
||||
|
||||
Rector\Symfony\Tests\HttpKernel\GetterToPropertyRector\Source\DummyProvider: ~
|
||||
|
||||
# require for interface autowiring
|
||||
'Rector\Bridge\Contract\ServiceTypeForNameProviderInterface': '@Rector\Symfony\Tests\HttpKernel\GetterToPropertyRector\Source\DummyProvider'
|
@ -1,3 +0,0 @@
|
||||
services:
|
||||
Rector\Rector\Architecture\DependencyInjection\AnnotatedPropertyInjectToConstructorInjectionRector:
|
||||
$annotation: 'JMS\DiExtraBundle\Annotation\Inject'
|
Loading…
x
Reference in New Issue
Block a user