2019-10-13 05:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2022-06-06 17:12:56 +00:00
|
|
|
namespace Rector\BetterPhpDocParser\PhpDocManipulator;
|
2019-08-30 18:21:21 +00:00
|
|
|
|
2023-09-01 11:10:35 +00:00
|
|
|
use RectorPrefix202309\Nette\Utils\Strings;
|
2022-06-06 17:12:56 +00:00
|
|
|
use PhpParser\Node;
|
2022-08-20 20:49:07 +00:00
|
|
|
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
|
2022-06-06 17:12:56 +00:00
|
|
|
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
2023-06-02 09:42:16 +00:00
|
|
|
use Rector\BetterPhpDocParser\PhpDoc\StringNode;
|
2022-06-06 17:12:56 +00:00
|
|
|
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
|
|
|
use Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher;
|
|
|
|
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
|
2022-08-20 20:57:03 +00:00
|
|
|
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
|
2019-08-30 18:21:21 +00:00
|
|
|
final class PhpDocClassRenamer
|
|
|
|
{
|
2021-04-04 09:01:11 +00:00
|
|
|
/**
|
2023-06-11 23:01:39 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher
|
2021-04-04 09:01:11 +00:00
|
|
|
*/
|
|
|
|
private $classAnnotationMatcher;
|
2022-06-07 08:22:29 +00:00
|
|
|
public function __construct(ClassAnnotationMatcher $classAnnotationMatcher)
|
2021-04-04 09:01:11 +00:00
|
|
|
{
|
|
|
|
$this->classAnnotationMatcher = $classAnnotationMatcher;
|
|
|
|
}
|
2019-08-30 18:21:21 +00:00
|
|
|
/**
|
|
|
|
* Covers annotations like @ORM, @Serializer, @Assert etc
|
|
|
|
* See https://github.com/rectorphp/rector/issues/1872
|
|
|
|
*
|
|
|
|
* @param string[] $oldToNewClasses
|
|
|
|
*/
|
2023-09-11 21:30:42 +00:00
|
|
|
public function changeTypeInAnnotationTypes(Node $node, PhpDocInfo $phpDocInfo, array $oldToNewClasses, bool &$hasChanged) : bool
|
2019-08-30 18:21:21 +00:00
|
|
|
{
|
2023-09-11 21:30:42 +00:00
|
|
|
$this->processAssertChoiceTagValueNode($oldToNewClasses, $phpDocInfo, $hasChanged);
|
|
|
|
$this->processDoctrineRelationTagValueNode($node, $oldToNewClasses, $phpDocInfo, $hasChanged);
|
|
|
|
$this->processSerializerTypeTagValueNode($oldToNewClasses, $phpDocInfo, $hasChanged);
|
|
|
|
return $hasChanged;
|
2019-08-30 19:41:21 +00:00
|
|
|
}
|
|
|
|
/**
|
2021-04-04 09:01:11 +00:00
|
|
|
* @param array<string, string> $oldToNewClasses
|
2019-08-30 19:41:21 +00:00
|
|
|
*/
|
2023-09-11 21:30:42 +00:00
|
|
|
private function processAssertChoiceTagValueNode(array $oldToNewClasses, PhpDocInfo $phpDocInfo, bool &$hasChanged) : void
|
2019-08-30 19:41:21 +00:00
|
|
|
{
|
2023-05-26 14:00:49 +00:00
|
|
|
$assertChoiceDoctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass('Symfony\\Component\\Validator\\Constraints\\Choice');
|
|
|
|
if (!$assertChoiceDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
|
2021-04-04 09:01:11 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-05-26 14:00:49 +00:00
|
|
|
$callbackArrayItemNode = $assertChoiceDoctrineAnnotationTagValueNode->getValue('callback');
|
2022-08-20 20:49:07 +00:00
|
|
|
if (!$callbackArrayItemNode instanceof ArrayItemNode) {
|
2019-08-30 18:21:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-08-20 20:49:07 +00:00
|
|
|
$callbackClass = $callbackArrayItemNode->value;
|
|
|
|
// array is needed for callable
|
|
|
|
if (!$callbackClass instanceof CurlyListNode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$callableCallbackArrayItems = $callbackClass->getValues();
|
|
|
|
$classNameArrayItemNode = $callableCallbackArrayItems[0];
|
2023-06-02 09:42:16 +00:00
|
|
|
$classNameStringNode = $classNameArrayItemNode->value;
|
|
|
|
if (!$classNameStringNode instanceof StringNode) {
|
|
|
|
return;
|
|
|
|
}
|
2019-08-30 19:41:21 +00:00
|
|
|
foreach ($oldToNewClasses as $oldClass => $newClass) {
|
2023-06-02 09:42:16 +00:00
|
|
|
if ($classNameStringNode->value !== $oldClass) {
|
2019-08-30 19:41:21 +00:00
|
|
|
continue;
|
|
|
|
}
|
2023-06-02 09:42:16 +00:00
|
|
|
$classNameStringNode->value = $newClass;
|
2022-08-20 20:49:07 +00:00
|
|
|
// trigger reprint
|
2022-08-20 20:57:03 +00:00
|
|
|
$classNameArrayItemNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
|
2023-09-11 21:30:42 +00:00
|
|
|
$hasChanged = \true;
|
2019-08-30 19:41:21 +00:00
|
|
|
break;
|
2019-08-30 18:21:21 +00:00
|
|
|
}
|
2019-08-30 19:41:21 +00:00
|
|
|
}
|
|
|
|
/**
|
2021-04-04 09:01:11 +00:00
|
|
|
* @param array<string, string> $oldToNewClasses
|
2019-08-30 19:41:21 +00:00
|
|
|
*/
|
2023-09-11 21:30:42 +00:00
|
|
|
private function processDoctrineRelationTagValueNode(Node $node, array $oldToNewClasses, PhpDocInfo $phpDocInfo, bool &$hasChanged) : void
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2022-06-07 09:46:15 +00:00
|
|
|
$doctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClasses(['Doctrine\\ORM\\Mapping\\OneToMany', 'Doctrine\\ORM\\Mapping\\ManyToMany', 'Doctrine\\ORM\\Mapping\\Embedded']);
|
2022-06-07 08:22:29 +00:00
|
|
|
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
|
2021-04-04 09:01:11 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-09-11 21:30:42 +00:00
|
|
|
$this->processDoctrineToMany($doctrineAnnotationTagValueNode, $node, $oldToNewClasses, $hasChanged);
|
2021-04-04 09:01:11 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param array<string, string> $oldToNewClasses
|
|
|
|
*/
|
2023-09-11 21:30:42 +00:00
|
|
|
private function processSerializerTypeTagValueNode(array $oldToNewClasses, PhpDocInfo $phpDocInfo, bool &$hasChanged) : void
|
2019-08-30 19:41:21 +00:00
|
|
|
{
|
2022-06-07 09:46:15 +00:00
|
|
|
$doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass('JMS\\Serializer\\Annotation\\Type');
|
2022-06-07 08:22:29 +00:00
|
|
|
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
|
2019-08-30 19:41:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-08-20 20:49:07 +00:00
|
|
|
$classNameArrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();
|
2019-08-30 19:41:21 +00:00
|
|
|
foreach ($oldToNewClasses as $oldClass => $newClass) {
|
2023-06-02 09:42:16 +00:00
|
|
|
if ($classNameArrayItemNode instanceof ArrayItemNode && $classNameArrayItemNode->value instanceof StringNode) {
|
|
|
|
$classNameStringNode = $classNameArrayItemNode->value;
|
|
|
|
if ($classNameStringNode->value === $oldClass) {
|
|
|
|
$classNameStringNode->value = $newClass;
|
2021-04-04 09:01:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
2023-06-02 09:42:16 +00:00
|
|
|
$classNameStringNode->value = Strings::replace($classNameStringNode->value, '#\\b' . \preg_quote($oldClass, '#') . '\\b#', $newClass);
|
2022-08-20 20:57:03 +00:00
|
|
|
$classNameArrayItemNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
|
2023-09-11 21:30:42 +00:00
|
|
|
$hasChanged = \true;
|
2019-08-30 19:41:21 +00:00
|
|
|
}
|
2022-08-20 20:49:07 +00:00
|
|
|
$currentTypeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
|
|
|
|
if (!$currentTypeArrayItemNode instanceof ArrayItemNode) {
|
2021-04-04 09:01:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
2023-06-02 09:42:16 +00:00
|
|
|
$currentTypeStringNode = $currentTypeArrayItemNode->value;
|
|
|
|
if (!$currentTypeStringNode instanceof StringNode) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ($currentTypeStringNode->value === $oldClass) {
|
|
|
|
$currentTypeStringNode->value = $newClass;
|
2023-09-11 21:30:42 +00:00
|
|
|
$hasChanged = \true;
|
2022-08-20 20:49:07 +00:00
|
|
|
}
|
2021-04-04 09:01:11 +00:00
|
|
|
}
|
2019-08-30 19:41:21 +00:00
|
|
|
}
|
|
|
|
/**
|
2021-04-04 09:01:11 +00:00
|
|
|
* @param array<string, string> $oldToNewClasses
|
2019-08-30 19:41:21 +00:00
|
|
|
*/
|
2023-09-11 21:30:42 +00:00
|
|
|
private function processDoctrineToMany(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, Node $node, array $oldToNewClasses, bool &$hasChanged) : void
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2022-06-07 09:46:15 +00:00
|
|
|
$classKey = $doctrineAnnotationTagValueNode->hasClassName('Doctrine\\ORM\\Mapping\\Embedded') ? 'class' : 'targetEntity';
|
2022-08-20 20:49:07 +00:00
|
|
|
$targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue($classKey);
|
|
|
|
if (!$targetEntityArrayItemNode instanceof ArrayItemNode) {
|
2019-08-30 19:41:21 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-06-02 09:42:16 +00:00
|
|
|
$targetEntityStringNode = $targetEntityArrayItemNode->value;
|
|
|
|
if (!$targetEntityStringNode instanceof StringNode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$targetEntityClass = $targetEntityStringNode->value;
|
2021-04-04 09:01:11 +00:00
|
|
|
// resolve to FQN
|
2022-08-20 20:49:07 +00:00
|
|
|
$tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntityClass, $node);
|
2019-08-30 19:41:21 +00:00
|
|
|
foreach ($oldToNewClasses as $oldClass => $newClass) {
|
2021-04-04 09:01:11 +00:00
|
|
|
if ($tagFullyQualifiedName !== $oldClass) {
|
|
|
|
continue;
|
2021-01-19 23:29:52 +00:00
|
|
|
}
|
2023-06-02 09:42:16 +00:00
|
|
|
$targetEntityStringNode->value = $newClass;
|
2022-08-20 20:57:03 +00:00
|
|
|
$targetEntityArrayItemNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
|
2023-09-11 21:30:42 +00:00
|
|
|
$hasChanged = \true;
|
2019-08-30 19:41:21 +00:00
|
|
|
}
|
2019-08-30 18:21:21 +00:00
|
|
|
}
|
|
|
|
}
|