Skip to content

Commit

Permalink
[BetterPhpDocParser] Add support for parsing Doctrine annotatio… (#1902)
Browse files Browse the repository at this point in the history
[BetterPhpDocParser] Add support for parsing Doctrine annotations
  • Loading branch information
TomasVotruba authored Aug 26, 2019
2 parents f517abb + faaf605 commit 073e7cc
Show file tree
Hide file tree
Showing 44 changed files with 889 additions and 91 deletions.
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions ecs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

namespace Rector\Architecture\Rector\Class_;

use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use Rector\Architecture\Tests\Rector\Class_\RemoveRepositoryFromEntityAnnotationRector\RemoveRepositoryFromEntityAnnotationRectorTest;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\Rector\AbstractRector;
Expand All @@ -17,11 +15,6 @@
*/
final class RemoveRepositoryFromEntityAnnotationRector extends AbstractRector
{
/**
* @var string
*/
private const DOCTRINE_ORM_MAPPING_ENTITY = 'Doctrine\ORM\Mapping\Entity';

/**
* @var DocBlockManipulator
*/
Expand Down Expand Up @@ -79,21 +72,13 @@ public function refactor(Node $node): ?Node
}

$phpDocInfo = $this->docBlockManipulator->createPhpDocInfoFromNode($node);
if (! $phpDocInfo->hasTag(self::DOCTRINE_ORM_MAPPING_ENTITY)) {
return null;
}

$entityTags = $phpDocInfo->getTagsByName(self::DOCTRINE_ORM_MAPPING_ENTITY);
if ($entityTags === []) {
return null;
}

$entityTag = $entityTags[0];
if (! $entityTag->value instanceof GenericTagValueNode) {
$doctrineEntityTag = $phpDocInfo->getDoctrineEntityTag();
if ($doctrineEntityTag === null) {
return null;
}

$entityTag->value->value = Strings::replace($entityTag->value->value, '#\(repositoryClass="(.*?)"\)#');
$doctrineEntityTag->removeRepositoryClass();

// save the entity tag
$this->docBlockManipulator->updateNodeWithPhpDocInfo($node, $phpDocInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
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
{
Expand Down Expand Up @@ -125,7 +125,8 @@ private function createFromPhpDocValueNode(PhpDocTagValueNode $phpDocTagValueNod
$typeNode,
$phpDocTagValueNode->isVariadic,
$phpDocTagValueNode->parameterName,
$phpDocTagValueNode->description
$phpDocTagValueNode->description,
false // @todo maybe solve better
);
}

Expand Down

This file was deleted.

28 changes: 28 additions & 0 deletions packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareVarTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Attributes\Contract\Ast\AttributeAwareNodeInterface;
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\ColumnTagValueNode;
use Rector\DoctrinePhpDocParser\Ast\PhpDoc\EntityTagValueNode;

final class PhpDocInfo
{
Expand Down Expand Up @@ -167,6 +169,32 @@ public function getVarTypes(): array
return $this->getResolvedTypesAttribute($varTagValue);
}

public function getDoctrineEntityTag(): ?EntityTagValueNode
{
foreach ($this->phpDocNode->children as $phpDocChildNode) {
if ($phpDocChildNode instanceof PhpDocTagNode) {
if ($phpDocChildNode->value instanceof EntityTagValueNode) {
return $phpDocChildNode->value;
}
}
}

return null;
}

public function getDoctrineColumnTagValueNode(): ?ColumnTagValueNode
{
foreach ($this->phpDocNode->children as $phpDocChildNode) {
if ($phpDocChildNode instanceof PhpDocTagNode) {
if ($phpDocChildNode->value instanceof ColumnTagValueNode) {
return $phpDocChildNode->value;
}
}
}

return null;
}

/**
* @return string[]
*/
Expand Down
14 changes: 13 additions & 1 deletion packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfoFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
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
{
Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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;
Expand All @@ -20,6 +21,7 @@
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;

Expand Down Expand Up @@ -50,18 +52,25 @@ 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);

$this->privatesCaller = new PrivatesCaller();
$this->privatesAccessor = new PrivatesAccessor();
$this->attributeAwareNodeFactory = $attributeAwareNodeFactory;
$this->multilineSpaceFormatPreserver = $multilineSpaceFormatPreserver;
$this->ormTagParser = $ormTagParser;
}

/**
Expand Down Expand Up @@ -101,8 +110,28 @@ public function parse(TokenIterator $tokenIterator): PhpDocNode
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@

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
{
/**
* @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 === []) {
Expand All @@ -26,7 +29,6 @@ public function restoreInOutputWithTokensStartAndEndPosition(

$newNodeOutput = '';
$i = 0;

// replace system whitespace by old ones
foreach (Strings::split($nodeOutput, '#\s+#') as $nodeOutputPart) {
$newNodeOutput .= ($oldWhitespaces[$i] ?? '') . $nodeOutputPart;
Expand All @@ -41,11 +43,16 @@ public function restoreInOutputWithTokensStartAndEndPosition(
* @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];
}
Expand Down
10 changes: 6 additions & 4 deletions packages/BetterPhpDocParser/src/Printer/PhpDocInfoPrinter.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public function printFormatPreserving(PhpDocInfo $phpDocInfo, bool $shouldSkipEm
{
$this->attributeAwarePhpDocNode = $phpDocInfo->getPhpDocNode();
$this->tokens = $phpDocInfo->getTokens();

$this->tokenCount = count($phpDocInfo->getTokens());
$this->phpDocInfo = $phpDocInfo;

Expand Down Expand Up @@ -154,16 +155,17 @@ private function printNode(
$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
Expand Down
Loading

0 comments on commit 073e7cc

Please sign in to comment.