[Generic] Decouple rules to tailored sets (#5635)

Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
Tomas Votruba 2021-02-20 23:48:31 +01:00 committed by GitHub
parent da535f7809
commit 13e33f0bff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 1078 additions and 1069 deletions

View File

@ -3,8 +3,8 @@
declare(strict_types=1);
use Rector\Doctrine\Rector\Class_\AddEntityIdByConditionRector;
use Rector\Generic\Rector\Class_\AddInterfaceByTraitRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Transform\Rector\Class_\AddInterfaceByTraitRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {

View File

@ -6766,10 +6766,10 @@ Add interface by used trait
:wrench: **configure it!**
- class: `Rector\Generic\Rector\Class_\AddInterfaceByTraitRector`
- class: `Rector\Transform\Rector\Class_\AddInterfaceByTraitRector`
```php
use Rector\Generic\Rector\Class_\AddInterfaceByTraitRector;
use Rector\Transform\Rector\Class_\AddInterfaceByTraitRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -6840,7 +6840,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
Turns non-private properties with `@inject` to private properties and constructor injection
- class: `Rector\Generic\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector`
- class: `Rector\DependencyInjection\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector`
```diff
/**
@ -6968,10 +6968,10 @@ Changes properties with specified annotations class to constructor injection
:wrench: **configure it!**
- class: `Rector\Generic\Rector\Property\InjectAnnotationClassRector`
- class: `Rector\DependencyInjection\Rector\Property\InjectAnnotationClassRector`
```php
use Rector\Generic\Rector\Property\InjectAnnotationClassRector;
use Rector\DependencyInjection\Rector\Property\InjectAnnotationClassRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -7015,10 +7015,10 @@ Merges old interface to a new one, that already has its methods
:wrench: **configure it!**
- class: `Rector\Generic\Rector\Class_\MergeInterfacesRector`
- class: `Rector\Transform\Rector\Class_\MergeInterfacesRector`
```php
use Rector\Generic\Rector\Class_\MergeInterfacesRector;
use Rector\Transform\Rector\Class_\MergeInterfacesRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {

View File

@ -60,6 +60,11 @@ final class PhpDocInfoFactory
*/
private $rectorChangeCollector;
/**
* @var array<string, PhpDocInfo>
*/
private $phpDocInfosByObjectHash = [];
public function __construct(
AttributeAwareNodeFactory $attributeAwareNodeFactory,
CurrentNodeProvider $currentNodeProvider,
@ -96,6 +101,11 @@ final class PhpDocInfoFactory
public function createFromNode(Node $node): ?PhpDocInfo
{
$objectHash = spl_object_hash($node);
if (isset($this->phpDocInfosByObjectHash[$objectHash])) {
return $this->phpDocInfosByObjectHash[$objectHash];
}
/** needed for @see PhpDocNodeFactoryInterface */
$this->currentNodeProvider->setNode($node);
@ -112,6 +122,7 @@ final class PhpDocInfoFactory
} else {
$content = $docComment->getText();
$tokens = $this->lexer->tokenize($content);
try {
$phpDocNode = $this->parseTokensToPhpDocNode($tokens);
} catch (ParserException $parserException) {
@ -121,7 +132,10 @@ final class PhpDocInfoFactory
$this->setPositionOfLastToken($phpDocNode);
}
return $this->createFromPhpDocNode($phpDocNode, $content, $tokens, $node);
$phpDocInfo = $this->createFromPhpDocNode($phpDocNode, $content, $tokens, $node);
$this->phpDocInfosByObjectHash[$objectHash] = $phpDocInfo;
return $phpDocInfo;
}
public function createEmpty(Node $node): PhpDocInfo

View File

@ -34,9 +34,10 @@ final class JMSInjectPhpDocNodeFactory extends AbstractPhpDocNodeFactory impleme
private $tagValueNodePrinter;
public function __construct(
NodeNameResolver $nodeNameResolver, ArrayPartPhpDocTagPrinter $arrayPartPhpDocTagPrinter,
TagValueNodePrinter $tagValueNodePrinter)
{
NodeNameResolver $nodeNameResolver,
ArrayPartPhpDocTagPrinter $arrayPartPhpDocTagPrinter,
TagValueNodePrinter $tagValueNodePrinter
) {
$this->nodeNameResolver = $nodeNameResolver;
$this->arrayPartPhpDocTagPrinter = $arrayPartPhpDocTagPrinter;
$this->tagValueNodePrinter = $tagValueNodePrinter;

View File

@ -119,6 +119,7 @@ final class MultiPhpDocNodeFactory extends AbstractPhpDocNodeFactory implements
SensioTemplateTagValueNode::class => 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Template',
SensioMethodTagValueNode::class => 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Method',
SensioRouteTagValueNode::class => 'Sensio\Bundle\FrameworkExtraBundle\Configuration\Route',
// JMS
JMSInjectParamsTagValueNode::class => 'JMS\DiExtraBundle\Annotation\InjectParams',
JMSServiceValueNode::class => 'JMS\DiExtraBundle\Annotation\Service',

View File

@ -140,7 +140,6 @@ final class BetterPhpDocParser extends PhpDocParser
$tokenIterator->tryConsumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC);
$phpDocNode = new PhpDocNode(array_values($children));
$docContent = $this->annotationContentResolver->resolveFromTokenIterator($originalTokenIterator);
return $this->attributeAwareNodeFactory->createFromNode($phpDocNode, $docContent);
@ -160,6 +159,7 @@ final class BetterPhpDocParser extends PhpDocParser
}
$phpDocTagValueNode = $this->parseTagValue($tokenIterator, $tag);
return new PhpDocTagNode($tag, $phpDocTagValueNode);
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\NodeAnalyzer;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\FamilyTree\NodeAnalyzer\ClassChildAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class NetteInjectPropertyAnalyzer
{
/**
* @var ClassChildAnalyzer
*/
private $classChildAnalyzer;
public function __construct(ClassChildAnalyzer $classChildAnalyzer)
{
$this->classChildAnalyzer = $classChildAnalyzer;
}
public function detect(Property $property, PhpDocInfo $phpDocInfo): bool
{
if (! $phpDocInfo->hasByType(NetteInjectTagNode::class)) {
return false;
}
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return false;
}
if ($classLike->isAbstract()) {
return false;
}
if ($this->classChildAnalyzer->hasChildClassConstructor($classLike)) {
return false;
}
if ($this->classChildAnalyzer->hasParentClassConstructor($classLike)) {
return false;
}
// it needs @var tag as well, to get the type
if ($phpDocInfo->getVarTagValueNode() !== null) {
return true;
}
return $property->type !== null;
}
}

View File

@ -0,0 +1,216 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Rector\Property;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\JMS\JMSInjectTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPDI\PHPDIInjectTagValueNode;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\DependencyInjection\NodeAnalyzer\NetteInjectPropertyAnalyzer;
use Rector\DependencyInjection\TypeAnalyzer\InjectParameterAnalyzer;
use Rector\DependencyInjection\TypeAnalyzer\InjectTagValueNodeToServiceTypeResolver;
use Rector\FamilyTree\NodeAnalyzer\PropertyUsageAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* Can cover these cases:
* - https://doc.nette.org/en/2.4/di-usage#toc-inject-annotations
* - https://github.com/Kdyby/Autowired/blob/master/docs/en/index.md#autowired-properties
* - http://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations
* - https://github.com/rectorphp/rector/issues/700#issue-370301169
* - https://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations#inject
*
* @see \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\AnnotatedPropertyInjectToConstructorInjectionRectorTest
*/
final class AnnotatedPropertyInjectToConstructorInjectionRector extends AbstractRector
{
/**
* @var array<class-string<ShortNameAwareTagInterface>>
*/
private const INJECT_TAG_VALUE_NODE_TYPES = [PHPDIInjectTagValueNode::class, JMSInjectTagValueNode::class];
/**
* @var PropertyUsageAnalyzer
*/
private $propertyUsageAnalyzer;
/**
* @var PhpDocTypeChanger
*/
private $phpDocTypeChanger;
/**
* @var InjectParameterAnalyzer
*/
private $injectParameterAnalyzer;
/**
* @var InjectTagValueNodeToServiceTypeResolver
*/
private $injectTagValueNodeToServiceTypeResolver;
/**
* @var NetteInjectPropertyAnalyzer
*/
private $netteInjectPropertyAnalyzer;
/**
* @var PhpDocTagRemover
*/
private $phpDocTagRemover;
public function __construct(
PhpDocTypeChanger $phpDocTypeChanger,
InjectParameterAnalyzer $injectParameterAnalyzer,
InjectTagValueNodeToServiceTypeResolver $injectTagValueNodeToServiceTypeResolver,
PropertyUsageAnalyzer $propertyUsageAnalyzer,
NetteInjectPropertyAnalyzer $netteInjectPropertyAnalyzer,
PhpDocTagRemover $phpDocTagRemover
) {
$this->propertyUsageAnalyzer = $propertyUsageAnalyzer;
$this->phpDocTypeChanger = $phpDocTypeChanger;
$this->injectParameterAnalyzer = $injectParameterAnalyzer;
$this->injectTagValueNodeToServiceTypeResolver = $injectTagValueNodeToServiceTypeResolver;
$this->netteInjectPropertyAnalyzer = $netteInjectPropertyAnalyzer;
$this->phpDocTagRemover = $phpDocTagRemover;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Turns properties with `@inject` to private properties and constructor injection',
[
new CodeSample(
<<<'CODE_SAMPLE'
/**
* @var SomeService
* @inject
*/
public $someService;
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
/**
* @var SomeService
*/
private $someService;
public function __construct(SomeService $someService)
{
$this->someService = $someService;
}
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Property::class];
}
/**
* @param Property $node
*/
public function refactor(Node $node): ?Node
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
if (! $phpDocInfo instanceof PhpDocInfo) {
return null;
}
if ($this->netteInjectPropertyAnalyzer->detect($node, $phpDocInfo)) {
return $this->refactorNetteInjectProperty($phpDocInfo, $node);
}
foreach (self::INJECT_TAG_VALUE_NODE_TYPES as $tagValueNodeType) {
$injectTagValueNode = $phpDocInfo->getByType($tagValueNodeType);
if ($injectTagValueNode === null) {
continue;
}
if ($this->injectParameterAnalyzer->isParameterInject($injectTagValueNode)) {
return null;
}
$serviceType = $this->injectTagValueNodeToServiceTypeResolver->resolve(
$node,
$phpDocInfo,
$injectTagValueNode
);
if ($serviceType instanceof MixedType) {
return null;
}
$this->refactorPropertyWithAnnotation($node, $serviceType, $injectTagValueNode);
if ($this->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
$this->removeNode($node);
return null;
}
return $node;
}
return null;
}
private function refactorPropertyWithAnnotation(
Property $property,
Type $type,
AbstractTagValueNode $tagValueNode
): void {
$propertyName = $this->getName($property);
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$this->phpDocTypeChanger->changeVarType($phpDocInfo, $type);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $tagValueNode);
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
throw new ShouldNotHappenException();
}
$this->addConstructorDependencyToClass($classLike, $type, $propertyName, $property->flags);
}
private function refactorNetteInjectProperty(PhpDocInfo $phpDocInfo, Property $property): ?Property
{
$phpDocInfo->removeByType(NetteInjectTagNode::class);
if ($this->propertyUsageAnalyzer->isPropertyFetchedInChildClass($property)) {
$this->visibilityManipulator->makeProtected($property);
} else {
$this->visibilityManipulator->makePrivate($property);
}
$this->addPropertyToCollector($property);
if ($this->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
$this->removeNode($property);
return null;
}
return $property;
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\TypeAnalyzer;
use Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\JMS\JMSInjectTagValueNode;
final class InjectParameterAnalyzer
{
/**
* @var string
* @see https://regex101.com/r/pjusUN/1
*/
private const BETWEEN_PERCENT_CHARS_REGEX = '#%(.*?)%#';
public function isParameterInject(PhpDocTagValueNode $phpDocTagValueNode): bool
{
if (! $phpDocTagValueNode instanceof JMSInjectTagValueNode) {
return false;
}
$serviceName = $phpDocTagValueNode->getServiceName();
if ($serviceName === null) {
return false;
}
return (bool) Strings::match($serviceName, self::BETWEEN_PERCENT_CHARS_REGEX);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\TypeAnalyzer;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\JMS\JMSInjectTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPDI\PHPDIInjectTagValueNode;
use Rector\Core\Exception\ShouldNotHappenException;
final class InjectTagValueNodeToServiceTypeResolver
{
/**
* @var JMSDITypeResolver
*/
private $jmsDITypeResolver;
public function __construct(JMSDITypeResolver $jmsDITypeResolver)
{
$this->jmsDITypeResolver = $jmsDITypeResolver;
}
public function resolve(Property $property, PhpDocInfo $phpDocInfo, PhpDocTagValueNode $phpDocTagValueNode): Type
{
if ($phpDocTagValueNode instanceof JMSInjectTagValueNode) {
return $this->jmsDITypeResolver->resolve($property, $phpDocTagValueNode);
}
if ($phpDocTagValueNode instanceof PHPDIInjectTagValueNode) {
return $phpDocInfo->getVarType();
}
throw new ShouldNotHappenException();
}
}

View File

@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\TypeAnalyzer;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\JMS\JMSInjectTagValueNode;
use Rector\ChangesReporting\Application\ErrorAndDiffCollector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Symfony\ServiceMapProvider;
use Symplify\SmartFileSystem\SmartFileInfo;
final class JMSDITypeResolver
{
/**
* @var ErrorAndDiffCollector
*/
private $errorAndDiffCollector;
/**
* @var ServiceMapProvider
*/
private $serviceMapProvider;
/**
* @var PhpDocInfoFactory
*/
private $phpDocInfoFactory;
public function __construct(
ErrorAndDiffCollector $errorAndDiffCollector,
ServiceMapProvider $serviceMapProvider,
PhpDocInfoFactory $phpDocInfoFactory
) {
$this->errorAndDiffCollector = $errorAndDiffCollector;
$this->serviceMapProvider = $serviceMapProvider;
$this->phpDocInfoFactory = $phpDocInfoFactory;
}
public function resolve(Property $property, JMSInjectTagValueNode $jmsInjectTagValueNode): Type
{
$serviceMap = $this->serviceMapProvider->provide();
$serviceName = $jmsInjectTagValueNode->getServiceName();
if ($serviceName) {
if (class_exists($serviceName)) {
// single class service
return new ObjectType($serviceName);
}
// 2. service name
if ($serviceMap->hasService($serviceName)) {
$serviceType = $serviceMap->getServiceType($serviceName);
if ($serviceType !== null) {
return $serviceType;
}
}
}
// 3. service is in @var annotation
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$varType = $phpDocInfo->getVarType();
if (! $varType instanceof MixedType) {
return $varType;
}
// the @var is missing and service name was not found → report it
$this->reportServiceNotFound($serviceName, $property);
return new MixedType();
}
private function reportServiceNotFound(?string $serviceName, Property $property): void
{
if ($serviceName !== null) {
return;
}
/** @var SmartFileInfo $fileInfo */
$fileInfo = $property->getAttribute(AttributeKey::FILE_INFO);
$errorMessage = sprintf('Service "%s" was not found in DI Container of your Symfony App.', $serviceName);
$this->errorAndDiffCollector->addErrorWithRectorClassMessageAndFileInfo(
self::class,
$errorMessage,
$fileInfo
);
}
}

View File

@ -2,10 +2,10 @@
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Iterator;
use Rector\Generic\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Rector\DependencyInjection\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ChildPropertyCallProtected extends SomeParentWithInject
{
public function run()
{
return $this->someProductWith;
}
}
class SomeParentWithInject
{
/**
* @inject
* @var SomeProductWithInterface
*/
public $someProductWith;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ChildPropertyCallProtected extends SomeParentWithInject
{
public function run()
{
return $this->someProductWith;
}
}
class SomeParentWithInject
{
public function __construct(protected \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface $someProductWith)
{
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use stdClass;
use DateTimeInterface;
@ -24,7 +24,7 @@ class ClassWithInjects
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use stdClass;
use DateTimeInterface;

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects4
{
/**
* @var SomeProductWithInterface
* @inject
*/
protected $property;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects4
{
public function __construct(private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface $property)
{
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
class ClassWithInjects10ParentParent
{
@ -28,7 +28,7 @@ class ClassWithInjects10 extends ClassWithInjects10Parent
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
class ClassWithInjects10ParentParent
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use DateTimeInterface;
@ -16,7 +16,7 @@ class ClassWithInjects8Parent
class ClassWithInjects8 extends ClassWithInjects8Parent
{
/**
* @var \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct
* @var \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct
* @inject
*/
public $property;
@ -26,7 +26,7 @@ class ClassWithInjects8 extends ClassWithInjects8Parent
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use DateTimeInterface;
@ -39,7 +39,7 @@ class ClassWithInjects8Parent
class ClassWithInjects8 extends ClassWithInjects8Parent
{
public function __construct(\DateTimeInterface $dateTime, private \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct $property)
public function __construct(\DateTimeInterface $dateTime, private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct $property)
{
parent::__construct($dateTime);
}

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithParent;
abstract class SkipAbstractClass
{
/**
* @var SomeProductWithParent
* @inject
*/
protected $property;
}

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use DateTimeInterface;
use stdClass;

View File

@ -0,0 +1,21 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct;
class SkipIfHasChildren
{
/**
* @var \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct
* @inject
*/
public $someProduct;
}
class ChildOfOneClass extends SkipIfHasChildren
{
public function __construct()
{
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct;
class SkipIfHasParentWithConstructor extends ParentWithConstructor
{
/**
* @var \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct
* @inject
*/
public $someProduct;
}
class ParentWithConstructor
{
public function __construct()
{
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
class SkipPropertyWithoutDoc
{
public $property;
}

View File

@ -0,0 +1,30 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixturePhp74;
use stdClass;
class TypedProperty
{
/**
* @inject
*/
public stdClass $property;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixturePhp74;
use stdClass;
class TypedProperty
{
public function __construct(private \stdClass $property)
{
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;
@ -16,7 +16,7 @@ class ClassWithInjects35
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;

View File

@ -0,0 +1,41 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeInterface;
use JMS\DiExtraBundle\Annotation as DI;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects7
{
/**
* @var SomeInterface
* @DI\Inject("irelevant")
*/
private $session;
/**
* @var SomeProductWithInterface
* @DI\Inject
*/
private $product;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeInterface;
use JMS\DiExtraBundle\Annotation as DI;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects7
{
public function __construct(private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeInterface $session, private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface $product)
{
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;
@ -16,7 +16,7 @@ class ClassWithPublicInjects
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use DI\Annotation\Inject;
class InjectFromVar
{
/**
* @Inject
* @var \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\ExistingDependency
*/
private $someDependency;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use DI\Annotation\Inject;
class InjectFromVar
{
public function __construct(private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\ExistingDependency $someDependency)
{
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use DI\Annotation\Inject;
class InjectFromProtectedVar
{
/**
* @Inject
* @var \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\ExistingDependency
*/
protected $someDependency;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use DI\Annotation\Inject;
class InjectFromProtectedVar
{
public function __construct(protected \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\ExistingDependency $someDependency)
{
}
}
?>

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\DifferntButFirstListed\Bar as FooBarFirst;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\Bar;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\Foo;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\DifferntButFirstListed\Foo as BarFooLast;
use DI\Annotation\Inject;
class InjectFromVarWithTypeOfSameName
{
/**
* @Inject
*
* @var Bar
*/
private $someBarDependency;
/**
* @Inject
*
* @var Foo
*/
private $someFooDependency;
}
?>
-----
<?php
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\DifferntButFirstListed\Bar as FooBarFirst;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\Bar;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\Foo;
use Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\DifferntButFirstListed\Foo as BarFooLast;
use DI\Annotation\Inject;
class InjectFromVarWithTypeOfSameName
{
public function __construct(private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\Bar $someBarDependency, private \Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\Foo $someFooDependency)
{
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;

View File

@ -1,10 +1,10 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;
class SomeController
final class SomeController
{
/**
* @DI\Inject("entity.manager")
@ -21,11 +21,11 @@ class SomeController
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;
class SomeController
final class SomeController
{
public function __construct(private \Rector\Symfony\Tests\Rector\MethodCall\AbstractToConstructorInjectionRectorSource\SomeEntityManager $entityManager, private \Rector\Symfony\Tests\Rector\MethodCall\AbstractToConstructorInjectionRectorSource\Contract\SomeTranslatorInterface $translator)
{

View File

@ -1,6 +1,6 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;
@ -16,7 +16,7 @@ class SomeController2
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixtureTagValueNodes;
use JMS\DiExtraBundle\Annotation as DI;

View File

@ -2,10 +2,10 @@
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Iterator;
use Rector\Generic\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Rector\DependencyInjection\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class Bar
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\DifferntButFirstListed;
final class Bar
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\DifferntButFirstListed;
final class Foo
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class ExistingDependency
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class Foo
{
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
interface SomeInterface
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
class SomeParent
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProduct
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProductWithInterface implements SomeInterface
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProductWithParent extends SomeParent
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProductWithTrait
{
use SomeTrait;
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
trait SomeTrait
{
}

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector;
namespace Rector\DependencyInjection\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class InjectAnnotationClassRectorTest extends AbstractRectorTestCase
final class TagValueNodesTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
@ -20,7 +20,7 @@ final class InjectAnnotationClassRectorTest extends AbstractRectorTestCase
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureTagValueNodes');
}
protected function provideConfigFilePath(): string

View File

@ -1,8 +1,9 @@
<?php
use JMS\DiExtraBundle\Annotation\Inject;
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Generic\Rector\Property\InjectAnnotationClassRector;
use Rector\DependencyInjection\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -13,8 +14,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
);
$services = $containerConfigurator->services();
$services->set(InjectAnnotationClassRector::class)
->call('configure', [[
InjectAnnotationClassRector::ANNOTATION_CLASSES => [Inject::class, \DI\Annotation\Inject::class],
]]);
$services->set(AnnotatedPropertyInjectToConstructorInjectionRector::class);
};

View File

@ -1,150 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Rector\Property;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\FamilyTree\NodeAnalyzer\ClassChildAnalyzer;
use Rector\FamilyTree\NodeAnalyzer\PropertyUsageAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* Can cover these cases:
* - https://doc.nette.org/en/2.4/di-usage#toc-inject-annotations
* - https://github.com/Kdyby/Autowired/blob/master/docs/en/index.md#autowired-properties
* - http://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations
* - https://github.com/rectorphp/rector/issues/700#issue-370301169
*
* @see \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\AnnotatedPropertyInjectToConstructorInjectionRectorTest
*/
final class AnnotatedPropertyInjectToConstructorInjectionRector extends AbstractRector
{
/**
* @var PropertyUsageAnalyzer
*/
private $propertyUsageAnalyzer;
/**
* @var ClassChildAnalyzer
*/
private $classChildAnalyzer;
public function __construct(
ClassChildAnalyzer $classChildAnalyzer,
PropertyUsageAnalyzer $propertyUsageAnalyzer
) {
$this->propertyUsageAnalyzer = $propertyUsageAnalyzer;
$this->classChildAnalyzer = $classChildAnalyzer;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Turns non-private properties with `@inject` to private properties and constructor injection',
[
new CodeSample(
<<<'CODE_SAMPLE'
/**
* @var SomeService
* @inject
*/
public $someService;
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
/**
* @var SomeService
*/
private $someService;
public function __construct(SomeService $someService)
{
$this->someService = $someService;
}
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Property::class];
}
/**
* @param Property $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkipProperty($node)) {
return null;
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$phpDocInfo->removeByType(NetteInjectTagNode::class);
if ($this->propertyUsageAnalyzer->isPropertyFetchedInChildClass($node)) {
$this->visibilityManipulator->makeProtected($node);
} else {
$this->visibilityManipulator->makePrivate($node);
}
$this->addPropertyToCollector($node);
if ($this->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
$this->removeNode($node);
return null;
}
return $node;
}
private function shouldSkipProperty(Property $property): bool
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
if (! $phpDocInfo->hasByType(NetteInjectTagNode::class)) {
return true;
}
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof ClassLike) {
return true;
}
if (! $classLike instanceof Class_) {
return true;
}
if ($classLike->isAbstract()) {
return true;
}
if ($this->classChildAnalyzer->hasChildClassConstructor($classLike)) {
return true;
}
if ($this->classChildAnalyzer->hasParentClassConstructor($classLike)) {
return true;
}
// it needs @var tag as well, to get the type
if ($phpDocInfo->getVarTagValueNode() !== null) {
return false;
}
return $property->type === null;
}
}

View File

@ -1,294 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Rector\Property;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\JMS\JMSInjectTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPDI\PHPDIInjectTagValueNode;
use Rector\ChangesReporting\Application\ErrorAndDiffCollector;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Symfony\ServiceMapProvider;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
* @see https://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations#inject
*
* @see \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\InjectAnnotationClassRectorTest
*/
final class InjectAnnotationClassRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var string
*/
public const ANNOTATION_CLASSES = 'annotation_classes';
/**
* @var array<string, class-string<JMSInjectTagValueNode>|class-string<PHPDIInjectTagValueNode>>
*/
private const ANNOTATION_TO_TAG_CLASS = [
'DI\Annotation\Inject' => PHPDIInjectTagValueNode::class,
'JMS\DiExtraBundle\Annotation\Inject' => JMSInjectTagValueNode::class,
];
/**
* @var string
* @see https://regex101.com/r/pjusUN/1
*/
private const BETWEEN_PERCENT_CHARS_REGEX = '#%(.*?)%#';
/**
* @var string[]
*/
private $annotationClasses = [];
/**
* @var ErrorAndDiffCollector
*/
private $errorAndDiffCollector;
/**
* @var ServiceMapProvider
*/
private $serviceMapProvider;
/**
* @var PhpDocTypeChanger
*/
private $phpDocTypeChanger;
public function __construct(
ServiceMapProvider $serviceMapProvider,
ErrorAndDiffCollector $errorAndDiffCollector,
PhpDocTypeChanger $phpDocTypeChanger
) {
$this->errorAndDiffCollector = $errorAndDiffCollector;
$this->serviceMapProvider = $serviceMapProvider;
$this->phpDocTypeChanger = $phpDocTypeChanger;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Changes properties with specified annotations class to constructor injection',
[
new ConfiguredCodeSample(
<<<'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;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = entityManager;
}
}
CODE_SAMPLE
,
[
self::ANNOTATION_CLASSES => ['DI\Annotation\Inject', 'JMS\DiExtraBundle\Annotation\Inject'],
]
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Property::class];
}
/**
* @param Property $node
*/
public function refactor(Node $node): ?Node
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
foreach ($this->annotationClasses as $annotationClass) {
$this->ensureAnnotationClassIsSupported($annotationClass);
$tagClass = self::ANNOTATION_TO_TAG_CLASS[$annotationClass];
$injectTagValueNode = $phpDocInfo->getByType($tagClass);
if ($injectTagValueNode === null) {
continue;
}
if ($this->isParameterInject($injectTagValueNode)) {
return null;
}
$type = $this->resolveType($node, $injectTagValueNode);
return $this->refactorPropertyWithAnnotation($node, $type, $tagClass);
}
return null;
}
public function configure(array $configuration): void
{
$this->annotationClasses = $configuration[self::ANNOTATION_CLASSES] ?? [];
}
private function ensureAnnotationClassIsSupported(string $annotationClass): void
{
if (isset(self::ANNOTATION_TO_TAG_CLASS[$annotationClass])) {
return;
}
$availableAnnotations = array_keys(self::ANNOTATION_TO_TAG_CLASS);
$errorMessage = sprintf(
'Annotation class "%s" is not implemented yet. Use one of "%s" or add custom tag for it to Rector.',
$annotationClass,
implode('", "', $availableAnnotations)
);
throw new NotImplementedYetException($errorMessage);
}
private function isParameterInject(PhpDocTagValueNode $phpDocTagValueNode): bool
{
if (! $phpDocTagValueNode instanceof JMSInjectTagValueNode) {
return false;
}
$serviceName = $phpDocTagValueNode->getServiceName();
if ($serviceName === null) {
return false;
}
return (bool) Strings::match($serviceName, self::BETWEEN_PERCENT_CHARS_REGEX);
}
private function resolveType(Property $property, PhpDocTagValueNode $phpDocTagValueNode): Type
{
if ($phpDocTagValueNode instanceof JMSInjectTagValueNode) {
return $this->resolveJMSDIInjectType($property, $phpDocTagValueNode);
}
if ($phpDocTagValueNode instanceof PHPDIInjectTagValueNode) {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
return $phpDocInfo->getVarType();
}
throw new ShouldNotHappenException();
}
private function refactorPropertyWithAnnotation(Property $property, Type $type, string $tagClass): ?Property
{
if ($type instanceof MixedType) {
return null;
}
$propertyName = $this->getName($property);
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$this->phpDocTypeChanger->changeVarType($phpDocInfo, $type);
$phpDocInfo->removeByType($tagClass);
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
throw new ShouldNotHappenException();
}
$this->addConstructorDependencyToClass($classLike, $type, $propertyName, $property->flags);
if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
$this->removeNode($property);
return null;
}
return $property;
}
private function resolveJMSDIInjectType(Property $property, JMSInjectTagValueNode $jmsInjectTagValueNode): Type
{
$serviceMap = $this->serviceMapProvider->provide();
$serviceName = $jmsInjectTagValueNode->getServiceName();
if ($serviceName) {
if (class_exists($serviceName)) {
// single class service
return new ObjectType($serviceName);
}
// 2. service name
if ($serviceMap->hasService($serviceName)) {
$serviceType = $serviceMap->getServiceType($serviceName);
if ($serviceType !== null) {
return $serviceType;
}
}
}
// 3. service is in @var annotation
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$varType = $phpDocInfo->getVarType();
if (! $varType instanceof MixedType) {
return $varType;
}
// the @var is missing and service name was not found → report it
$this->reportServiceNotFound($serviceName, $property);
return new MixedType();
}
private function reportServiceNotFound(?string $serviceName, Property $property): void
{
if ($serviceName !== null) {
return;
}
/** @var SmartFileInfo $fileInfo */
$fileInfo = $property->getAttribute(AttributeKey::FILE_INFO);
$errorMessage = sprintf('Service "%s" was not found in DI Container of your Symfony App.', $serviceName);
$this->errorAndDiffCollector->addErrorWithRectorClassMessageAndFileInfo(
self::class,
$errorMessage,
$fileInfo
);
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
class SomeClass
{
use SomeTrait;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
class SomeClass implements \Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeInterface
{
use SomeTrait;
}
?>

View File

@ -1,11 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeInterface;
use Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
class SkipExisting implements SomeInterface
{
use SomeTrait;
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source;
interface SomeInterface
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source;
trait SomeTrait
{
}

View File

@ -1,27 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass implements SomeInterface, SomeOldInterface
{
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass implements SomeInterface
{
}
?>

View File

@ -1,25 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass2 implements SomeOldInterface
{
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass2 implements Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface
{
}
?>

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source;
interface SomeInterface
{
}

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source;
interface SomeOldInterface
{
}

View File

@ -1,47 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ChildPropertyCallProtected extends SomeParentWithInject
{
public function run()
{
return $this->someProductWith;
}
}
class SomeParentWithInject
{
/**
* @inject
* @var SomeProductWithInterface
*/
public $someProductWith;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ChildPropertyCallProtected extends SomeParentWithInject
{
public function run()
{
return $this->someProductWith;
}
}
class SomeParentWithInject
{
public function __construct(protected \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface $someProductWith)
{
}
}
?>

View File

@ -1,31 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects4
{
/**
* @var SomeProductWithInterface
* @inject
*/
protected $property;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects4
{
public function __construct(private \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface $property)
{
}
}
?>

View File

@ -1,14 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithParent;
abstract class SkipAbstractClass
{
/**
* @var SomeProductWithParent
* @inject
*/
protected $property;
}

View File

@ -1,21 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct;
class SkipIfHasChildren
{
/**
* @var \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct
* @inject
*/
public $someProduct;
}
class ChildOfOneClass extends SkipIfHasChildren
{
public function __construct()
{
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct;
class SkipIfHasParentWithConstructor extends ParentWithConstructor
{
/**
* @var \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProduct
* @inject
*/
public $someProduct;
}
class ParentWithConstructor
{
public function __construct()
{
}
}

View File

@ -1,8 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Fixture;
class SkipPropertyWithoutDoc
{
public $property;
}

View File

@ -1,30 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixturePhp74;
use stdClass;
class TypedProperty
{
/**
* @inject
*/
public stdClass $property;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\FixturePhp74;
use stdClass;
class TypedProperty
{
public function __construct(private \stdClass $property)
{
}
}
?>

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
interface SomeInterface
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
class SomeParent
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProduct
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProductWithInterface implements SomeInterface
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProductWithParent extends SomeParent
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
final class SomeProductWithTrait
{
use SomeTrait;
}

View File

@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source;
trait SomeTrait
{
}

View File

@ -1,41 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeInterface;
use JMS\DiExtraBundle\Annotation as DI;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects7
{
/**
* @var SomeInterface
* @DI\Inject("irelevant")
*/
private $session;
/**
* @var SomeProductWithInterface
* @DI\Inject
*/
private $product;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeInterface;
use JMS\DiExtraBundle\Annotation as DI;
use Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface;
class ClassWithInjects7
{
public function __construct(private \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeInterface $session, private \Rector\Generic\Tests\Rector\Property\AnnotatedPropertyInjectToConstructorInjectionRector\Source\SomeProductWithInterface $product)
{
}
}
?>

View File

@ -1,31 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use DI\Annotation\Inject;
class InjectFromVar
{
/**
* @Inject
* @var \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\ExistingDependency
*/
private $someDependency;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use DI\Annotation\Inject;
class InjectFromVar
{
public function __construct(private \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\ExistingDependency $someDependency)
{
}
}
?>

View File

@ -1,31 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use DI\Annotation\Inject;
class InjectFromProtectedVar
{
/**
* @Inject
* @var \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\ExistingDependency
*/
protected $someDependency;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use DI\Annotation\Inject;
class InjectFromProtectedVar
{
public function __construct(protected \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\ExistingDependency $someDependency)
{
}
}
?>

View File

@ -1,47 +0,0 @@
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\DifferntButFirstListed\Bar as FooBarFirst;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\Bar;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\Foo;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\DifferntButFirstListed\Foo as BarFooLast;
use DI\Annotation\Inject;
class InjectFromVarWithTypeOfSameName
{
/**
* @Inject
*
* @var Bar
*/
private $someBarDependency;
/**
* @Inject
*
* @var Foo
*/
private $someFooDependency;
}
?>
-----
<?php
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Fixture;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\DifferntButFirstListed\Bar as FooBarFirst;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\Bar;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\Foo;
use Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\DifferntButFirstListed\Foo as BarFooLast;
use DI\Annotation\Inject;
class InjectFromVarWithTypeOfSameName
{
public function __construct(private \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\Bar $someBarDependency, private \Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\Foo $someFooDependency)
{
}
}
?>

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source;
final class Bar
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\DifferntButFirstListed;
final class Bar
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source\DifferntButFirstListed;
final class Foo
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source;
final class ExistingDependency
{
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Property\InjectAnnotationClassRector\Source;
final class Foo
{
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Generic\Rector\Class_;
namespace Rector\Transform\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
@ -14,14 +14,14 @@ use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\AddInterfaceByTraitRectorTest
* @see \Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\AddInterfaceByTraitRectorTest
*/
final class AddInterfaceByTraitRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var string
*/
public const INTERFACE_BY_TRAIT = '$interfaceByTrait';
public const INTERFACE_BY_TRAIT = 'interface_by_trait';
/**
* @var string[]

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Generic\Rector\Class_;
namespace Rector\Transform\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Name;
@ -17,7 +17,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
* - https://github.com/FriendsOfPHP/PHP-CS-Fixer/commit/a1cdb4d2dd8f45d731244eed406e1d537218cc66
* - https://github.com/FriendsOfPHP/PHP-CS-Fixer/commit/614d2e6f7af5a5b0be5363ff536aed2b7ee5a31d
*
* @see \Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\MergeInterfacesRectorTest
* @see \Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\MergeInterfacesRectorTest
*/
final class MergeInterfacesRector extends AbstractRector implements ConfigurableRectorInterface
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector;
namespace Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

View File

@ -0,0 +1,25 @@
<?php
namespace Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
class SomeClass
{
use SomeTrait;
}
?>
-----
<?php
namespace Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
class SomeClass implements \Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeInterface
{
use SomeTrait;
}
?>

View File

@ -0,0 +1,11 @@
<?php
namespace Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeInterface;
use Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
class SkipExisting implements SomeInterface
{
use SomeTrait;
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source;
interface SomeInterface
{
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source;
trait SomeTrait
{
}

View File

@ -1,8 +1,8 @@
<?php
use Rector\Generic\Rector\Class_\AddInterfaceByTraitRector;
use Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeInterface;
use Rector\Generic\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
use Rector\Transform\Rector\Class_\AddInterfaceByTraitRector;
use Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeInterface;
use Rector\Transform\Tests\Rector\Class_\AddInterfaceByTraitRector\Source\SomeTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass implements SomeInterface, SomeOldInterface
{
}
?>
-----
<?php
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass implements SomeInterface
{
}
?>

View File

@ -0,0 +1,25 @@
<?php
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass2 implements SomeOldInterface
{
}
?>
-----
<?php
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Fixture;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
class SomeClass2 implements Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface
{
}
?>

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector;
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source;
interface SomeInterface
{
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source;
interface SomeOldInterface
{
}

View File

@ -1,8 +1,8 @@
<?php
use Rector\Generic\Rector\Class_\MergeInterfacesRector;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface;
use Rector\Generic\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
use Rector\Transform\Rector\Class_\MergeInterfacesRector;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeInterface;
use Rector\Transform\Tests\Rector\Class_\MergeInterfacesRector\Source\SomeOldInterface;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {