Skip to content

Commit

Permalink
[BetterPhpDocParser] Add ArrayItemNode to improve value transfer in a…
Browse files Browse the repository at this point in the history
…nnotation curly lists (#2786)
  • Loading branch information
TomasVotruba committed Aug 20, 2022
1 parent 6f530a1 commit a33b200
Show file tree
Hide file tree
Showing 36 changed files with 542 additions and 336 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;

use Iterator;
use PhpParser\Node\Scalar\String_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser;
use Rector\Testing\PHPUnit\AbstractTestCase;
Expand All @@ -25,26 +27,28 @@ protected function setUp(): void

/**
* @dataProvider provideData()
* @param array<string, string>|string[] $expectedArray
*
* @param ArrayItemNode[] $expectedArrayItemNodes
*/
public function test(string $docContent, array $expectedArray): void
public function test(string $docContent, array $expectedArrayItemNodes): void
{
$betterTokenIterator = $this->tokenIteratorFactory->create($docContent);

$array = $this->arrayParser->parseCurlyArray($betterTokenIterator);
$this->assertSame($expectedArray, $array);
$arrayItemNodes = $this->arrayParser->parseCurlyArray($betterTokenIterator);
$this->assertEquals($expectedArrayItemNodes, $arrayItemNodes);
}

public function provideData(): Iterator
{
yield ['{key: "value"}', [
'key' => '"value"',
]];
yield ['{key: "value"}', [new ArrayItemNode('value', 'key', String_::KIND_DOUBLE_QUOTED)]];

yield ['{"key": "value"}', [
'"key"' => '"value"',
new ArrayItemNode('value', 'key', String_::KIND_DOUBLE_QUOTED, String_::KIND_DOUBLE_QUOTED),
]];

yield ['{"value", "value2"}', ['"value"', '"value2"']];
yield ['{"value", "value2"}', [
new ArrayItemNode('value', null, String_::KIND_DOUBLE_QUOTED),
new ArrayItemNode('value2', null, String_::KIND_DOUBLE_QUOTED),
]];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;

use Iterator;
use PhpParser\Node\Scalar\String_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDocInfo\TokenIteratorFactory;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
Expand Down Expand Up @@ -40,7 +42,10 @@ public function test(string $docContent, CurlyListNode | array $expectedValue):

public function provideData(): Iterator
{
$curlyListNode = new CurlyListNode(['"chalet"', '"apartment"']);
$curlyListNode = new CurlyListNode([
new ArrayItemNode('chalet', null, String_::KIND_DOUBLE_QUOTED),
new ArrayItemNode('apartment', null, String_::KIND_DOUBLE_QUOTED),
]);
yield ['{"chalet", "apartment"}', $curlyListNode];

yield [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace Rector\Tests\BetterPhpDocParser\PhpDocParser\TagValueNodeReprint;

use PhpParser\Node;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
Expand Down Expand Up @@ -58,8 +60,13 @@ public function test(): void
$doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass(
'Symfony\Component\Routing\Annotation\Route'
);

// this will extended tokens of first node
$doctrineAnnotationTagValueNode->changeValue('methods', new CurlyListNode(['"GET"', '"HEAD"']));
$methodsCurlyListNode = new CurlyListNode([
new ArrayItemNode('GET', null, String_::KIND_DOUBLE_QUOTED),
new ArrayItemNode('HEAD', null, String_::KIND_DOUBLE_QUOTED),
]);
$doctrineAnnotationTagValueNode->values[] = new ArrayItemNode($methodsCurlyListNode, 'methods');

$expectedDocContent = trim((string) $inputFileInfoAndExpected->getExpected());

Expand Down
18 changes: 16 additions & 2 deletions packages/BetterPhpDocParser/PhpDoc/ArrayItemNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,32 @@ final class ArrayItemNode implements PhpDocTagValueNode, Stringable
* @param String_::KIND_*|null $kindValueQuoted
*/
public function __construct(
private readonly mixed $value,
private readonly int|null $kindValueQuoted = null,
public mixed $value,
public mixed $key,
public int|null $kindValueQuoted = null,
public int|null $kindKeyQuoted = null,
) {
}

public function __toString(): string
{
$value = '';

if ($this->kindKeyQuoted === String_::KIND_DOUBLE_QUOTED) {
$value .= '"' . $this->key . '" = ';
} elseif ($this->key !== null) {
$value .= $this->key . '=';
}

// @todo depends on the context! possibly the top array is quting this stinrg already
if ($this->kindValueQuoted === String_::KIND_DOUBLE_QUOTED) {
$value .= '"' . $this->value . '"';
} elseif (is_array($this->value)) {
foreach ($this->value as $singleValue) {
$value .= $singleValue;
}
} else {
$value .= $this->value;
}

return $value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
final class DoctrineAnnotationTagValueNode extends AbstractValuesAwareNode implements Stringable
{
/**
* @param array<mixed, mixed> $values
* @param ArrayItemNode[] $values
*/
public function __construct(
public IdentifierTypeNode $identifierTypeNode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher;
Expand Down Expand Up @@ -43,21 +44,30 @@ private function processAssertChoiceTagValueNode(array $oldToNewClasses, PhpDocI
return;
}

$callback = $assertChoiceTagValueNode->getValueWithoutQuotes('callback');
if (! $callback instanceof CurlyListNode) {
$callbackArrayItemNode = $assertChoiceTagValueNode->getValue('callback');
if (! $callbackArrayItemNode instanceof ArrayItemNode) {
return;
}

$callbackClass = $callback->getValueWithoutQuotes(0);
$callbackClass = $callbackArrayItemNode->value;

// array is needed for callable
if (! $callbackClass instanceof CurlyListNode) {
return;
}

$callableCallbackArrayItems = $callbackClass->getValues();
$classNameArrayItemNode = $callableCallbackArrayItems[0];

foreach ($oldToNewClasses as $oldClass => $newClass) {
if ($callbackClass !== $oldClass) {
if ($classNameArrayItemNode->value !== $oldClass) {
continue;
}

$callback->changeValue('0', $newClass);
$classNameArrayItemNode->value = $newClass;

$assertChoiceTagValueNode->changeValue('callback', $callback);
// trigger reprint
$classNameArrayItemNode->setAttribute('orig_node', null);
break;
}
}
Expand Down Expand Up @@ -93,29 +103,32 @@ private function processSerializerTypeTagValueNode(array $oldToNewClasses, PhpDo
return;
}

foreach ($oldToNewClasses as $oldClass => $newClass) {
$className = $doctrineAnnotationTagValueNode->getSilentValue();
$classNameArrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();

if (is_string($className)) {
if ($className === $oldClass) {
$doctrineAnnotationTagValueNode->changeSilentValue($newClass);
foreach ($oldToNewClasses as $oldClass => $newClass) {
if ($classNameArrayItemNode instanceof ArrayItemNode) {
if ($classNameArrayItemNode->value === $oldClass) {
$classNameArrayItemNode->value = $newClass;
continue;
}

$newContent = Strings::replace($className, '#\b' . preg_quote($oldClass, '#') . '\b#', $newClass);
if ($newContent === $className) {
continue;
}
$classNameArrayItemNode->value = Strings::replace(
$classNameArrayItemNode->value,
'#\b' . preg_quote($oldClass, '#') . '\b#',
$newClass
);

$doctrineAnnotationTagValueNode->changeSilentValue($newContent);
continue;
$classNameArrayItemNode->setAttribute('orig_node', null);
}

$currentType = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type');
if ($currentType === $oldClass) {
$doctrineAnnotationTagValueNode->changeValue('type', $newClass);
$currentTypeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
if (! $currentTypeArrayItemNode instanceof ArrayItemNode) {
continue;
}

if ($currentTypeArrayItemNode->value === $oldClass) {
$currentTypeArrayItemNode->value = $newClass;
}
}
}

Expand All @@ -131,20 +144,23 @@ private function processDoctrineToMany(
'Doctrine\ORM\Mapping\Embedded'
) ? 'class' : 'targetEntity';

$targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes($classKey);
if ($targetEntity === null) {
$targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue($classKey);
if (! $targetEntityArrayItemNode instanceof ArrayItemNode) {
return;
}

$targetEntityClass = $targetEntityArrayItemNode->value;

// resolve to FQN
$tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntity, $node);
$tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntityClass, $node);

foreach ($oldToNewClasses as $oldClass => $newClass) {
if ($tagFullyQualifiedName !== $oldClass) {
continue;
}

$doctrineAnnotationTagValueNode->changeValue($classKey, $newClass);
$targetEntityArrayItemNode->value = $newClass;
$targetEntityArrayItemNode->setAttribute('orig_node', null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\PlainValueParser;
Expand All @@ -26,7 +27,8 @@ public function __construct(

/**
* mimics: https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1024-L1041
* @return array<mixed, mixed>
*
* @return ArrayItemNode[]
*/
public function resolveAnnotationMethodCall(BetterTokenIterator $tokenIterator): array
{
Expand Down Expand Up @@ -76,7 +78,8 @@ public function resolveAnnotationValue(

/**
* @see https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1051-L1079
* @return array<mixed>
*
* @return ArrayItemNode[]
*/
private function resolveAnnotationValues(BetterTokenIterator $tokenIterator): array
{
Expand Down Expand Up @@ -110,7 +113,7 @@ private function resolveAnnotationValues(BetterTokenIterator $tokenIterator): ar
}
}

return $values;
return $this->arrayParser->createArrayFromValues($values);
}

/**
Expand Down

0 comments on commit a33b200

Please sign in to comment.