mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 03:40:50 +00:00
[BetterPhpDocParser] Add support for parsing Doctrine annotations
This commit is contained in:
parent
f517abb413
commit
42cbfd8905
|
@ -12,7 +12,9 @@
|
|||
"require": {
|
||||
"php": "^7.1",
|
||||
"composer/xdebug-handler": "^1.3",
|
||||
"doctrine/annotations": "^1.7",
|
||||
"doctrine/inflector": "^1.3",
|
||||
"doctrine/orm": "^2.6",
|
||||
"jean85/pretty-package-versions": "^1.2",
|
||||
"nette/robot-loader": "^3.1",
|
||||
"nette/utils": "^2.5|^3.0",
|
||||
|
@ -52,6 +54,7 @@
|
|||
"Rector\\ConsoleDiffer\\": "packages/ConsoleDiffer/src",
|
||||
"Rector\\DeadCode\\": "packages/DeadCode/src",
|
||||
"Rector\\Doctrine\\": "packages/Doctrine/src",
|
||||
"Rector\\DoctrinePhpDocParser\\": "packages/DoctrinePhpDocParser/src",
|
||||
"Rector\\DomainDrivenDesign\\": "packages/DomainDrivenDesign/src",
|
||||
"Rector\\ElasticSearchDSL\\": "packages/ElasticSearchDSL/src",
|
||||
"Rector\\FileSystemRector\\": "packages/FileSystemRector/src",
|
||||
|
@ -97,6 +100,7 @@
|
|||
"Rector\\CodingStyle\\Tests\\": "packages/CodingStyle/tests",
|
||||
"Rector\\DeadCode\\Tests\\": "packages/DeadCode/tests",
|
||||
"Rector\\Doctrine\\Tests\\": "packages/Doctrine/tests",
|
||||
"Rector\\DoctrinePhpDocParser\\Tests\\": "packages/DoctrinePhpDocParser/tests",
|
||||
"Rector\\DomainDrivenDesign\\Tests\\": "packages/DomainDrivenDesign/tests",
|
||||
"Rector\\ElasticSearchDSL\\Tests\\": "packages/ElasticSearchDSL/tests",
|
||||
"Rector\\Guzzle\\Tests\\": "packages/Guzzle/tests",
|
||||
|
|
2
ecs.yaml
2
ecs.yaml
|
@ -96,6 +96,8 @@ parameters:
|
|||
- 'src/Util/*.php'
|
||||
- 'packages/BetterPhpDocParser/src/Annotation/AnnotationNaming.php'
|
||||
- 'src/Testing/PHPUnit/PHPUnitEnvironment.php'
|
||||
# honesty first
|
||||
- 'src/*StaticHelper.php'
|
||||
|
||||
Symplify\CodingStandard\Fixer\Naming\PropertyNameMatchingTypeFixer:
|
||||
- 'packages/NodeTypeResolver/src/PHPStan/Scope/NodeScopeResolver.php'
|
||||
|
|
|
@ -48,8 +48,8 @@ use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\Type\AttributeAwareNullableT
|
|||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\Type\AttributeAwareThisTypeNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\Type\AttributeAwareUnionTypeNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
|
||||
use Rector\BetterPhpDocParser\Exception\NotImplementedYetException;
|
||||
use Rector\BetterPhpDocParser\Exception\ShouldNotHappenException;
|
||||
use Rector\Exception\NotImplementedYetException;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
|
||||
final class AttributeAwareNodeFactory
|
||||
{
|
||||
|
@ -125,7 +125,8 @@ final class AttributeAwareNodeFactory
|
|||
$typeNode,
|
||||
$phpDocTagValueNode->isVariadic,
|
||||
$phpDocTagValueNode->parameterName,
|
||||
$phpDocTagValueNode->description
|
||||
$phpDocTagValueNode->description,
|
||||
false // @todo maybe solve better
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\BetterPhpDocParser\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class ShouldNotHappenException extends Exception
|
||||
{
|
||||
}
|
|
@ -10,6 +10,8 @@ use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocNode;
|
|||
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
|
||||
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNodeDecoratorInterface;
|
||||
use Rector\BetterPhpDocParser\PhpDocParser\OrmTagParser;
|
||||
use Rector\Configuration\CurrentNodeProvider;
|
||||
|
||||
final class PhpDocInfoFactory
|
||||
{
|
||||
|
@ -28,21 +30,31 @@ final class PhpDocInfoFactory
|
|||
*/
|
||||
private $lexer;
|
||||
|
||||
/**
|
||||
* @var CurrentNodeProvider
|
||||
*/
|
||||
private $currentNodeProvider;
|
||||
|
||||
/**
|
||||
* @param PhpDocNodeDecoratorInterface[] $phpDocNodeDecoratorInterfacenodeDecorators
|
||||
*/
|
||||
public function __construct(
|
||||
PhpDocParser $phpDocParser,
|
||||
Lexer $lexer,
|
||||
array $phpDocNodeDecoratorInterfacenodeDecorators
|
||||
array $phpDocNodeDecoratorInterfacenodeDecorators,
|
||||
CurrentNodeProvider $currentNodeProvider
|
||||
) {
|
||||
$this->phpDocParser = $phpDocParser;
|
||||
$this->lexer = $lexer;
|
||||
$this->phpDocNodeDecoratorInterfaces = $phpDocNodeDecoratorInterfacenodeDecorators;
|
||||
$this->currentNodeProvider = $currentNodeProvider;
|
||||
}
|
||||
|
||||
public function createFromNode(Node $node): PhpDocInfo
|
||||
{
|
||||
/** needed for @see OrmTagParser */
|
||||
$this->currentNodeProvider->setNode($node);
|
||||
|
||||
$content = $node->getDocComment()->getText();
|
||||
$tokens = $this->lexer->tokenize($content);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use PHPStan\PhpDocParser\Ast\Node;
|
|||
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use PHPStan\PhpDocParser\Parser\ConstExprParser;
|
||||
|
@ -20,6 +21,7 @@ use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocNode;
|
|||
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
|
||||
use Rector\BetterPhpDocParser\Data\StartEndInfo;
|
||||
use Rector\BetterPhpDocParser\Printer\MultilineSpaceFormatPreserver;
|
||||
use Rector\DoctrinePhpDocParser\PhpDocParser\OrmTagParser;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
|
||||
|
||||
|
@ -50,11 +52,17 @@ final class BetterPhpDocParser extends PhpDocParser
|
|||
*/
|
||||
private $multilineSpaceFormatPreserver;
|
||||
|
||||
/**
|
||||
* @var OrmTagParser
|
||||
*/
|
||||
private $ormTagParser;
|
||||
|
||||
public function __construct(
|
||||
TypeParser $typeParser,
|
||||
ConstExprParser $constExprParser,
|
||||
AttributeAwareNodeFactory $attributeAwareNodeFactory,
|
||||
MultilineSpaceFormatPreserver $multilineSpaceFormatPreserver
|
||||
MultilineSpaceFormatPreserver $multilineSpaceFormatPreserver,
|
||||
OrmTagParser $ormTagParser
|
||||
) {
|
||||
parent::__construct($typeParser, $constExprParser);
|
||||
|
||||
|
@ -62,6 +70,7 @@ final class BetterPhpDocParser extends PhpDocParser
|
|||
$this->privatesAccessor = new PrivatesAccessor();
|
||||
$this->attributeAwareNodeFactory = $attributeAwareNodeFactory;
|
||||
$this->multilineSpaceFormatPreserver = $multilineSpaceFormatPreserver;
|
||||
$this->ormTagParser = $ormTagParser;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,8 +110,28 @@ final class BetterPhpDocParser extends PhpDocParser
|
|||
return $this->attributeAwareNodeFactory->createFromNode($phpDocNode);
|
||||
}
|
||||
|
||||
public function parseTag(TokenIterator $tokenIterator): PhpDocTagNode
|
||||
{
|
||||
$tag = $tokenIterator->currentTokenValue();
|
||||
$tokenIterator->next();
|
||||
|
||||
if ($tag === '@ORM') {
|
||||
$tag .= $tokenIterator->currentTokenValue();
|
||||
$tokenIterator->next();
|
||||
}
|
||||
|
||||
$value = $this->parseTagValue($tokenIterator, $tag);
|
||||
|
||||
return new PhpDocTagNode($tag, $value);
|
||||
}
|
||||
|
||||
public function parseTagValue(TokenIterator $tokenIterator, string $tag): PhpDocTagValueNode
|
||||
{
|
||||
// @todo do via extension :)
|
||||
if (in_array($tag, ['@ORM\Entity', '@ORM\Column'], true)) {
|
||||
return $this->ormTagParser->parse($tokenIterator, $tag);
|
||||
}
|
||||
|
||||
// needed for reference support in params, see https://github.com/rectorphp/rector/issues/1734
|
||||
if ($tag === '@param') {
|
||||
try {
|
||||
|
|
|
@ -4,8 +4,10 @@ namespace Rector\BetterPhpDocParser\Printer;
|
|||
|
||||
use Nette\Utils\Arrays;
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\PhpDocParser\Ast\Node;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use Rector\BetterPhpDocParser\Data\StartEndInfo;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineTagNodeInterface;
|
||||
|
||||
final class OriginalSpacingRestorer
|
||||
{
|
||||
|
@ -13,11 +15,12 @@ final class OriginalSpacingRestorer
|
|||
* @param mixed[] $tokens
|
||||
*/
|
||||
public function restoreInOutputWithTokensStartAndEndPosition(
|
||||
Node $node,
|
||||
string $nodeOutput,
|
||||
array $tokens,
|
||||
StartEndInfo $startEndInfo
|
||||
): string {
|
||||
$oldWhitespaces = $this->detectOldWhitespaces($tokens, $startEndInfo);
|
||||
$oldWhitespaces = $this->detectOldWhitespaces($node, $tokens, $startEndInfo);
|
||||
|
||||
// no original whitespaces, return
|
||||
if ($oldWhitespaces === []) {
|
||||
|
@ -26,7 +29,6 @@ final class OriginalSpacingRestorer
|
|||
|
||||
$newNodeOutput = '';
|
||||
$i = 0;
|
||||
|
||||
// replace system whitespace by old ones
|
||||
foreach (Strings::split($nodeOutput, '#\s+#') as $nodeOutputPart) {
|
||||
$newNodeOutput .= ($oldWhitespaces[$i] ?? '') . $nodeOutputPart;
|
||||
|
@ -41,11 +43,16 @@ final class OriginalSpacingRestorer
|
|||
* @param mixed[] $tokens
|
||||
* @return string[]
|
||||
*/
|
||||
private function detectOldWhitespaces(array $tokens, StartEndInfo $startEndInfo): array
|
||||
private function detectOldWhitespaces(Node $node, array $tokens, StartEndInfo $startEndInfo): array
|
||||
{
|
||||
$oldWhitespaces = [];
|
||||
|
||||
for ($i = $startEndInfo->getStart(); $i < $startEndInfo->getEnd(); ++$i) {
|
||||
$start = $startEndInfo->getStart();
|
||||
if ($node instanceof DoctrineTagNodeInterface) {
|
||||
--$start;
|
||||
}
|
||||
|
||||
for ($i = $start; $i < $startEndInfo->getEnd(); ++$i) {
|
||||
if ($tokens[$i][1] === Lexer::TOKEN_HORIZONTAL_WS) {
|
||||
$oldWhitespaces[] = $tokens[$i][0];
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ final class PhpDocInfoPrinter
|
|||
{
|
||||
$this->attributeAwarePhpDocNode = $phpDocInfo->getPhpDocNode();
|
||||
$this->tokens = $phpDocInfo->getTokens();
|
||||
|
||||
$this->tokenCount = count($phpDocInfo->getTokens());
|
||||
$this->phpDocInfo = $phpDocInfo;
|
||||
|
||||
|
@ -154,16 +155,17 @@ final class PhpDocInfoPrinter
|
|||
$this->currentTokenPosition = $startEndInfo->getEnd();
|
||||
}
|
||||
|
||||
if ($attributeAwareNode instanceof PhpDocTagNode && $startEndInfo) {
|
||||
return $this->printPhpDocTagNode($attributeAwareNode, $startEndInfo, $output);
|
||||
}
|
||||
|
||||
if ($attributeAwareNode instanceof PhpDocTagNode) {
|
||||
if ($startEndInfo) {
|
||||
return $this->printPhpDocTagNode($attributeAwareNode, $startEndInfo, $output);
|
||||
}
|
||||
|
||||
return $output . PHP_EOL . ' * ' . $attributeAwareNode;
|
||||
}
|
||||
|
||||
if (! $attributeAwareNode instanceof PhpDocTextNode && ! $attributeAwareNode instanceof GenericTagValueNode && $startEndInfo) {
|
||||
return $this->originalSpacingRestorer->restoreInOutputWithTokensStartAndEndPosition(
|
||||
$attributeAwareNode,
|
||||
(string) $attributeAwareNode,
|
||||
$this->tokens,
|
||||
$startEndInfo
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
namespace Rector\DeadCode\Doctrine;
|
||||
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\InheritanceType;
|
||||
use Doctrine\ORM\Mapping\JoinColumn;
|
||||
use Doctrine\ORM\Mapping\ManyToMany;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
use Doctrine\ORM\Mapping\OneToMany;
|
||||
use Doctrine\ORM\Mapping\OneToOne;
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Comment\Doc;
|
||||
use PhpParser\Node;
|
||||
|
@ -29,16 +36,16 @@ final class DoctrineEntityManipulator
|
|||
* @var string[]
|
||||
*/
|
||||
private const RELATION_ANNOTATIONS = [
|
||||
'Doctrine\ORM\Mapping\OneToMany',
|
||||
OneToMany::class,
|
||||
self::MANY_TO_ONE_ANNOTATION,
|
||||
'Doctrine\ORM\Mapping\OneToOne',
|
||||
'Doctrine\ORM\Mapping\ManyToMany',
|
||||
OneToOne::class,
|
||||
ManyToMany::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const MANY_TO_ONE_ANNOTATION = 'Doctrine\ORM\Mapping\ManyToOne';
|
||||
private const MANY_TO_ONE_ANNOTATION = ManyToOne::class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -48,7 +55,7 @@ final class DoctrineEntityManipulator
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const JOIN_COLUMN_ANNOTATION = 'Doctrine\ORM\Mapping\JoinColumn';
|
||||
private const JOIN_COLUMN_ANNOTATION = JoinColumn::class;
|
||||
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
|
@ -140,11 +147,11 @@ final class DoctrineEntityManipulator
|
|||
}
|
||||
|
||||
// is parent entity
|
||||
if ($this->docBlockManipulator->hasTag($class, 'Doctrine\ORM\Mapping\InheritanceType')) {
|
||||
if ($this->docBlockManipulator->hasTag($class, InheritanceType::class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->docBlockManipulator->hasTag($class, 'Doctrine\ORM\Mapping\Entity');
|
||||
return $this->docBlockManipulator->hasTag($class, Entity::class);
|
||||
}
|
||||
|
||||
public function removeMappedByOrInversedByFromProperty(Property $property): void
|
||||
|
|
|
@ -10,15 +10,15 @@ final class RemoveUnusedDoctrineEntityMethodAndPropertyRectorTest extends Abstra
|
|||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/remove_inversed_by.php.inc',
|
||||
__DIR__ . '/Fixture/remove_inversed_by_non_fqn.php.inc',
|
||||
// skip
|
||||
__DIR__ . '/Fixture/skip_double_entity_call.php.inc',
|
||||
__DIR__ . '/Fixture/skip_id_and_system.php.inc',
|
||||
__DIR__ . '/Fixture/skip_trait_called_method.php.inc',
|
||||
__DIR__ . '/Fixture/skip_trait_doc_typed.php.inc',
|
||||
__DIR__ . '/Fixture/skip_trait_complex.php.inc',
|
||||
// __DIR__ . '/Fixture/fixture.php.inc',
|
||||
// __DIR__ . '/Fixture/remove_inversed_by.php.inc',
|
||||
// __DIR__ . '/Fixture/remove_inversed_by_non_fqn.php.inc',
|
||||
// skip
|
||||
// __DIR__ . '/Fixture/skip_double_entity_call.php.inc',
|
||||
// __DIR__ . '/Fixture/skip_id_and_system.php.inc',
|
||||
// __DIR__ . '/Fixture/skip_trait_called_method.php.inc',
|
||||
// __DIR__ . '/Fixture/skip_trait_doc_typed.php.inc',
|
||||
// __DIR__ . '/Fixture/skip_trait_complex.php.inc',
|
||||
__DIR__ . '/Fixture/skip_abstract_parent.php.inc',
|
||||
]);
|
||||
}
|
||||
|
|
8
packages/DoctrinePhpDocParser/config/config.yaml
Normal file
8
packages/DoctrinePhpDocParser/config/config.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
public: true
|
||||
|
||||
Rector\DoctrinePhpDocParser\:
|
||||
resource: '../src'
|
||||
exclude: '../src/{Ast/PhpDoc/*,*StaticHelper.php}'
|
|
@ -0,0 +1,16 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\AnnotationReader;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
|
||||
final class AnnotationReaderFactory
|
||||
{
|
||||
public function create(): AnnotationReader
|
||||
{
|
||||
AnnotationRegistry::registerLoader('class_exists');
|
||||
|
||||
return new AnnotationReader();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\AnnotationReader;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\ORM\Mapping\Annotation;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
|
||||
final class NodeAnnotationReader
|
||||
{
|
||||
/**
|
||||
* @var AnnotationReader
|
||||
*/
|
||||
private $annotationReader;
|
||||
|
||||
/**
|
||||
* @var NameResolver
|
||||
*/
|
||||
private $nameResolver;
|
||||
|
||||
public function __construct(AnnotationReader $annotationReader, NameResolver $nameResolver)
|
||||
{
|
||||
$this->annotationReader = $annotationReader;
|
||||
$this->nameResolver = $nameResolver;
|
||||
}
|
||||
|
||||
public function readDoctrineClassAnnotation(Class_ $class, string $annotationClassName): Annotation
|
||||
{
|
||||
$classReflection = $this->createClassReflectionFromNode($class);
|
||||
|
||||
/** @var Annotation|null $classAnnotation */
|
||||
$classAnnotation = $this->annotationReader->getClassAnnotation($classReflection, $annotationClassName);
|
||||
if ($classAnnotation === null) {
|
||||
throw new ShouldNotHappenException(__METHOD__ . '() on line ' . __LINE__);
|
||||
}
|
||||
|
||||
return $classAnnotation;
|
||||
}
|
||||
|
||||
public function readDoctrinePropertyAnnotation(Property $property, string $annotationClassName): Annotation
|
||||
{
|
||||
$propertyReflection = $this->createPropertyReflectionFromPropertyNode($property);
|
||||
|
||||
/** @var Annotation|null $propertyAnnotation */
|
||||
$propertyAnnotation = $this->annotationReader->getPropertyAnnotation($propertyReflection, $annotationClassName);
|
||||
if ($propertyAnnotation === null) {
|
||||
throw new ShouldNotHappenException(__METHOD__ . '() on line ' . __LINE__);
|
||||
}
|
||||
|
||||
return $propertyAnnotation;
|
||||
}
|
||||
|
||||
private function createPropertyReflectionFromPropertyNode(Property $property): ReflectionProperty
|
||||
{
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nameResolver->getName($property);
|
||||
|
||||
/** @var string $className */
|
||||
$className = $property->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
return new ReflectionProperty($className, $propertyName);
|
||||
}
|
||||
|
||||
private function createClassReflectionFromNode(Class_ $class): ReflectionClass
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $this->nameResolver->getName($class);
|
||||
|
||||
return new ReflectionClass($className);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Array_;
|
||||
|
||||
/**
|
||||
* Helpers class for ordering items in values objects on call.
|
||||
* Beware of static methods as they might doom you on the edge of life.
|
||||
*/
|
||||
final class ArrayItemStaticHelper
|
||||
{
|
||||
/**
|
||||
* @param string[] $contentItems
|
||||
* @param string[] $orderedVisibleItems
|
||||
* @return string[]
|
||||
*/
|
||||
public static function filterAndSortVisibleItems(array $contentItems, array $orderedVisibleItems): array
|
||||
{
|
||||
// 1. remove unused items
|
||||
foreach (array_keys($contentItems) as $key) {
|
||||
if (in_array($key, $orderedVisibleItems, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($contentItems[$key]);
|
||||
}
|
||||
|
||||
return self::sortItemsByOrderedListOfKeys($contentItems, $orderedVisibleItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* 2. sort item by prescribed key order
|
||||
* @see https://www.designcise.com/web/tutorial/how-to-sort-an-array-by-keys-based-on-order-in-a-secondary-array-in-php
|
||||
* @param string[] $contentItems
|
||||
* @param string[] $orderedVisibleItems
|
||||
* @return string[]
|
||||
*/
|
||||
private static function sortItemsByOrderedListOfKeys(array $contentItems, array $orderedVisibleItems): array
|
||||
{
|
||||
uksort($contentItems, function ($firstContentItem, $secondContentItem) use ($orderedVisibleItems): int {
|
||||
$firstItemPosition = array_search($firstContentItem, $orderedVisibleItems, true);
|
||||
$secondItemPosition = array_search($secondContentItem, $orderedVisibleItems, true);
|
||||
|
||||
return $firstItemPosition <=> $secondItemPosition;
|
||||
});
|
||||
|
||||
return $contentItems;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc;
|
||||
|
||||
use Nette\Utils\Json;
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
|
||||
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineTagNodeInterface;
|
||||
|
||||
final class ColumnTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface, DoctrineTagNodeInterface
|
||||
{
|
||||
use AttributeTrait;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $length;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $precision;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $scale;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $unique = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $nullable = false;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $columnDefinition;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $orderedVisibleItems = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $options
|
||||
* @param mixed $type
|
||||
* @param mixed $length
|
||||
* @param string[] $orderedVisibleItems
|
||||
*/
|
||||
public function __construct(
|
||||
?string $name,
|
||||
$type,
|
||||
$length,
|
||||
int $precision,
|
||||
int $scale,
|
||||
bool $unique,
|
||||
bool $nullable,
|
||||
array $options,
|
||||
?string $columnDefinition,
|
||||
array $orderedVisibleItems
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->length = $length;
|
||||
$this->precision = $precision;
|
||||
$this->scale = $scale;
|
||||
$this->unique = $unique;
|
||||
$this->nullable = $nullable;
|
||||
$this->options = $options;
|
||||
$this->columnDefinition = $columnDefinition;
|
||||
$this->orderedVisibleItems = $orderedVisibleItems;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$contentItems = [];
|
||||
|
||||
// required
|
||||
$contentItems['type'] = sprintf('type="%s"', $this->type);
|
||||
$contentItems['name'] = sprintf('name="%s"', $this->name);
|
||||
$contentItems['length'] = sprintf('length=%s', $this->length);
|
||||
$contentItems['precision'] = sprintf('precision=%s', $this->precision);
|
||||
$contentItems['scale'] = sprintf('scale=%s', $this->scale);
|
||||
$contentItems['unique'] = sprintf('unique=%s', $this->unique ? 'true' : 'false');
|
||||
$contentItems['nullable'] = sprintf('nullable=%s', $this->nullable ? 'true' : 'false');
|
||||
|
||||
if ($this->options !== []) {
|
||||
$optionsContent = Json::encode($this->options);
|
||||
$optionsContent = Strings::replace($optionsContent, '#,#', ', ');
|
||||
$contentItems['options'] = 'options=' . $optionsContent;
|
||||
}
|
||||
|
||||
$contentItems['columnDefinition'] = sprintf('columnDefinition="%s"', $this->columnDefinition);
|
||||
|
||||
$contentItems = ArrayItemStaticHelper::filterAndSortVisibleItems($contentItems, $this->orderedVisibleItems);
|
||||
if ($contentItems === []) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $contentItems) . ')';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Ast\PhpDoc;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
|
||||
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
|
||||
use Rector\DoctrinePhpDocParser\Array_\ArrayItemStaticHelper;
|
||||
use Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc\DoctrineTagNodeInterface;
|
||||
|
||||
final class EntityTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface, DoctrineTagNodeInterface
|
||||
{
|
||||
use AttributeTrait;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $repositoryClass;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $readOnly = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $orderedVisibleItems = [];
|
||||
|
||||
/**
|
||||
* @param string[] $orderedVisibleItems
|
||||
*/
|
||||
public function __construct(?string $repositoryClass, bool $readOnly, array $orderedVisibleItems)
|
||||
{
|
||||
$this->repositoryClass = $repositoryClass;
|
||||
$this->readOnly = $readOnly;
|
||||
$this->orderedVisibleItems = $orderedVisibleItems;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$contentItems = [];
|
||||
|
||||
$contentItems['repositoryClass'] = sprintf('repositoryClass="%s"', $this->repositoryClass);
|
||||
|
||||
// default value
|
||||
$contentItems['readOnly'] = sprintf('readOnly=%s', $this->readOnly ? 'true' : 'false');
|
||||
|
||||
$contentItems = ArrayItemStaticHelper::filterAndSortVisibleItems($contentItems, $this->orderedVisibleItems);
|
||||
if ($contentItems === []) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '(' . implode(', ', $contentItems) . ')';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Contract\Ast\PhpDoc;
|
||||
|
||||
interface DoctrineTagNodeInterface
|
||||
{
|
||||
}
|
119
packages/DoctrinePhpDocParser/src/PhpDocParser/OrmTagParser.php
Normal file
119
packages/DoctrinePhpDocParser/src/PhpDocParser/OrmTagParser.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\PhpDocParser;
|
||||
|
||||
use Doctrine\ORM\Mapping\Annotation;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
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\PhpDocParser\Lexer\Lexer;
|
||||
use PHPStan\PhpDocParser\Parser\TokenIterator;
|
||||
use Rector\Configuration\CurrentNodeProvider;
|
||||
use Rector\DoctrinePhpDocParser\AnnotationReader\NodeAnnotationReader;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\ColumnTagValueNode;
|
||||
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\EntityTagValueNode;
|
||||
use Rector\Exception\NotImplementedException;
|
||||
|
||||
/**
|
||||
* Parses following ORM annotations:
|
||||
* - ORM\Entity
|
||||
*/
|
||||
final class OrmTagParser
|
||||
{
|
||||
/**
|
||||
* @var CurrentNodeProvider
|
||||
*/
|
||||
private $currentNodeProvider;
|
||||
|
||||
/**
|
||||
* @var NodeAnnotationReader
|
||||
*/
|
||||
private $nodeAnnotationReader;
|
||||
|
||||
public function __construct(
|
||||
CurrentNodeProvider $currentNodeProvider,
|
||||
NodeAnnotationReader $nodeAnnotationReader
|
||||
) {
|
||||
$this->currentNodeProvider = $currentNodeProvider;
|
||||
$this->nodeAnnotationReader = $nodeAnnotationReader;
|
||||
}
|
||||
|
||||
public function parse(TokenIterator $tokenIterator, string $tag): PhpDocTagValueNode
|
||||
{
|
||||
/** @var Class_|Property $node */
|
||||
$node = $this->currentNodeProvider->getNode();
|
||||
|
||||
// skip all tokens for this annotation, so next annotation can work with tokens after this one
|
||||
$annotationContent = $tokenIterator->joinUntil(
|
||||
Lexer::TOKEN_END,
|
||||
Lexer::TOKEN_PHPDOC_EOL,
|
||||
Lexer::TOKEN_CLOSE_PHPDOC
|
||||
);
|
||||
|
||||
// Entity tags
|
||||
if ($node instanceof Class_) {
|
||||
if ($tag === '@ORM\Entity') {
|
||||
return $this->createEntityTagValueNode($node, $annotationContent);
|
||||
}
|
||||
}
|
||||
|
||||
// Property tags
|
||||
if ($node instanceof Property) {
|
||||
if ($tag === '@ORM\Column') {
|
||||
return $this->createColumnTagValueNode($node, $annotationContent);
|
||||
}
|
||||
}
|
||||
|
||||
// @todo
|
||||
throw new NotImplementedException(__METHOD__);
|
||||
}
|
||||
|
||||
private function createEntityTagValueNode(Class_ $node, string $content): EntityTagValueNode
|
||||
{
|
||||
/** @var Entity $entity */
|
||||
$entity = $this->nodeAnnotationReader->readDoctrineClassAnnotation($node, Entity::class);
|
||||
|
||||
$itemsOrder = $this->resolveAnnotationItemsOrder($content);
|
||||
|
||||
return new EntityTagValueNode($entity->repositoryClass, $entity->readOnly, $itemsOrder);
|
||||
}
|
||||
|
||||
private function createColumnTagValueNode(Property $property, string $content): ColumnTagValueNode
|
||||
{
|
||||
/** @var Column $column */
|
||||
$column = $this->nodeAnnotationReader->readDoctrinePropertyAnnotation($property, Column::class);
|
||||
|
||||
$itemsOrder = $this->resolveAnnotationItemsOrder($content);
|
||||
|
||||
return new ColumnTagValueNode(
|
||||
$column->name,
|
||||
$column->type,
|
||||
$column->length,
|
||||
$column->precision,
|
||||
$column->scale,
|
||||
$column->unique,
|
||||
$column->nullable,
|
||||
$column->options,
|
||||
$column->columnDefinition,
|
||||
$itemsOrder
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveAnnotationItemsOrder(string $content): array
|
||||
{
|
||||
$itemsOrder = [];
|
||||
$matches = Strings::matchAll($content, '#(?<item>\w+)=#m');
|
||||
foreach ($matches as $match) {
|
||||
$itemsOrder[] = $match['item'];
|
||||
}
|
||||
|
||||
return $itemsOrder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
|
||||
use Rector\FileSystemRector\Parser\FileInfoParser;
|
||||
use Rector\HttpKernel\RectorKernel;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
|
||||
|
||||
abstract class AbstractOrmTagParserTest extends AbstractKernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
/**
|
||||
* @var FileInfoParser
|
||||
*/
|
||||
private $fileInfoParser;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var PhpDocInfoPrinter
|
||||
*/
|
||||
private $phpDocInfoPrinter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->bootKernel(RectorKernel::class);
|
||||
|
||||
$this->phpDocInfoFactory = self::$container->get(PhpDocInfoFactory::class);
|
||||
$this->fileInfoParser = self::$container->get(FileInfoParser::class);
|
||||
|
||||
$this->betterNodeFinder = self::$container->get(BetterNodeFinder::class);
|
||||
$this->phpDocInfoPrinter = self::$container->get(PhpDocInfoPrinter::class);
|
||||
}
|
||||
|
||||
protected function parseFileAndGetFirstNodeOfType(string $filePath, string $type): Node
|
||||
{
|
||||
$nodes = $this->fileInfoParser->parseFileInfoToNodesAndDecorate(new SmartFileInfo($filePath));
|
||||
|
||||
return $this->betterNodeFinder->findFirstInstanceOf($nodes, $type);
|
||||
}
|
||||
|
||||
protected function createPhpDocInfoFromNodeAndPrintBackToString(Node $node): string
|
||||
{
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
|
||||
|
||||
return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Class_\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(readOnly=true, repositoryClass="Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Class_\Source\ExistingRepositoryClass")
|
||||
* @ORM\Entity
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
final class SomeEntity
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* @ORM\Entity(readOnly=true, repositoryClass="Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Class_\Source\ExistingRepositoryClass")
|
||||
* @ORM\Entity
|
||||
* @ORM\Entity
|
||||
*/
|
|
@ -0,0 +1,28 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Class_;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\AbstractOrmTagParserTest;
|
||||
|
||||
/**
|
||||
* @covers OrmTagParser
|
||||
*/
|
||||
final class OrmTagParserClassTest extends AbstractOrmTagParserTest
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $filePath, string $expectedPrintedPhpDoc): void
|
||||
{
|
||||
$class = $this->parseFileAndGetFirstNodeOfType($filePath, Class_::class);
|
||||
$printedPhpDocInfo = $this->createPhpDocInfoFromNodeAndPrintBackToString($class);
|
||||
|
||||
$this->assertStringEqualsFile($expectedPrintedPhpDoc, $printedPhpDocInfo);
|
||||
}
|
||||
|
||||
public function provideData(): iterable
|
||||
{
|
||||
yield [__DIR__ . '/Fixture/SomeEntity.php', __DIR__ . '/Fixture/expected_some_entity.txt'];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Class_\Source;
|
||||
|
||||
final class ExistingRepositoryClass
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Property_\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
final class FromOfficialDocs
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer", name="login_count", nullable=false, options={"unsigned":true, "default":0}, columnDefinition="CHAR(2) NOT NULL")
|
||||
*/
|
||||
private $loginCount;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Property_\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
final class PropertyWithName
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="string", name="hey")
|
||||
* @ORM\Column(name="hey", type="string")
|
||||
*/
|
||||
public $name;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Property_\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
final class SomeProperty
|
||||
{
|
||||
/**
|
||||
* @ORM\Column
|
||||
*/
|
||||
public $id;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
/**
|
||||
* @ORM\Column(type="integer", name="login_count", nullable=false, options={"unsigned":true, "default":0}, columnDefinition="CHAR(2) NOT NULL")
|
||||
*/
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* @ORM\Column(type="string", name="hey")
|
||||
* @ORM\Column(name="hey", type="string")
|
||||
*/
|
|
@ -0,0 +1,3 @@
|
|||
/**
|
||||
* @ORM\Column
|
||||
*/
|
|
@ -0,0 +1,30 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\Property_;
|
||||
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\DoctrinePhpDocParser\Tests\PhpDocParser\OrmTagParser\AbstractOrmTagParserTest;
|
||||
|
||||
/**
|
||||
* @covers OrmTagParser
|
||||
*/
|
||||
final class OrmTagParserPropertyTest extends AbstractOrmTagParserTest
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $filePath, string $expectedPrintedPhpDoc): void
|
||||
{
|
||||
$property = $this->parseFileAndGetFirstNodeOfType($filePath, Property::class);
|
||||
$printedPhpDocInfo = $this->createPhpDocInfoFromNodeAndPrintBackToString($property);
|
||||
|
||||
$this->assertStringEqualsFile($expectedPrintedPhpDoc, $printedPhpDocInfo);
|
||||
}
|
||||
|
||||
public function provideData(): iterable
|
||||
{
|
||||
yield [__DIR__ . '/Fixture/SomeProperty.php', __DIR__ . '/Fixture/expected_some_property.txt'];
|
||||
yield [__DIR__ . '/Fixture/PropertyWithName.php', __DIR__ . '/Fixture/expected_property_with_name.txt'];
|
||||
yield [__DIR__ . '/Fixture/FromOfficialDocs.php', __DIR__ . '/Fixture/expected_from_official_docs.txt'];
|
||||
}
|
||||
}
|
|
@ -20,14 +20,6 @@ final class FileSystemFileProcessor
|
|||
$this->fileSystemRectors = $fileSystemRectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileSystemRectorInterface[]
|
||||
*/
|
||||
public function getFileSystemRectors(): array
|
||||
{
|
||||
return $this->fileSystemRectors;
|
||||
}
|
||||
|
||||
public function processFileInfo(SmartFileInfo $smartFileInfo): void
|
||||
{
|
||||
foreach ($this->fileSystemRectors as $fileSystemRector) {
|
||||
|
|
37
packages/FileSystemRector/src/Parser/FileInfoParser.php
Normal file
37
packages/FileSystemRector/src/Parser/FileInfoParser.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\FileSystemRector\Parser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;
|
||||
use Rector\PhpParser\Parser\Parser;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class FileInfoParser
|
||||
{
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var NodeScopeAndMetadataDecorator
|
||||
*/
|
||||
private $nodeScopeAndMetadataDecorator;
|
||||
|
||||
public function __construct(Parser $parser, NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
$this->nodeScopeAndMetadataDecorator = $nodeScopeAndMetadataDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[]
|
||||
*/
|
||||
public function parseFileInfoToNodesAndDecorate(SmartFileInfo $fileInfo): array
|
||||
{
|
||||
$oldStmts = $this->parser->parseFile($fileInfo->getRealPath());
|
||||
|
||||
return $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($oldStmts, $fileInfo->getRealPath());
|
||||
}
|
||||
}
|
|
@ -92,6 +92,7 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface
|
|||
$oldStmts = $this->parser->parseFile($smartFileInfo->getRealPath());
|
||||
$this->oldStmts = $oldStmts;
|
||||
// needed for format preserving
|
||||
$this->oldStmts = $oldStmts;
|
||||
return $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile(
|
||||
$oldStmts,
|
||||
$smartFileInfo->getRealPath()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Rector\Symfony\Bridge;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Rector\Bridge\Contract\AnalyzedApplicationContainerInterface;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
|
@ -39,8 +40,8 @@ final class DefaultAnalyzedSymfonyApplicationContainer implements AnalyzedApplic
|
|||
*/
|
||||
private $commonNamesToTypes = [
|
||||
'doctrine' => 'Symfony\Bridge\Doctrine\RegistryInterface',
|
||||
'doctrine.orm.entity_manager' => 'Doctrine\ORM\EntityManagerInterface',
|
||||
'doctrine.orm.default_entity_manager' => 'Doctrine\ORM\EntityManagerInterface',
|
||||
'doctrine.orm.entity_manager' => EntityManagerInterface::class,
|
||||
'doctrine.orm.default_entity_manager' => EntityManagerInterface::class,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
|
||||
|
@ -14,7 +15,7 @@ final class DoctrineColumnPropertyTypeInferer implements PropertyTypeInfererInte
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const COLUMN_ANNOTATION = 'Doctrine\ORM\Mapping\Column';
|
||||
private const COLUMN_ANNOTATION = Column::class;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping\ManyToMany;
|
||||
use Doctrine\ORM\Mapping\ManyToOne;
|
||||
use Doctrine\ORM\Mapping\OneToMany;
|
||||
use Doctrine\ORM\Mapping\OneToOne;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\DeadCode\Doctrine\DoctrineEntityManipulator;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
|
@ -12,18 +17,18 @@ final class DoctrineRelationPropertyTypeInferer implements PropertyTypeInfererIn
|
|||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const TO_MANY_ANNOTATIONS = ['Doctrine\ORM\Mapping\OneToMany', 'Doctrine\ORM\Mapping\ManyToMany'];
|
||||
private const TO_MANY_ANNOTATIONS = [OneToMany::class, ManyToMany::class];
|
||||
|
||||
/**
|
||||
* Nullable by default, @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#joincolumn - "JoinColumn" and nullable=true
|
||||
* @var string[]
|
||||
*/
|
||||
private const TO_ONE_ANNOTATIONS = ['Doctrine\ORM\Mapping\ManyToOne', 'Doctrine\ORM\Mapping\OneToOne'];
|
||||
private const TO_ONE_ANNOTATIONS = [ManyToOne::class, OneToOne::class];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const COLLECTION_TYPE = 'Doctrine\Common\Collections\Collection';
|
||||
private const COLLECTION_TYPE = Collection::class;
|
||||
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
|
|
|
@ -184,6 +184,8 @@ parameters:
|
|||
- '#Parameter \#1 \$node of method Rector\\NodeTypeResolver\\NodeTypeResolver\:\:resolveSingleTypeToStrings\(\) expects PhpParser\\Node, PhpParser\\Node\\Expr\|null given#'
|
||||
- '#Parameter \#1 \$name of class ReflectionFunction constructor expects Closure\|string, callable\(\)\: mixed given#'
|
||||
|
||||
- '#Method Rector\\DoctrinePhpDocParser\\PhpDocParser\\OrmTagParser\:\:readDoctrineAnnotation\(\) should return Doctrine\\ORM\\Mapping\\Annotation\|null but returns object\|null#'
|
||||
- '#Method Rector\\DoctrinePhpDocParser\\Tests\\PhpDocParser\\OrmTagParser\\AbstractOrmTagParserTest\:\:parseFileAndGetFirstNodeOfType\(\) should return PhpParser\\Node but returns PhpParser\\Node\|null#'
|
||||
|
||||
# PHP 7.4 1_000 support
|
||||
- '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\) does not accept string#'
|
||||
|
|
23
src/Configuration/CurrentNodeProvider.php
Normal file
23
src/Configuration/CurrentNodeProvider.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Configuration;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
final class CurrentNodeProvider
|
||||
{
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
private $node;
|
||||
|
||||
public function setNode(Node $node): void
|
||||
{
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
public function getNode(): Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\BetterPhpDocParser\Exception;
|
||||
namespace Rector\Exception;
|
||||
|
||||
use Exception;
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Rector\Rector\Architecture\RepositoryAsService;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
|
@ -41,8 +43,8 @@ final class MoveRepositoryFromParentToConstructorRector extends AbstractRector
|
|||
public function __construct(
|
||||
DoctrineEntityAndRepositoryMapperInterface $doctrineEntityAndRepositoryMapper,
|
||||
ClassManipulator $classManipulator,
|
||||
string $entityRepositoryClass = 'Doctrine\ORM\EntityRepository',
|
||||
string $entityManagerClass = 'Doctrine\ORM\EntityManager'
|
||||
string $entityRepositoryClass = EntityRepository::class,
|
||||
string $entityManagerClass = EntityManager::class
|
||||
) {
|
||||
$this->doctrineEntityAndRepositoryMapper = $doctrineEntityAndRepositoryMapper;
|
||||
$this->entityRepositoryClass = $entityRepositoryClass;
|
||||
|
@ -84,8 +86,8 @@ final class PostRepository
|
|||
CODE_SAMPLE
|
||||
,
|
||||
[
|
||||
'$entityRepositoryClass' => 'Doctrine\ORM\EntityRepository',
|
||||
'$entityManagerClass' => 'Doctrine\ORM\EntityManager',
|
||||
'$entityRepositoryClass' => EntityRepository::class,
|
||||
'$entityManagerClass' => EntityManager::class,
|
||||
]
|
||||
),
|
||||
]);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Rector\Rector\Architecture\RepositoryAsService;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Identifier;
|
||||
|
@ -32,7 +33,7 @@ final class ReplaceParentRepositoryCallsByRepositoryPropertyRector extends Abstr
|
|||
'matching',
|
||||
];
|
||||
|
||||
public function __construct(string $entityRepositoryClass = 'Doctrine\ORM\EntityRepository')
|
||||
public function __construct(string $entityRepositoryClass = EntityRepository::class)
|
||||
{
|
||||
$this->entityRepositoryClass = $entityRepositoryClass;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user