Updated Rector to commit a33b200e615b9451ba091e2724ab707f39198da7

a33b200e61 [BetterPhpDocParser] Add ArrayItemNode to improve value transfer in annotation curly lists (#2786)
This commit is contained in:
Tomas Votruba 2022-08-20 20:49:07 +00:00
parent a6435672ab
commit 402dbe5a36
59 changed files with 830 additions and 474 deletions

View File

@ -11,30 +11,50 @@ final class ArrayItemNode implements PhpDocTagValueNode
{
use NodeAttributes;
/**
* @readonly
* @var mixed
*/
private $value;
public $value;
/**
* @var mixed
*/
public $key;
/**
* @var String_::KIND_*|null
* @readonly
*/
private $kindValueQuoted = null;
public $kindValueQuoted = null;
/**
* @var int|null
*/
public $kindKeyQuoted = null;
/**
* @param String_::KIND_*|null $kindValueQuoted
* @param mixed $value
* @param mixed $key
*/
public function __construct($value, ?int $kindValueQuoted = null)
public function __construct($value, $key, ?int $kindValueQuoted = null, ?int $kindKeyQuoted = null)
{
$this->value = $value;
$this->key = $key;
$this->kindValueQuoted = $kindValueQuoted;
$this->kindKeyQuoted = $kindKeyQuoted;
}
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;
}

View File

@ -14,7 +14,7 @@ final class DoctrineAnnotationTagValueNode extends AbstractValuesAwareNode
*/
public $identifierTypeNode;
/**
* @param array<mixed, mixed> $values
* @param ArrayItemNode[] $values
*/
public function __construct(IdentifierTypeNode $identifierTypeNode, ?string $originalContent = null, array $values = [], ?string $silentKey = null)
{

View File

@ -5,6 +5,7 @@ namespace Rector\BetterPhpDocParser\PhpDocManipulator;
use RectorPrefix202208\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;
@ -41,17 +42,24 @@ final class PhpDocClassRenamer
if (!$assertChoiceTagValueNode instanceof DoctrineAnnotationTagValueNode) {
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);
$assertChoiceTagValueNode->changeValue('callback', $callback);
$classNameArrayItemNode->value = $newClass;
// trigger reprint
$classNameArrayItemNode->setAttribute('orig_node', null);
break;
}
}
@ -75,24 +83,22 @@ final class PhpDocClassRenamer
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return;
}
$classNameArrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();
foreach ($oldToNewClasses as $oldClass => $newClass) {
$className = $doctrineAnnotationTagValueNode->getSilentValue();
if (\is_string($className)) {
if ($className === $oldClass) {
$doctrineAnnotationTagValueNode->changeSilentValue($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;
}
$doctrineAnnotationTagValueNode->changeSilentValue($newContent);
$classNameArrayItemNode->value = Strings::replace($classNameArrayItemNode->value, '#\\b' . \preg_quote($oldClass, '#') . '\\b#', $newClass);
$classNameArrayItemNode->setAttribute('orig_node', null);
}
$currentTypeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
if (!$currentTypeArrayItemNode instanceof ArrayItemNode) {
continue;
}
$currentType = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type');
if ($currentType === $oldClass) {
$doctrineAnnotationTagValueNode->changeValue('type', $newClass);
continue;
if ($currentTypeArrayItemNode->value === $oldClass) {
$currentTypeArrayItemNode->value = $newClass;
}
}
}
@ -102,17 +108,19 @@ final class PhpDocClassRenamer
private function processDoctrineToMany(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, Node $node, array $oldToNewClasses) : void
{
$classKey = $doctrineAnnotationTagValueNode->hasClassName('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);
}
}
}

View File

@ -5,6 +5,7 @@ namespace Rector\BetterPhpDocParser\PhpDocParser;
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;
@ -33,7 +34,8 @@ final class StaticDoctrineAnnotationParser
}
/**
* 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
{
@ -73,7 +75,8 @@ final class StaticDoctrineAnnotationParser
}
/**
* @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
{
@ -100,7 +103,7 @@ final class StaticDoctrineAnnotationParser
$values[] = $nestedValues;
}
}
return $values;
return $this->arrayParser->createArrayFromValues($values);
}
/**
* @return CurlyListNode|string|array<mixed>|ConstExprNode|DoctrineAnnotationTagValueNode

View File

@ -3,8 +3,10 @@
declare (strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser;
use PhpParser\Node\Scalar\String_;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\ValueObject\Parser\BetterTokenIterator;
/**
* @see \Rector\Tests\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParserTest
@ -22,7 +24,8 @@ final class ArrayParser
}
/**
* Mimics https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1305-L1352
* @return mixed[]
*
* @return ArrayItemNode[]
*/
public function parseCurlyArray(BetterTokenIterator $tokenIterator) : array
{
@ -61,6 +64,29 @@ final class ArrayParser
}
return $this->createArrayFromValues($values);
}
/**
* @param mixed[] $values
* @return ArrayItemNode[]
*/
public function createArrayFromValues(array $values) : array
{
$arrayItemNodes = [];
$naturalKey = 0;
foreach ($values as $key => $value) {
if (\is_array($value)) {
[$nestedKey, $nestedValue] = $value;
if ($nestedKey instanceof ConstExprIntegerNode) {
$nestedKey = $nestedKey->value;
}
// curly candidate?
$arrayItemNodes[] = $this->createArrayItemFromKeyAndValue($nestedKey, $nestedValue);
} else {
$arrayItemNodes[] = $this->createArrayItemFromKeyAndValue($key !== $naturalKey ? $key : null, $value);
}
++$naturalKey;
}
return $arrayItemNodes;
}
/**
* Mimics https://github.com/doctrine/annotations/blob/c66f06b7c83e9a2a7523351a9d5a4b55f885e574/lib/Doctrine/Common/Annotations/DocParser.php#L1354-L1385
* @return array<null|mixed, mixed>
@ -103,23 +129,51 @@ final class ArrayParser
return [$key, $this->plainValueParser->parseValue($tokenIterator)];
}
/**
* @param mixed[] $values
* @return mixed[]
* @return String_::KIND_SINGLE_QUOTED|String_::KIND_DOUBLE_QUOTED|null
* @param mixed $val
*/
private function createArrayFromValues(array $values) : array
private function resolveQuoteKind($val) : ?int
{
$array = [];
foreach ($values as $value) {
[$key, $val] = $value;
if ($key instanceof ConstExprIntegerNode) {
$key = $key->value;
}
if ($key !== null) {
$array[$key] = $val;
} else {
$array[] = $val;
}
if ($this->isQuotedWith($val, '"')) {
return String_::KIND_DOUBLE_QUOTED;
}
return $array;
if ($this->isQuotedWith($val, "'")) {
return String_::KIND_SINGLE_QUOTED;
}
return null;
}
/**
* @param mixed $key
* @param mixed $value
*/
private function createArrayItemFromKeyAndValue($key, $value) : ArrayItemNode
{
$valueQuoteKind = $this->resolveQuoteKind($value);
if (\is_string($value) && $valueQuoteKind === String_::KIND_DOUBLE_QUOTED) {
// give raw value
$value = \trim($value, '"');
}
$keyQuoteKind = $this->resolveQuoteKind($key);
if (\is_string($key) && $keyQuoteKind === String_::KIND_DOUBLE_QUOTED) {
// give raw value
$key = \trim($key, '"');
}
if ($key !== null) {
return new ArrayItemNode($value, $key, $valueQuoteKind, $keyQuoteKind);
}
return new ArrayItemNode($value, null, $valueQuoteKind, $keyQuoteKind);
}
/**
* @param mixed $value
*/
private function isQuotedWith($value, string $quotes) : bool
{
if (!\is_string($value)) {
return \false;
}
if (\strncmp($value, $quotes, \strlen($quotes)) !== 0) {
return \false;
}
return \substr_compare($value, $quotes, -\strlen($quotes)) === 0;
}
}

View File

@ -14,6 +14,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocNodeVisitor\ChangedPhpDocNodeVisitor;
@ -194,8 +195,8 @@ final class PhpDocInfoPrinter
return $this->standardPrintPhpDocChildNode($phpDocChildNode);
}
if ($phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode && $shouldReprintChildNode) {
$silentValue = $phpDocChildNode->value->getSilentValue();
if ($silentValue === null) {
$silentValueArrayItemNode = $phpDocChildNode->value->getSilentValue();
if (!$silentValueArrayItemNode instanceof ArrayItemNode) {
$printedNode = (string) $phpDocChildNode;
// remove extra space between tags
$printedNode = Strings::replace($printedNode, self::TAG_AND_SPACE_REGEX, '$1(');

View File

@ -3,26 +3,20 @@
declare (strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation;
use RectorPrefix202208\Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\NodeAttributes;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Core\Util\StringUtils;
abstract class AbstractValuesAwareNode implements PhpDocTagValueNode
{
use NodeAttributes;
/**
* @var string
* @see https://regex101.com/r/H6JjOG/3
*/
private const UNQUOTED_VALUE_REGEX = '#^("|\')(?<content>.*?)("|\')$#';
/**
* @var bool
*/
protected $hasChanged = \false;
/**
* @var array<(string | int), mixed>
* @var ArrayItemNode[]
*/
public $values = [];
/**
@ -34,7 +28,7 @@ abstract class AbstractValuesAwareNode implements PhpDocTagValueNode
*/
protected $silentKey;
/**
* @param array<string|int, mixed> $values Must be public so node traverser can go through them
* @param ArrayItemNode[] $values Must be public so node traverser can go through them
*/
public function __construct(array $values = [], ?string $originalContent = null, ?string $silentKey = null)
{
@ -42,144 +36,68 @@ abstract class AbstractValuesAwareNode implements PhpDocTagValueNode
$this->originalContent = $originalContent;
$this->silentKey = $silentKey;
}
public function removeValue(string $key) : void
public function removeValue(string $desiredKey) : void
{
$quotedKey = '"' . $key . '"';
// isset?
if (!isset($this->values[$key]) && !isset($this->values[$quotedKey])) {
return;
foreach ($this->values as $key => $value) {
if ($value->key !== $desiredKey) {
continue;
}
unset($this->values[$key]);
// invoke reprint
$this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
}
unset($this->values[$key]);
unset($this->values[$quotedKey]);
// invoke reprint
$this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
}
/**
* @return mixed[]
* @return ArrayItemNode[]
*/
public function getValues() : array
{
return $this->values;
}
/**
* @return mixed|Node|null
* @param string|int $key
* @return ArrayItemNode[]
*/
public function getValue($key)
public function getValuesWithSilentKey() : array
{
// to allow false as default
if (!\array_key_exists($key, $this->values)) {
return null;
if ($this->silentKey === null) {
return $this->values;
}
return $this->values[$key];
}
/**
* @param mixed $value
*/
public function changeValue(string $key, $value) : void
{
// is quoted?
if (isset($this->values[$key]) && \is_string($this->values[$key]) && StringUtils::isMatch($this->values[$key], self::UNQUOTED_VALUE_REGEX)) {
$value = '"' . $value . '"';
}
$this->values[$key] = $value;
// invoke reprint
$this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
}
/**
* @return mixed|null
* @param string|int $key
*/
public function getValueWithoutQuotes($key)
{
$value = $this->getValue($key);
if ($value === null) {
return null;
}
return $this->removeQuotes($value);
}
/**
* @param mixed $value
*/
public function changeSilentValue($value) : void
{
// is quoted?
if (StringUtils::isMatch($this->values[0], self::UNQUOTED_VALUE_REGEX)) {
$value = '"' . $value . '"';
}
$this->values[0] = $value;
$this->hasChanged = \true;
// invoke reprint
$this->setAttribute(PhpDocAttributeKey::ORIG_NODE, null);
}
/**
* @return mixed|null
*/
public function getSilentValue()
{
$value = $this->values[0] ?? null;
if ($value === null) {
return null;
}
return $this->removeQuotes($value);
}
/**
* Useful for attributes
* @return array<int|string, mixed>
*/
public function getValuesWithExplicitSilentAndWithoutQuotes() : array
{
$explicitKeysValues = [];
foreach (\array_keys($this->values) as $key) {
$valueWithoutQuotes = $this->getValueWithoutQuotes($key);
if (\is_int($key) && $this->silentKey !== null) {
$explicitKeysValues[$this->silentKey] = $valueWithoutQuotes;
} else {
// to keep constant keys strings if quoted
if (\is_string($key) && \strpos($key, '::') === \false) {
$key = $this->removeQuotes($key);
}
$explicitKeysValues[$key] = $valueWithoutQuotes;
// to keep original values untouched, unless not changed
$silentKeyAwareValues = $this->values;
foreach ($silentKeyAwareValues as $silentKeyAwareValue) {
if ($silentKeyAwareValue->key === null) {
$silentKeyAwareValue->key = $this->silentKey;
break;
}
}
return $explicitKeysValues;
return $silentKeyAwareValues;
}
/**
* @return mixed|Node|null|ArrayItemNode
* @param string|int $desiredKey
*/
public function getValue($desiredKey)
{
foreach ($this->values as $value) {
if ($value->key === $desiredKey) {
return $value;
}
}
return null;
}
public function getSilentValue() : ?ArrayItemNode
{
foreach ($this->values as $value) {
if ($value->key === null) {
return $value;
}
}
return null;
}
public function markAsChanged() : void
{
$this->hasChanged = \true;
}
/**
* @param mixed|string $value
* @return mixed|string
*/
protected function removeQuotes($value)
{
if (\is_array($value)) {
return $this->removeQuotesFromArray($value);
}
if (!\is_string($value)) {
return $value;
}
$matches = Strings::match($value, self::UNQUOTED_VALUE_REGEX);
if ($matches === null) {
return $value;
}
return $matches['content'];
}
/**
* @param mixed[] $values
* @return array<int|string, mixed>
*/
protected function removeQuotesFromArray(array $values) : array
{
$unquotedArray = [];
foreach ($values as $key => $value) {
$unquotedKey = $this->removeQuotes($key);
$unquotedValue = $this->removeQuotes($value);
$unquotedArray[$unquotedKey] = $unquotedValue;
}
return $unquotedArray;
}
/**
* @param mixed[] $values
*/

View File

@ -3,29 +3,30 @@
declare (strict_types=1);
namespace Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Stringable;
use RectorPrefix202208\Webmozart\Assert\Assert;
final class CurlyListNode extends \Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\AbstractValuesAwareNode
{
/**
* @var ArrayItemNode[]
* @readonly
*/
private $arrayItemNodes = [];
/**
* @param ArrayItemNode[] $arrayItemNodes
*/
public function __construct(array $arrayItemNodes = [])
{
$this->arrayItemNodes = $arrayItemNodes;
Assert::allIsInstanceOf($this->arrayItemNodes, ArrayItemNode::class);
parent::__construct($this->arrayItemNodes);
}
public function __toString() : string
{
// possibly list items
return $this->implode($this->values);
}
/**
* @param mixed $value
*/
private function stringifyValue($value) : string
{
if ($value === \false) {
return 'false';
}
if ($value === \true) {
return 'true';
}
if (\is_array($value)) {
return $this->implode($value);
}
return (string) $value;
}
/**
* @param mixed[] $array
*/
@ -36,9 +37,9 @@ final class CurlyListNode extends \Rector\BetterPhpDocParser\ValueObject\PhpDoc\
$lastItemKey = \key($array);
foreach ($array as $key => $value) {
if (\is_int($key)) {
$itemContents .= $this->stringifyValue($value);
$itemContents .= (string) $value;
} else {
$itemContents .= $key . '=' . $this->stringifyValue($value);
$itemContents .= $key . '=' . $value;
}
if ($lastItemKey !== $key) {
$itemContents .= ', ';

View File

@ -5,6 +5,7 @@ namespace Rector\PhpAttribute;
use PhpParser\BuilderHelpers;
use PhpParser\Node\Expr;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\PhpAttribute\Contract\AnnotationToAttributeMapperInterface;
use Rector\PhpAttribute\Enum\DocTagNodeState;
@ -26,7 +27,7 @@ final class AnnotationToAttributeMapper
$this->annotationToAttributeMappers = $annotationToAttributeMappers;
}
/**
* @return \PhpParser\Node\Expr|string
* @return Expr|DocTagNodeState::REMOVE_ARRAY
* @param mixed $value
*/
public function map($value)
@ -43,6 +44,9 @@ final class AnnotationToAttributeMapper
if ($value instanceof DoctrineAnnotationTagValueNode) {
return DocTagNodeState::REMOVE_ARRAY;
}
if ($value instanceof ArrayItemNode) {
return BuilderHelpers::normalizeValue((string) $value);
}
// fallback
return BuilderHelpers::normalizeValue($value);
}

View File

@ -47,17 +47,20 @@ final class ArrayAnnotationToAttributeMapper implements AnnotationToAttributeMap
if ($valueExpr === DocTagNodeState::REMOVE_ARRAY) {
continue;
}
Assert::isInstanceOf($valueExpr, Expr::class);
// remove value
if ($this->isRemoveArrayPlaceholder($singleValue)) {
continue;
}
$keyExpr = null;
if (!\is_int($key)) {
$keyExpr = $this->annotationToAttributeMapper->map($key);
Assert::isInstanceOf($keyExpr, Expr::class);
if ($valueExpr instanceof ArrayItem) {
$arrayItems[] = $valueExpr;
} else {
$keyExpr = null;
if (!\is_int($key)) {
$keyExpr = $this->annotationToAttributeMapper->map($key);
Assert::isInstanceOf($keyExpr, Expr::class);
}
$arrayItems[] = new ArrayItem($valueExpr, $keyExpr);
}
$arrayItems[] = new ArrayItem($valueExpr, $keyExpr);
}
return new Array_($arrayItems);
}

View File

@ -0,0 +1,63 @@
<?php
declare (strict_types=1);
namespace Rector\PhpAttribute\AnnotationToAttributeMapper;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Scalar\String_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
use Rector\PhpAttribute\Contract\AnnotationToAttributeMapperInterface;
use RectorPrefix202208\Symfony\Contracts\Service\Attribute\Required;
/**
* @implements AnnotationToAttributeMapperInterface<ArrayItemNode>
*/
final class ArrayItemNodeAnnotationToAttributeMapper implements AnnotationToAttributeMapperInterface
{
/**
* @var \Rector\PhpAttribute\AnnotationToAttributeMapper
*/
private $annotationToAttributeMapper;
/**
* Avoid circular reference
* @required
*/
public function autowire(AnnotationToAttributeMapper $annotationToAttributeMapper) : void
{
$this->annotationToAttributeMapper = $annotationToAttributeMapper;
}
/**
* @param mixed $value
*/
public function isCandidate($value) : bool
{
return $value instanceof ArrayItemNode;
}
/**
* @param ArrayItemNode $arrayItemNode
*/
public function map($arrayItemNode) : Expr
{
$valueExpr = $this->annotationToAttributeMapper->map($arrayItemNode->value);
if ($arrayItemNode->key !== null) {
switch ($arrayItemNode->kindKeyQuoted) {
case String_::KIND_SINGLE_QUOTED:
$keyValue = "'" . $arrayItemNode->key . "'";
break;
case String_::KIND_DOUBLE_QUOTED:
$keyValue = '"' . $arrayItemNode->key . '"';
break;
default:
$keyValue = $arrayItemNode->key;
break;
}
/** @var Expr $keyExpr */
$keyExpr = $this->annotationToAttributeMapper->map($keyValue);
} else {
$keyExpr = null;
}
// @todo how to skip natural integer keys?
return new ArrayItem($valueExpr, $keyExpr);
}
}

View File

@ -43,7 +43,7 @@ final class CurlyListNodeAnnotationToAttributeMapper implements AnnotationToAttr
public function map($value) : \PhpParser\Node\Expr
{
$arrayItems = [];
$valuesWithExplicitSilent = $value->getValuesWithExplicitSilentAndWithoutQuotes();
$valuesWithExplicitSilent = $value->getValues();
$loop = -1;
foreach ($valuesWithExplicitSilent as $key => $singleValue) {
$valueExpr = $this->annotationToAttributeMapper->map($singleValue);
@ -51,7 +51,6 @@ final class CurlyListNodeAnnotationToAttributeMapper implements AnnotationToAttr
if ($valueExpr === DocTagNodeState::REMOVE_ARRAY) {
continue;
}
Assert::isInstanceOf($valueExpr, Expr::class);
if (!\is_int($key)) {
$keyExpr = $this->annotationToAttributeMapper->map($key);
Assert::isInstanceOf($keyExpr, Expr::class);

View File

@ -64,7 +64,7 @@ final class DoctrineAnnotationAnnotationToAttributeMapper implements AnnotationT
$annotationShortName = $this->resolveAnnotationName($value);
$values = $value->getValues();
if ($values !== []) {
$argValues = $this->annotationToAttributeMapper->map($value->getValuesWithExplicitSilentAndWithoutQuotes());
$argValues = $this->annotationToAttributeMapper->map($value->getValues());
if ($argValues instanceof Array_) {
// create named args
$args = $this->attributeArrayNameInliner->inlineArrayToArgs($argValues);

View File

@ -7,6 +7,7 @@ use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Identifier;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use Rector\Core\Exception\NotImplementedYetException;
use RectorPrefix202208\Webmozart\Assert\Assert;
@ -55,7 +56,9 @@ final class AttributeArrayNameInliner
// matching top root array key
if ($arg->value instanceof ArrayItem) {
$arrayItem = $arg->value;
if ($arrayItem->key instanceof String_) {
if ($arrayItem->key instanceof LNumber) {
$newArgs[] = new Arg($arrayItem->value);
} elseif ($arrayItem->key instanceof String_) {
$arrayItemString = $arrayItem->key;
$newArgs[] = new Arg($arrayItem->value, \false, \false, [], new Identifier($arrayItemString->value));
} elseif ($arrayItem->key === null) {

View File

@ -9,6 +9,7 @@ use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Use_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\Php80\ValueObject\AnnotationToAttribute;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
@ -77,7 +78,7 @@ final class PhpAttributeGroupFactory
*/
public function create(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, AnnotationToAttribute $annotationToAttribute, array $uses) : AttributeGroup
{
$values = $doctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes();
$values = $doctrineAnnotationTagValueNode->getValuesWithSilentKey();
$args = $this->createArgsFromItems($values, $annotationToAttribute->getAttributeClass());
$args = $this->attributeArrayNameInliner->inlineArrayToArgs($args);
$attributeName = $this->attributeNameFactory->create($annotationToAttribute, $doctrineAnnotationTagValueNode, $uses);
@ -85,14 +86,15 @@ final class PhpAttributeGroupFactory
return new AttributeGroup([$attribute]);
}
/**
* @param mixed[] $items
* @param ArrayItemNode[] $items
* @return Arg[]
*/
public function createArgsFromItems(array $items, string $attributeClass) : array
{
/** @var Expr[]|Expr\Array_ $items */
$items = $this->annotationToAttributeMapper->map($items);
$items = $this->exprParameterReflectionTypeCorrector->correctItemsByAttributeClass($items, $attributeClass);
return $this->namedArgsFactory->createFromValues($items);
/** @var Expr[]|Expr\Array_ $mappedItems */
$mappedItems = $this->annotationToAttributeMapper->map($items);
$mappedItems = $this->exprParameterReflectionTypeCorrector->correctItemsByAttributeClass($mappedItems, $attributeClass);
// the key here should contain the named argument
return $this->namedArgsFactory->createFromValues($mappedItems);
}
}

View File

@ -11,8 +11,10 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Use_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Php80\ValueObject\NestedAnnotationToAttribute;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
use Rector\PhpAttribute\AttributeArrayNameInliner;
@ -62,7 +64,8 @@ final class PhpNestedAttributeGroupFactory
*/
public function create(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, NestedAnnotationToAttribute $nestedAnnotationToAttribute, array $uses) : AttributeGroup
{
$values = $doctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes();
$values = $doctrineAnnotationTagValueNode->getValues();
$values = $this->removeItems($values, $nestedAnnotationToAttribute);
$args = $this->createArgsFromItems($values, $nestedAnnotationToAttribute);
$args = $this->attributeArrayNameInliner->inlineArrayToArgs($args);
$attributeName = $this->attributeNameFactory->create($nestedAnnotationToAttribute, $doctrineAnnotationTagValueNode, $uses);
@ -75,40 +78,46 @@ final class PhpNestedAttributeGroupFactory
public function createNested(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, NestedAnnotationToAttribute $nestedAnnotationToAttribute) : array
{
$attributeGroups = [];
$values = $doctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes();
foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $itemName => $nestedAttributeClass) {
$nestedValues = $values[$itemName] ?? null;
if ($nestedValues === null) {
if ($nestedAnnotationToAttribute->hasExplicitParameters()) {
return $this->createFromExplicitProperties($nestedAnnotationToAttribute, $doctrineAnnotationTagValueNode);
}
$nestedAttributeClass = $nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()[0];
foreach ($doctrineAnnotationTagValueNode->values as $arrayItemNode) {
$nestedDoctrineAnnotationTagValueNode = $arrayItemNode->value;
if (!$nestedDoctrineAnnotationTagValueNode instanceof CurlyListNode) {
continue;
}
if ($nestedValues instanceof CurlyListNode) {
foreach ($nestedValues->getValues() as $nestedDoctrineAnnotationTagValueNode) {
/** @var DoctrineAnnotationTagValueNode $nestedDoctrineAnnotationTagValueNode */
$args = $this->createArgsFromItems($nestedDoctrineAnnotationTagValueNode->getValuesWithExplicitSilentAndWithoutQuotes(), $nestedAnnotationToAttribute);
$args = $this->attributeArrayNameInliner->inlineArrayToArgs($args);
$originalIdentifier = $nestedDoctrineAnnotationTagValueNode->identifierTypeNode->name;
$attributeName = $this->resolveAliasedAttributeName($originalIdentifier, $nestedAttributeClass);
$attribute = new Attribute($attributeName, $args);
$attributeGroups[] = new AttributeGroup([$attribute]);
foreach ($nestedDoctrineAnnotationTagValueNode->values as $nestedArrayItemNode) {
if (!$nestedArrayItemNode->value instanceof DoctrineAnnotationTagValueNode) {
continue;
}
$attributeArgs = $this->createAttributeArgs($nestedArrayItemNode->value, $nestedAnnotationToAttribute);
$originalIdentifier = $doctrineAnnotationTagValueNode->identifierTypeNode->name;
$attributeName = $this->resolveAliasedAttributeName($originalIdentifier, $nestedAttributeClass);
$attribute = new Attribute($attributeName, $attributeArgs);
$attributeGroups[] = new AttributeGroup([$attribute]);
}
}
return $attributeGroups;
}
/**
* @param mixed[] $items
* @return Arg[]
*/
private function createArgsFromItems(array $items, NestedAnnotationToAttribute $nestedAnnotationToAttribute) : array
public function createAttributeArgs(DoctrineAnnotationTagValueNode $nestedDoctrineAnnotationTagValueNode, NestedAnnotationToAttribute $nestedAnnotationToAttribute) : array
{
// remove nested items
foreach (\array_keys($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()) as $itemName) {
unset($items[$itemName]);
}
/** @var Expr[]|Expr\Array_ $items */
$items = $this->annotationToAttributeMapper->map($items);
$items = $this->exprParameterReflectionTypeCorrector->correctItemsByAttributeClass($items, $nestedAnnotationToAttribute->getTag());
return $this->namedArgsFactory->createFromValues($items);
$args = $this->createArgsFromItems($nestedDoctrineAnnotationTagValueNode->getValues(), $nestedAnnotationToAttribute);
return $this->attributeArrayNameInliner->inlineArrayToArgs($args);
}
/**
* @param ArrayItemNode[] $arrayItemNodes
* @return Arg[]
*/
private function createArgsFromItems(array $arrayItemNodes, NestedAnnotationToAttribute $nestedAnnotationToAttribute) : array
{
/** @var Expr[]|Expr\Array_ $arrayItemNodes */
$arrayItemNodes = $this->annotationToAttributeMapper->map($arrayItemNodes);
$arrayItemNodes = $this->exprParameterReflectionTypeCorrector->correctItemsByAttributeClass($arrayItemNodes, $nestedAnnotationToAttribute->getTag());
return $this->namedArgsFactory->createFromValues($arrayItemNodes);
}
/**
* @todo improve this hardcoded approach later
@ -129,4 +138,48 @@ final class PhpNestedAttributeGroupFactory
}
return new FullyQualified($nestedAttributeClass);
}
/**
* @param ArrayItemNode[] $arrayItemNodes
* @return ArrayItemNode[]
*/
private function removeItems(array $arrayItemNodes, NestedAnnotationToAttribute $nestedAnnotationToAttribute) : array
{
foreach (\array_keys($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses()) as $itemToRemoveName) {
foreach ($arrayItemNodes as $key => $arrayItemNode) {
if ($arrayItemNode->key !== $itemToRemoveName) {
continue;
}
unset($arrayItemNodes[$key]);
}
}
return $arrayItemNodes;
}
/**
* @return AttributeGroup[]
*/
private function createFromExplicitProperties(NestedAnnotationToAttribute $nestedAnnotationToAttribute, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : array
{
$attributeGroups = [];
foreach ($nestedAnnotationToAttribute->getAnnotationPropertiesToAttributeClasses() as $itemName => $nestedAttributeClass) {
$nestedArrayItemNode = $doctrineAnnotationTagValueNode->getValue($itemName);
if (!$nestedArrayItemNode instanceof ArrayItemNode) {
continue;
}
if (!$nestedArrayItemNode->value instanceof CurlyListNode) {
throw new ShouldNotHappenException();
}
foreach ($nestedArrayItemNode->value->getValues() as $arrayItemNode) {
$nestedDoctrineAnnotationTagValueNode = $arrayItemNode->value;
if (!$nestedDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
throw new ShouldNotHappenException();
}
$attributeArgs = $this->createAttributeArgs($nestedDoctrineAnnotationTagValueNode, $nestedAnnotationToAttribute);
$originalIdentifier = $nestedDoctrineAnnotationTagValueNode->identifierTypeNode->name;
$attributeName = $this->resolveAliasedAttributeName($originalIdentifier, $nestedAttributeClass);
$attribute = new Attribute($attributeName, $attributeArgs);
$attributeGroups[] = new AttributeGroup([$attribute]);
}
}
return $attributeGroups;
}
}

View File

@ -4,6 +4,7 @@ declare (strict_types=1);
namespace Rector\Php80\NodeAnalyzer;
use PhpParser\Node\Expr\ClassConstFetch;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\Core\PhpParser\Node\NodeFactory;
final class AnnotationTargetResolver
{
@ -30,17 +31,19 @@ final class AnnotationTargetResolver
$this->nodeFactory = $nodeFactory;
}
/**
* @param array<int|string, mixed> $targetValues
* @param ArrayItemNode[] $targetValues
* @return ClassConstFetch[]
*/
public function resolveFlagClassConstFetches(array $targetValues) : array
{
$classConstFetches = [];
foreach (self::TARGET_TO_CONSTANT_MAP as $target => $constant) {
if (!\in_array($target, $targetValues, \true)) {
continue;
foreach ($targetValues as $targetValue) {
foreach (self::TARGET_TO_CONSTANT_MAP as $target => $constant) {
if ($target !== $targetValue->value) {
continue;
}
$classConstFetches[] = $this->nodeFactory->createClassConstFetch('Attribute', $constant);
}
$classConstFetches[] = $this->nodeFactory->createClassConstFetch('Attribute', $constant);
}
return $classConstFetches;
}

View File

@ -12,6 +12,7 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Use_;
use PHPStan\PhpDocParser\Ast\Node as DocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
@ -27,7 +28,6 @@ use Rector\Php80\NodeManipulator\AttributeGroupNamedArgumentManipulator;
use Rector\Php80\ValueObject\AnnotationToAttribute;
use Rector\Php80\ValueObject\DoctrineTagAndAnnotationToAttribute;
use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory;
//use Rector\PhpAttribute\UnwrapableAnnotationAnalyzer;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use RectorPrefix202208\Symplify\Astral\PhpDocParser\PhpDocNodeTraverser;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
@ -122,7 +122,7 @@ CODE_SAMPLE
return null;
}
$uses = $this->useImportsResolver->resolveBareUsesForNode($node);
// 1. generic tags
// 1. bare tags without annotation class, e.g. "@inject"
$genericAttributeGroups = $this->processGenericTags($phpDocInfo);
// 2. Doctrine annotation classes
$annotationAttributeGroups = $this->processDoctrineAnnotationClasses($phpDocInfo, $uses);
@ -179,7 +179,7 @@ CODE_SAMPLE
return $attributeGroups;
}
/**
* @param Node\Stmt\Use_[] $uses
* @param Use_[] $uses
* @return AttributeGroup[]
*/
private function processDoctrineAnnotationClasses(PhpDocInfo $phpDocInfo, array $uses) : array

View File

@ -8,6 +8,7 @@ use PhpParser\Node\Arg;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\MixedType;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
@ -177,12 +178,8 @@ CODE_SAMPLE
if ($this->shouldRemoveAnnotations) {
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $targetDoctrineAnnotationTagValueNode);
}
$targets = $targetDoctrineAnnotationTagValueNode->getSilentValue();
if ($targets instanceof CurlyListNode) {
$targetValues = $targets->getValuesWithExplicitSilentAndWithoutQuotes();
} elseif (\is_string($targets)) {
$targetValues = [$targets];
} else {
$targetValues = $this->resolveTargetValues($targetDoctrineAnnotationTagValueNode);
if ($targetValues === []) {
return;
}
$flagClassConstFetches = $this->annotationTargetResolver->resolveFlagClassConstFetches($targetValues);
@ -200,4 +197,18 @@ CODE_SAMPLE
// has attribute? skip it
return $this->phpAttributeAnalyzer->hasPhpAttribute($class, AttributeName::ATTRIBUTE);
}
/**
* @return ArrayItemNode[]
*/
private function resolveTargetValues(DoctrineAnnotationTagValueNode $targetDoctrineAnnotationTagValueNode) : array
{
$silentTargetsArrayItemNode = $targetDoctrineAnnotationTagValueNode->getSilentValue();
if ($silentTargetsArrayItemNode instanceof ArrayItemNode) {
if ($silentTargetsArrayItemNode->value instanceof CurlyListNode) {
return $silentTargetsArrayItemNode->value->getValues();
}
return [$silentTargetsArrayItemNode];
}
return [];
}
}

View File

@ -5,6 +5,7 @@ namespace Rector\Php80\ValueObject;
use Rector\Core\Validation\RectorAssert;
use Rector\Php80\Contract\ValueObject\AnnotationToAttributeInterface;
use RectorPrefix202208\Webmozart\Assert\Assert;
final class NestedAnnotationToAttribute implements AnnotationToAttributeInterface
{
/**
@ -31,6 +32,7 @@ final class NestedAnnotationToAttribute implements AnnotationToAttributeInterfac
$this->annotationPropertiesToAttributeClasses = $annotationPropertiesToAttributeClasses;
$this->removeOriginal = $removeOriginal;
RectorAssert::className($tag);
Assert::allString($annotationPropertiesToAttributeClasses);
}
public function getTag() : string
{
@ -51,4 +53,13 @@ final class NestedAnnotationToAttribute implements AnnotationToAttributeInterfac
{
return $this->removeOriginal;
}
public function hasExplicitParameters() : bool
{
foreach (\array_keys($this->annotationPropertiesToAttributeClasses) as $itemName) {
if (\is_string($itemName)) {
return \true;
}
}
return \false;
}
}

View File

@ -17,12 +17,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '6f530a1deff42a44f008e7f35c70501fd2635bae';
public const PACKAGE_VERSION = 'a33b200e615b9451ba091e2724ab707f39198da7';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2022-08-20 20:54:46';
public const RELEASE_DATE = '2022-08-20 22:44:59';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit88ac621afd5b114466a1fd611b35d804::getLoader();
return ComposerAutoloaderInitbf3b0c550a047519dae38bd8e43006f2::getLoader();

View File

@ -1735,6 +1735,7 @@ return array(
'Rector\\Doctrine\\NodeFactory\\ParamFactory' => $vendorDir . '/rector/rector-doctrine/src/NodeFactory/ParamFactory.php',
'Rector\\Doctrine\\NodeFactory\\RepositoryAssignFactory' => $vendorDir . '/rector/rector-doctrine/src/NodeFactory/RepositoryAssignFactory.php',
'Rector\\Doctrine\\NodeFactory\\RepositoryNodeFactory' => $vendorDir . '/rector/rector-doctrine/src/NodeFactory/RepositoryNodeFactory.php',
'Rector\\Doctrine\\NodeFactory\\SluggableClassMethodFactory' => $vendorDir . '/rector/rector-doctrine/src/NodeFactory/SluggableClassMethodFactory.php',
'Rector\\Doctrine\\NodeFactory\\TranslationClassNodeFactory' => $vendorDir . '/rector/rector-doctrine/src/NodeFactory/TranslationClassNodeFactory.php',
'Rector\\Doctrine\\NodeFactory\\ValueAssignFactory' => $vendorDir . '/rector/rector-doctrine/src/NodeFactory/ValueAssignFactory.php',
'Rector\\Doctrine\\NodeManipulator\\ColumnPropertyTypeResolver' => $vendorDir . '/rector/rector-doctrine/src/NodeManipulator/ColumnPropertyTypeResolver.php',
@ -2424,6 +2425,7 @@ return array(
'Rector\\Php82\\Rector\\Class_\\ReadOnlyClassRector' => $baseDir . '/rules/Php82/Rector/Class_/ReadOnlyClassRector.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayItemNodeAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ClassConstFetchAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/ClassConstFetchAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ConstExprNodeAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/ConstExprNodeAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\CurlyListNodeAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/CurlyListNodeAnnotationToAttributeMapper.php',
@ -2638,7 +2640,6 @@ return array(
'Rector\\Symfony\\NodeFactory\\Annotations\\DoctrineAnnotationFromNewFactory' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/Annotations/DoctrineAnnotationFromNewFactory.php',
'Rector\\Symfony\\NodeFactory\\Annotations\\DoctrineAnnotationKeyToValuesResolver' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/Annotations/DoctrineAnnotationKeyToValuesResolver.php',
'Rector\\Symfony\\NodeFactory\\Annotations\\StringValueQuoteWrapper' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/Annotations/StringValueQuoteWrapper.php',
'Rector\\Symfony\\NodeFactory\\Annotations\\ValueQuoteWrapper' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/Annotations/ValueQuoteWrapper.php',
'Rector\\Symfony\\NodeFactory\\ArrayFromCompactFactory' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/ArrayFromCompactFactory.php',
'Rector\\Symfony\\NodeFactory\\BareLogoutClassMethodFactory' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/BareLogoutClassMethodFactory.php',
'Rector\\Symfony\\NodeFactory\\EventReferenceFactory' => $vendorDir . '/rector/rector-symfony/src/NodeFactory/EventReferenceFactory.php',
@ -2723,6 +2724,7 @@ return array(
'Rector\\Symfony\\Rector\\StaticCall\\BinaryFileResponseCreateToNewInstanceRector' => $vendorDir . '/rector/rector-symfony/src/Rector/StaticCall/BinaryFileResponseCreateToNewInstanceRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\ParseFileRector' => $vendorDir . '/rector/rector-symfony/src/Rector/StaticCall/ParseFileRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\ProcessBuilderInstanceRector' => $vendorDir . '/rector/rector-symfony/src/Rector/StaticCall/ProcessBuilderInstanceRector.php',
'Rector\\Symfony\\Rector\\StaticPropertyFetch\\KernelTestCaseContainerPropertyDeprecationRector' => $vendorDir . '/rector/rector-symfony/src/Rector/StaticPropertyFetch/KernelTestCaseContainerPropertyDeprecationRector.php',
'Rector\\Symfony\\Set\\JMSSetList' => $vendorDir . '/rector/rector-symfony/src/Set/JMSSetList.php',
'Rector\\Symfony\\Set\\SensiolabsSetList' => $vendorDir . '/rector/rector-symfony/src/Set/SensiolabsSetList.php',
'Rector\\Symfony\\Set\\SwiftmailerSetList' => $vendorDir . '/rector/rector-symfony/src/Set/SwiftmailerSetList.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit88ac621afd5b114466a1fd611b35d804
class ComposerAutoloaderInitbf3b0c550a047519dae38bd8e43006f2
{
private static $loader;
@ -22,19 +22,19 @@ class ComposerAutoloaderInit88ac621afd5b114466a1fd611b35d804
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit88ac621afd5b114466a1fd611b35d804', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitbf3b0c550a047519dae38bd8e43006f2', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit88ac621afd5b114466a1fd611b35d804', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitbf3b0c550a047519dae38bd8e43006f2', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit88ac621afd5b114466a1fd611b35d804::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitbf3b0c550a047519dae38bd8e43006f2::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit88ac621afd5b114466a1fd611b35d804::$files;
$includeFiles = \Composer\Autoload\ComposerStaticInitbf3b0c550a047519dae38bd8e43006f2::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire88ac621afd5b114466a1fd611b35d804($fileIdentifier, $file);
composerRequirebf3b0c550a047519dae38bd8e43006f2($fileIdentifier, $file);
}
return $loader;
@ -46,7 +46,7 @@ class ComposerAutoloaderInit88ac621afd5b114466a1fd611b35d804
* @param string $file
* @return void
*/
function composerRequire88ac621afd5b114466a1fd611b35d804($fileIdentifier, $file)
function composerRequirebf3b0c550a047519dae38bd8e43006f2($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInit88ac621afd5b114466a1fd611b35d804
class ComposerStaticInitbf3b0c550a047519dae38bd8e43006f2
{
public static $files = array (
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
@ -2032,6 +2032,7 @@ class ComposerStaticInit88ac621afd5b114466a1fd611b35d804
'Rector\\Doctrine\\NodeFactory\\ParamFactory' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeFactory/ParamFactory.php',
'Rector\\Doctrine\\NodeFactory\\RepositoryAssignFactory' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeFactory/RepositoryAssignFactory.php',
'Rector\\Doctrine\\NodeFactory\\RepositoryNodeFactory' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeFactory/RepositoryNodeFactory.php',
'Rector\\Doctrine\\NodeFactory\\SluggableClassMethodFactory' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeFactory/SluggableClassMethodFactory.php',
'Rector\\Doctrine\\NodeFactory\\TranslationClassNodeFactory' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeFactory/TranslationClassNodeFactory.php',
'Rector\\Doctrine\\NodeFactory\\ValueAssignFactory' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeFactory/ValueAssignFactory.php',
'Rector\\Doctrine\\NodeManipulator\\ColumnPropertyTypeResolver' => __DIR__ . '/..' . '/rector/rector-doctrine/src/NodeManipulator/ColumnPropertyTypeResolver.php',
@ -2721,6 +2722,7 @@ class ComposerStaticInit88ac621afd5b114466a1fd611b35d804
'Rector\\Php82\\Rector\\Class_\\ReadOnlyClassRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/Class_/ReadOnlyClassRector.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayItemNodeAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ClassConstFetchAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/ClassConstFetchAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ConstExprNodeAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/ConstExprNodeAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\CurlyListNodeAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/CurlyListNodeAnnotationToAttributeMapper.php',
@ -2935,7 +2937,6 @@ class ComposerStaticInit88ac621afd5b114466a1fd611b35d804
'Rector\\Symfony\\NodeFactory\\Annotations\\DoctrineAnnotationFromNewFactory' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/Annotations/DoctrineAnnotationFromNewFactory.php',
'Rector\\Symfony\\NodeFactory\\Annotations\\DoctrineAnnotationKeyToValuesResolver' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/Annotations/DoctrineAnnotationKeyToValuesResolver.php',
'Rector\\Symfony\\NodeFactory\\Annotations\\StringValueQuoteWrapper' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/Annotations/StringValueQuoteWrapper.php',
'Rector\\Symfony\\NodeFactory\\Annotations\\ValueQuoteWrapper' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/Annotations/ValueQuoteWrapper.php',
'Rector\\Symfony\\NodeFactory\\ArrayFromCompactFactory' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/ArrayFromCompactFactory.php',
'Rector\\Symfony\\NodeFactory\\BareLogoutClassMethodFactory' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/BareLogoutClassMethodFactory.php',
'Rector\\Symfony\\NodeFactory\\EventReferenceFactory' => __DIR__ . '/..' . '/rector/rector-symfony/src/NodeFactory/EventReferenceFactory.php',
@ -3020,6 +3021,7 @@ class ComposerStaticInit88ac621afd5b114466a1fd611b35d804
'Rector\\Symfony\\Rector\\StaticCall\\BinaryFileResponseCreateToNewInstanceRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/StaticCall/BinaryFileResponseCreateToNewInstanceRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\ParseFileRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/StaticCall/ParseFileRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\ProcessBuilderInstanceRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/StaticCall/ProcessBuilderInstanceRector.php',
'Rector\\Symfony\\Rector\\StaticPropertyFetch\\KernelTestCaseContainerPropertyDeprecationRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/StaticPropertyFetch/KernelTestCaseContainerPropertyDeprecationRector.php',
'Rector\\Symfony\\Set\\JMSSetList' => __DIR__ . '/..' . '/rector/rector-symfony/src/Set/JMSSetList.php',
'Rector\\Symfony\\Set\\SensiolabsSetList' => __DIR__ . '/..' . '/rector/rector-symfony/src/Set/SensiolabsSetList.php',
'Rector\\Symfony\\Set\\SwiftmailerSetList' => __DIR__ . '/..' . '/rector/rector-symfony/src/Set/SwiftmailerSetList.php',
@ -3254,9 +3256,9 @@ class ComposerStaticInit88ac621afd5b114466a1fd611b35d804
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit88ac621afd5b114466a1fd611b35d804::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit88ac621afd5b114466a1fd611b35d804::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit88ac621afd5b114466a1fd611b35d804::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitbf3b0c550a047519dae38bd8e43006f2::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitbf3b0c550a047519dae38bd8e43006f2::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitbf3b0c550a047519dae38bd8e43006f2::$classMap;
}, null, ClassLoader::class);
}

View File

@ -1852,12 +1852,12 @@
"source": {
"type": "git",
"url": "https:\/\/github.com\/rectorphp\/rector-doctrine.git",
"reference": "c63e2882d4d61abacdc760561cddcd6ee394d777"
"reference": "e1dba76992666c6fc9da35c3832ba56a6e1b28ec"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-doctrine\/zipball\/c63e2882d4d61abacdc760561cddcd6ee394d777",
"reference": "c63e2882d4d61abacdc760561cddcd6ee394d777",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-doctrine\/zipball\/e1dba76992666c6fc9da35c3832ba56a6e1b28ec",
"reference": "e1dba76992666c6fc9da35c3832ba56a6e1b28ec",
"shasum": ""
},
"require": {
@ -1882,7 +1882,7 @@
"symplify\/rule-doc-generator": "^11.0",
"symplify\/vendor-patches": "^11.0"
},
"time": "2022-08-18T19:37:47+00:00",
"time": "2022-08-20T20:00:45+00:00",
"default-branch": true,
"type": "rector-extension",
"extra": {
@ -2267,12 +2267,12 @@
"source": {
"type": "git",
"url": "https:\/\/github.com\/rectorphp\/rector-symfony.git",
"reference": "20ac2a40e499d7933596f7c9c56d6ac0ab9df95d"
"reference": "b689a4e48af872b4d0dcd3945a3ebf6262efdb5e"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-symfony\/zipball\/20ac2a40e499d7933596f7c9c56d6ac0ab9df95d",
"reference": "20ac2a40e499d7933596f7c9c56d6ac0ab9df95d",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-symfony\/zipball\/b689a4e48af872b4d0dcd3945a3ebf6262efdb5e",
"reference": "b689a4e48af872b4d0dcd3945a3ebf6262efdb5e",
"shasum": ""
},
"require": {
@ -2289,19 +2289,19 @@
"phpstan\/phpstan-strict-rules": "^1.3",
"phpstan\/phpstan-webmozart-assert": "^1.2",
"phpunit\/phpunit": "^9.5",
"rector\/phpstan-rules": "^0.5.8",
"rector\/rector-src": "dev-main",
"rector\/phpstan-rules": "^0.5.16",
"rector\/rector-src": "dev-main#b216950",
"symfony\/routing": "^6.1",
"symfony\/security-core": "^6.1",
"symfony\/security-http": "^6.1",
"symplify\/easy-ci": "^11.0",
"symplify\/easy-coding-standard": "^11.0",
"symplify\/phpstan-extensions": "^11.0",
"symplify\/phpstan-rules": "^11.0",
"symplify\/rule-doc-generator": "^11.0",
"symplify\/vendor-patches": "^11.0"
"symplify\/easy-ci": "^11.1",
"symplify\/easy-coding-standard": "^11.1",
"symplify\/phpstan-extensions": "^11.1",
"symplify\/phpstan-rules": "^11.1",
"symplify\/rule-doc-generator": "^11.1",
"symplify\/vendor-patches": "^11.1"
},
"time": "2022-08-19T12:50:54+00:00",
"time": "2022-08-20T20:19:06+00:00",
"default-branch": true,
"type": "rector-extension",
"extra": {

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ namespace Rector\RectorInstaller;
*/
final class GeneratedConfig
{
public const EXTENSIONS = array('rector/rector-cakephp' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-cakephp', 'relative_install_path' => '../../rector-cakephp', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main c2ec06c'), 'rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main c63e288'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main efc7ddf'), 'rector/rector-generator' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-generator', 'relative_install_path' => '../../rector-generator', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 38440b9'), 'rector/rector-laravel' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-laravel', 'relative_install_path' => '../../rector-laravel', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 20edb95'), 'rector/rector-phpoffice' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-phpoffice', 'relative_install_path' => '../../rector-phpoffice', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 3638a66'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 0e0e399'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 20ac2a4'));
public const EXTENSIONS = array('rector/rector-cakephp' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-cakephp', 'relative_install_path' => '../../rector-cakephp', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main c2ec06c'), 'rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main e1dba76'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main efc7ddf'), 'rector/rector-generator' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-generator', 'relative_install_path' => '../../rector-generator', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 38440b9'), 'rector/rector-laravel' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-laravel', 'relative_install_path' => '../../rector-laravel', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 20edb95'), 'rector/rector-phpoffice' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-phpoffice', 'relative_install_path' => '../../rector-phpoffice', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 3638a66'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 0e0e399'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main b689a4e'));
private function __construct()
{
}

View File

@ -17,6 +17,6 @@ return static function (RectorConfig $rectorConfig) : void {
'*/Source/*',
'*/Fixture/*',
]);
$rectorConfig->sets([LevelSetList::UP_TO_PHP_81, SetList::DEAD_CODE, SetList::CODE_QUALITY, SetList::NAMING]);
$rectorConfig->sets([LevelSetList::UP_TO_PHP_81, SetList::DEAD_CODE, SetList::CODE_QUALITY, SetList::NAMING, SetList::TYPE_DECLARATION, SetList::TYPE_DECLARATION_STRICT, SetList::PRIVATIZATION]);
$rectorConfig->ruleWithConfiguration(StringClassNameToClassConstantRector::class, ['Doctrine\\*', 'Gedmo\\*', 'Knp\\*', 'DateTime', 'DateTimeInterface']);
};

View File

@ -3,9 +3,11 @@
declare (strict_types=1);
namespace Rector\Doctrine\NodeFactory;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
@ -43,8 +45,8 @@ final class EntityIdNodeFactory
// add @ORM\Id
$phpDocTagNodes = [];
$phpDocTagNodes[] = new SpacelessPhpDocTagNode('@ORM\\Id', new DoctrineAnnotationTagValueNode(new IdentifierTypeNode('Doctrine\\ORM\\Mapping\\Id'), null, []));
$phpDocTagNodes[] = new SpacelessPhpDocTagNode('@ORM\\Column', new DoctrineAnnotationTagValueNode(new IdentifierTypeNode('Doctrine\\ORM\\Mapping\\Column'), null, ['type' => '"integer"']));
$phpDocTagNodes[] = new SpacelessPhpDocTagNode('@ORM\\GeneratedValue', new DoctrineAnnotationTagValueNode(new IdentifierTypeNode('Doctrine\\ORM\\Mapping\\GeneratedValue'), null, ['strategy' => '"AUTO"']));
$phpDocTagNodes[] = new SpacelessPhpDocTagNode('@ORM\\Column', new DoctrineAnnotationTagValueNode(new IdentifierTypeNode('Doctrine\\ORM\\Mapping\\Column'), null, [new ArrayItemNode('integer', 'type', String_::KIND_DOUBLE_QUOTED)]));
$phpDocTagNodes[] = new SpacelessPhpDocTagNode('@ORM\\GeneratedValue', new DoctrineAnnotationTagValueNode(new IdentifierTypeNode('Doctrine\\ORM\\Mapping\\GeneratedValue'), null, [new ArrayItemNode('AUTO', 'strategy', String_::KIND_DOUBLE_QUOTED)]));
foreach ($phpDocTagNodes as $phpDocTagNode) {
$phpDocInfo->addPhpDocTagNode($phpDocTagNode);
}

View File

@ -0,0 +1,55 @@
<?php
declare (strict_types=1);
namespace Rector\Doctrine\NodeFactory;
use PhpParser\Builder\Method;
use PhpParser\Node\Expr;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\StringType;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\PhpAttribute\AnnotationToAttributeMapper;
final class SluggableClassMethodFactory
{
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory
*/
private $phpDocInfoFactory;
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger
*/
private $phpDocTypeChanger;
/**
* @readonly
* @var \Rector\PhpAttribute\AnnotationToAttributeMapper
*/
private $annotationToAttributeMapper;
public function __construct(PhpDocInfoFactory $phpDocInfoFactory, PhpDocTypeChanger $phpDocTypeChanger, AnnotationToAttributeMapper $annotationToAttributeMapper)
{
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->phpDocTypeChanger = $phpDocTypeChanger;
$this->annotationToAttributeMapper = $annotationToAttributeMapper;
}
public function createGetSluggableFields(ArrayItemNode $arrayItemNode) : ClassMethod
{
$builderClassMethod = new Method('getSluggableFields');
$builderClassMethod->makePublic();
$builderClassMethod->setReturnType(new Identifier('array'));
/** @var Expr $slugFieldsExprs */
$slugFieldsExprs = $this->annotationToAttributeMapper->map($arrayItemNode->value);
$builderClassMethod->addStmt(new Return_($slugFieldsExprs));
$classMethod = $builderClassMethod->getNode();
$returnArrayType = new ArrayType(new MixedType(), new StringType());
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $returnArrayType);
return $classMethod;
}
}

View File

@ -13,6 +13,7 @@ use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
@ -111,11 +112,14 @@ final class ColumnPropertyTypeResolver
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$type = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type');
if (!\is_string($type)) {
$typeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
if (!$typeArrayItemNode instanceof ArrayItemNode) {
return new MixedType();
}
return $this->createPHPStanTypeFromDoctrineStringType($type, $isNullable);
if (!\is_string($typeArrayItemNode->value)) {
return null;
}
return $this->createPHPStanTypeFromDoctrineStringType($typeArrayItemNode->value, $isNullable);
}
/**
* @return \PHPStan\Type\MixedType|\PHPStan\Type\Type

View File

@ -6,6 +6,7 @@ namespace Rector\Doctrine\NodeManipulator;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
final class DoctrineItemDefaultValueManipulator
@ -27,10 +28,11 @@ final class DoctrineItemDefaultValueManipulator
*/
private function hasItemWithDefaultValue(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, string $itemKey, $defaultValue) : bool
{
$currentValue = $doctrineAnnotationTagValueNode->getValueWithoutQuotes($itemKey);
if ($currentValue === null) {
$currentValueArrayItemNode = $doctrineAnnotationTagValueNode->getValue($itemKey);
if (!$currentValueArrayItemNode instanceof ArrayItemNode) {
return \false;
}
$currentValue = $currentValueArrayItemNode->value;
if ($defaultValue === \false) {
return $currentValue instanceof ConstExprFalseNode;
}

View File

@ -6,6 +6,7 @@ namespace Rector\Doctrine\NodeManipulator;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
@ -56,7 +57,10 @@ final class NullabilityColumnPropertyTypeResolver
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return \true;
}
$nullableValue = $doctrineAnnotationTagValueNode->getValue('nullable');
return $nullableValue === null || $nullableValue instanceof ConstExprTrueNode;
$nullableValueArrayItemNode = $doctrineAnnotationTagValueNode->getValue('nullable');
if (!$nullableValueArrayItemNode instanceof ArrayItemNode) {
return \true;
}
return $nullableValueArrayItemNode->value instanceof ConstExprTrueNode;
}
}

View File

@ -7,6 +7,7 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
@ -65,11 +66,14 @@ final class ToManyRelationPropertyTypeResolver
}
private function processToManyRelation(Property $property, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : ?\PHPStan\Type\Type
{
$targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('targetEntity');
if (!\is_string($targetEntity)) {
$targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue('targetEntity');
if (!$targetEntityArrayItemNode instanceof ArrayItemNode) {
return null;
}
return $this->resolveTypeFromTargetEntity($targetEntity, $property);
if (!\is_string($targetEntityArrayItemNode->value)) {
return null;
}
return $this->resolveTypeFromTargetEntity($targetEntityArrayItemNode->value, $property);
}
/**
* @param \PhpParser\Node\Expr|string $targetEntity

View File

@ -11,6 +11,7 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
@ -79,15 +80,19 @@ final class ToOneRelationPropertyTypeResolver
}
private function processToOneRelation(Property $property, DoctrineAnnotationTagValueNode $toOneDoctrineAnnotationTagValueNode, ?DoctrineAnnotationTagValueNode $joinDoctrineAnnotationTagValueNode) : Type
{
$targetEntity = $toOneDoctrineAnnotationTagValueNode->getValueWithoutQuotes('targetEntity');
if (!\is_string($targetEntity)) {
$targetEntity = $toOneDoctrineAnnotationTagValueNode->getValue('targetEntity');
if (!$targetEntity instanceof ArrayItemNode) {
return new MixedType();
}
if (\substr_compare($targetEntity, '::class', -\strlen('::class')) === 0) {
$targetEntity = Strings::before($targetEntity, '::class');
$targetEntityClass = $targetEntity->value;
if (!\is_string($targetEntityClass)) {
return new MixedType();
}
if (\substr_compare($targetEntityClass, '::class', -\strlen('::class')) === 0) {
$targetEntityClass = Strings::before($targetEntityClass, '::class');
}
// resolve to FQN
$tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntity, $property);
$tagFullyQualifiedName = $this->classAnnotationMatcher->resolveTagFullyQualifiedName($targetEntityClass, $property);
if ($tagFullyQualifiedName === null) {
return new MixedType();
}

View File

@ -5,7 +5,7 @@ namespace Rector\Doctrine\PhpDocParser;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher;
class DoctrineClassAnnotationMatcher
final class DoctrineClassAnnotationMatcher
{
/**
* @readonly

View File

@ -93,7 +93,7 @@ CODE_SAMPLE
$classPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($class);
$hasLoggableAnnotation = \false;
$phpDocNodeTraverser = new PhpDocNodeTraverser();
$phpDocNodeTraverser->traverseWithCallable($classPhpDocInfo->getPhpDocNode(), '', function ($node) use(&$hasLoggableAnnotation) {
$phpDocNodeTraverser->traverseWithCallable($classPhpDocInfo->getPhpDocNode(), '', function ($node) use(&$hasLoggableAnnotation) : ?int {
if (!$node instanceof SpacelessPhpDocTagNode) {
return null;
}
@ -122,7 +122,7 @@ CODE_SAMPLE
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$hasChanged = \false;
$phpDocNodeTraverser = new PhpDocNodeTraverser();
$phpDocNodeTraverser->traverseWithCallable($phpDocInfo->getPhpDocNode(), '', function ($node) use($phpDocInfo, &$hasChanged) {
$phpDocNodeTraverser->traverseWithCallable($phpDocInfo->getPhpDocNode(), '', function ($node) use($phpDocInfo, &$hasChanged) : ?int {
if (!$node instanceof SpacelessPhpDocTagNode) {
return null;
}

View File

@ -7,6 +7,7 @@ use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Core\Rector\AbstractRector;
@ -116,8 +117,11 @@ CODE_SAMPLE
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return;
}
$type = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type');
if ($type !== 'datetime') {
$typeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
if (!$typeArrayItemNode instanceof ArrayItemNode) {
return;
}
if ($typeArrayItemNode->value !== 'datetime') {
return;
}
$node = $this->constructorAssignPropertyAnalyzer->resolveConstructorAssign($property);
@ -127,11 +131,14 @@ CODE_SAMPLE
}
$this->hasChanged = \true;
// 1. remove default options from database level
$options = $doctrineAnnotationTagValueNode->getValue('options');
if ($options instanceof CurlyListNode) {
$options->removeValue('default');
$optionsArrayItemNode = $doctrineAnnotationTagValueNode->getValue('options');
if ($optionsArrayItemNode instanceof ArrayItemNode) {
if (!$optionsArrayItemNode->value instanceof CurlyListNode) {
return;
}
$optionsArrayItemNode->value->removeValue('default');
// if empty, remove it completely
if ($options->getValues() === []) {
if ($optionsArrayItemNode->value->getValues() === []) {
$doctrineAnnotationTagValueNode->removeValue('options');
}
}

View File

@ -5,6 +5,7 @@ namespace Rector\Doctrine\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -56,11 +57,10 @@ CODE_SAMPLE
return null;
}
$repositoryClass = $doctrineAnnotationTagValueNode->getValue('repositoryClass');
if (!\is_string($repositoryClass)) {
if (!$repositoryClass instanceof ArrayItemNode) {
return null;
}
$doctrineAnnotationTagValueNode->removeValue('repositoryClass');
$phpDocInfo->markAsChanged();
return $node;
}
}

View File

@ -4,19 +4,14 @@ declare (strict_types=1);
namespace Rector\Doctrine\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\StringType;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeManipulator\ClassInsertManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Doctrine\NodeFactory\SluggableClassMethodFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -34,13 +29,13 @@ final class SluggableBehaviorRector extends AbstractRector
private $classInsertManipulator;
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger
* @var \Rector\Doctrine\NodeFactory\SluggableClassMethodFactory
*/
private $phpDocTypeChanger;
public function __construct(ClassInsertManipulator $classInsertManipulator, PhpDocTypeChanger $phpDocTypeChanger)
private $sluggableClassMethodFactory;
public function __construct(ClassInsertManipulator $classInsertManipulator, SluggableClassMethodFactory $sluggableClassMethodFactory)
{
$this->classInsertManipulator = $classInsertManipulator;
$this->phpDocTypeChanger = $phpDocTypeChanger;
$this->sluggableClassMethodFactory = $sluggableClassMethodFactory;
}
public function getRuleDefinition() : RuleDefinition
{
@ -124,23 +119,11 @@ CODE_SAMPLE
}
$this->classInsertManipulator->addAsFirstTrait($node, 'Knp\\DoctrineBehaviors\\Model\\Sluggable\\SluggableTrait');
$node->implements[] = new FullyQualified('Knp\\DoctrineBehaviors\\Contract\\Entity\\SluggableInterface');
if (!\is_array($slugFields)) {
throw new ShouldNotHappenException();
if (!$slugFields instanceof ArrayItemNode) {
return null;
}
$this->addGetSluggableFieldsClassMethod($node, $slugFields);
$getSluggableClassMethod = $this->sluggableClassMethodFactory->createGetSluggableFields($slugFields);
$this->classInsertManipulator->addAsFirstMethod($node, $getSluggableClassMethod);
return $node;
}
/**
* @param string[] $slugFields
*/
private function addGetSluggableFieldsClassMethod(Class_ $class, array $slugFields) : void
{
$classMethod = $this->nodeFactory->createPublicMethod('getSluggableFields');
$classMethod->returnType = new Identifier('array');
$classMethod->stmts[] = new Return_($this->nodeFactory->createArray($slugFields));
$returnType = new ArrayType(new MixedType(), new StringType());
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $returnType);
$this->classInsertManipulator->addAsFirstMethod($class, $classMethod);
}
}

View File

@ -6,6 +6,7 @@ namespace Rector\Doctrine\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\Core\NodeManipulator\ClassInsertManipulator;
@ -89,11 +90,14 @@ CODE_SAMPLE
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$fieldName = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('fieldName');
if (!\is_string($fieldName)) {
$fieldNameArrayItemNode = $doctrineAnnotationTagValueNode->getValue('fieldName');
if (!$fieldNameArrayItemNode instanceof ArrayItemNode) {
return null;
}
$this->removePropertyAndClassMethods($node, $fieldName);
if (!\is_string($fieldNameArrayItemNode->value)) {
return null;
}
$this->removePropertyAndClassMethods($node, $fieldNameArrayItemNode->value);
$this->classInsertManipulator->addAsFirstTrait($node, 'Knp\\DoctrineBehaviors\\Model\\SoftDeletable\\SoftDeletableTrait');
$node->implements[] = new FullyQualified('Knp\\DoctrineBehaviors\\Contract\\Entity\\SoftDeletableInterface');
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineAnnotationTagValueNode);

View File

@ -10,6 +10,7 @@ use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\StringType;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockClassRenamer;
@ -83,8 +84,11 @@ CODE_SAMPLE
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$type = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type');
if ($type !== 'bigint') {
$typeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
if (!$typeArrayItemNode instanceof ArrayItemNode) {
return null;
}
if ($typeArrayItemNode->value !== 'bigint') {
return null;
}
$varTagValueNode = $phpDocInfo->getVarTagValueNode();

View File

@ -11,6 +11,7 @@ use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\PropertyProperty;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\Core\Rector\AbstractRector;
@ -75,11 +76,18 @@ CODE_SAMPLE
if (!$defaultValue instanceof Expr) {
return null;
}
$type = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('type');
if (\in_array($type, ['bool', 'boolean'], \true)) {
$typeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
if (!$typeArrayItemNode instanceof ArrayItemNode) {
return null;
}
$typeValue = $typeArrayItemNode->value;
if (!\is_string($typeValue)) {
return null;
}
if (\in_array($typeValue, ['bool', 'boolean'], \true)) {
return $this->refactorToBoolType($onlyProperty, $node);
}
if (\in_array($type, ['int', 'integer', 'bigint', 'smallint'], \true)) {
if (\in_array($typeValue, ['int', 'integer', 'bigint', 'smallint'], \true)) {
return $this->refactorToIntType($onlyProperty, $node);
}
return null;

View File

@ -12,6 +12,7 @@ use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
@ -24,7 +25,13 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class DoctrineTargetEntityStringToClassConstantRector extends AbstractRector
{
/**
* @var string
*/
private const ATTRIBUTE_NAME__TARGET_ENTITY = 'targetEntity';
/**
* @var string
*/
private const ATTRIBUTE_NAME__CLASS = 'class';
/**
* @var array<class-string<OneToMany|ManyToOne|OneToOne|ManyToMany|Embedded>, string>
@ -73,6 +80,9 @@ final class SomeClass
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes() : array
{
return [Property::class];
@ -83,7 +93,7 @@ CODE_SAMPLE
public function refactor(Node $node) : ?Node
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
if ($phpDocInfo !== null) {
if ($phpDocInfo instanceof PhpDocInfo) {
$property = $this->changeTypeInAnnotationTypes($node, $phpDocInfo);
$annotationDetected = $property !== null || $phpDocInfo->hasChanged();
if ($annotationDetected) {
@ -137,9 +147,12 @@ CODE_SAMPLE
private function processDoctrineToMany(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, Property $property) : ?Property
{
$key = $doctrineAnnotationTagValueNode->hasClassName('Doctrine\\ORM\\Mapping\\Embedded') ? self::ATTRIBUTE_NAME__CLASS : self::ATTRIBUTE_NAME__TARGET_ENTITY;
/** @var ?string $targetEntity */
$targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes($key);
if ($targetEntity === null) {
$targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue($key);
if (!$targetEntityArrayItemNode instanceof ArrayItemNode) {
return null;
}
$targetEntity = $targetEntityArrayItemNode->value;
if (!\is_string($targetEntity)) {
return null;
}
// resolve to FQN
@ -150,8 +163,14 @@ CODE_SAMPLE
if ($tagFullyQualifiedName === $targetEntity) {
return null;
}
$doctrineAnnotationTagValueNode->removeValue($key);
$doctrineAnnotationTagValueNode->values[$key] = '\\' . \ltrim($tagFullyQualifiedName, '\\') . '::class';
$currentArrayItemNode = $doctrineAnnotationTagValueNode->getValue($key);
if (!$currentArrayItemNode instanceof ArrayItemNode) {
return null;
}
// no quotes needed, it's a constants
$currentArrayItemNode->kindValueQuoted = null;
$currentArrayItemNode->value = '\\' . \ltrim($tagFullyQualifiedName, '\\') . '::class';
$currentArrayItemNode->setAttribute('orig_node', null);
return $property;
}
/**

View File

@ -9,9 +9,9 @@ use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Doctrine\PhpDoc\ShortClassExpander;
use Rector\StaticTypeMapper\Naming\NameScopeFactory;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
@ -62,11 +62,14 @@ final class CollectionTypeResolver
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('targetEntity');
if (!\is_string($targetEntity)) {
throw new ShouldNotHappenException();
$targetEntityArrayItemNode = $doctrineAnnotationTagValueNode->getValue('targetEntity');
if (!$targetEntityArrayItemNode instanceof ArrayItemNode) {
return null;
}
$fullyQualifiedTargetEntity = $this->shortClassExpander->resolveFqnTargetEntity($targetEntity, $property);
if (!\is_string($targetEntityArrayItemNode->value)) {
return null;
}
$fullyQualifiedTargetEntity = $this->shortClassExpander->resolveFqnTargetEntity($targetEntityArrayItemNode->value, $property);
return new FullyQualifiedObjectType($fullyQualifiedTargetEntity);
}
}

View File

@ -14,17 +14,17 @@
"phpstan\/phpstan-strict-rules": "^1.3",
"phpstan\/phpstan-webmozart-assert": "^1.2",
"phpunit\/phpunit": "^9.5",
"rector\/phpstan-rules": "^0.5.8",
"rector\/rector-src": "dev-main",
"rector\/phpstan-rules": "^0.5.16",
"rector\/rector-src": "dev-main#b216950",
"symfony\/routing": "^6.1",
"symfony\/security-core": "^6.1",
"symfony\/security-http": "^6.1",
"symplify\/easy-ci": "^11.0",
"symplify\/easy-coding-standard": "^11.0",
"symplify\/phpstan-extensions": "^11.0",
"symplify\/phpstan-rules": "^11.0",
"symplify\/rule-doc-generator": "^11.0",
"symplify\/vendor-patches": "^11.0"
"symplify\/easy-ci": "^11.1",
"symplify\/easy-coding-standard": "^11.1",
"symplify\/phpstan-extensions": "^11.1",
"symplify\/phpstan-rules": "^11.1",
"symplify\/rule-doc-generator": "^11.1",
"symplify\/vendor-patches": "^11.1"
},
"autoload": {
"psr-4": {

View File

@ -10,6 +10,7 @@ use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Rector\Renaming\ValueObject\RenameClassConstFetch;
use Rector\Symfony\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector;
use Rector\Symfony\Set\SymfonySetList;
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
use Rector\TypeDeclaration\ValueObject\AddReturnTypeDeclaration;
@ -48,4 +49,5 @@ return static function (RectorConfig $rectorConfig) : void {
// @see https://github.com/symfony/symfony/pull/40536
new RenameClassConstFetch('Symfony\\Component\\HttpKernel\\HttpKernelInterface', 'MASTER_REQUEST', 'MAIN_REQUEST'),
]);
$rectorConfig->rule(KernelTestCaseContainerPropertyDeprecationRector::class);
};

View File

@ -9,4 +9,8 @@ final class SymfonyAnnotation
* @var string
*/
public const ROUTE = 'Symfony\\Component\\Routing\\Annotation\\Route';
/**
* @var string
*/
public const SENSIO_METHOD = 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Method';
}

View File

@ -10,6 +10,7 @@ use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\IntegerType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Doctrine\NodeAnalyzer\AttrinationFinder;
@ -75,14 +76,22 @@ final class RouteRequiredParamNameToTypesResolver
{
$paramsToRegexes = [];
$requirementsValue = $doctrineAnnotationTagValueNode->getValue('requirements');
if (!$requirementsValue instanceof CurlyListNode) {
if (!$requirementsValue instanceof ArrayItemNode) {
return [];
}
foreach ($requirementsValue->getValuesWithExplicitSilentAndWithoutQuotes() as $paramName => $paramRegex) {
if (!\is_string($paramName)) {
if (!$requirementsValue->value instanceof CurlyListNode) {
return [];
}
foreach ($requirementsValue->value->getValuesWithSilentKey() as $nestedArrayItemNode) {
if (!\is_string($nestedArrayItemNode->value)) {
continue;
}
$paramsToRegexes[$paramName] = (string) $paramRegex;
if (!\is_string($nestedArrayItemNode->key)) {
continue;
}
$paramName = $nestedArrayItemNode->key;
$paramRegex = $nestedArrayItemNode->value;
$paramsToRegexes[$paramName] = $paramRegex;
}
return $paramsToRegexes;
}

View File

@ -25,4 +25,12 @@ final class SymfonyTestCaseAnalyzer
}
return $classReflection->isSubclassOf('Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase');
}
public function isInKernelTestCase(Node $node) : bool
{
$classReflection = $this->reflectionResolver->resolveClassReflection($node);
if (!$classReflection instanceof ClassReflection) {
return \false;
}
return $classReflection->isSubclassOf('Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase');
}
}

View File

@ -3,9 +3,18 @@
declare (strict_types=1);
namespace Rector\Symfony\NodeFactory\Annotations;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
final class StringValueQuoteWrapper
{
/**
* @var \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser
*/
private $arrayParser;
public function __construct(ArrayParser $arrayParser)
{
$this->arrayParser = $arrayParser;
}
/**
* @return mixed|CurlyListNode|string
* @param mixed $value
@ -34,6 +43,7 @@ final class StringValueQuoteWrapper
$value[$nestedKey] = '"' . $nestedValue . '"';
}
}
return new CurlyListNode($value);
$arrayItemNodes = $this->arrayParser->createArrayFromValues($value);
return new CurlyListNode($arrayItemNodes);
}
}

View File

@ -1,49 +0,0 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\NodeFactory\Annotations;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
final class ValueQuoteWrapper
{
/**
* @return mixed|CurlyListNode|string
* @param mixed $value
*/
public function wrap($value)
{
if (\is_string($value)) {
return '"' . $value . '"';
}
if (\is_array($value)) {
return $this->wrapArray($value);
}
return $value;
}
/**
* @param mixed[] $value
*/
private function wrapArray(array $value) : CurlyListNode
{
$wrappedValue = [];
foreach ($value as $nestedKey => $nestedValue) {
switch (\true) {
case \is_numeric($nestedKey):
$key = $nestedKey;
break;
default:
$key = '"' . $nestedKey . '"';
break;
}
switch (\true) {
case \is_string($nestedValue):
$wrappedValue[$key] = '"' . $nestedValue . '"';
break;
default:
$wrappedValue[$key] = $nestedValue;
break;
}
}
return new CurlyListNode($wrappedValue);
}
}

View File

@ -14,6 +14,7 @@ use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\ArrayType;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Core\PhpParser\Node\NodeFactory;
@ -83,12 +84,13 @@ final class ThisRenderFactory
}
private function resolveParametersExpr(?Return_ $return, DoctrineAnnotationTagValueNode $templateDoctrineAnnotationTagValueNode) : ?Expr
{
$vars = $templateDoctrineAnnotationTagValueNode->getValue('vars');
if ($vars instanceof CurlyListNode) {
$vars = $vars->getValuesWithExplicitSilentAndWithoutQuotes();
$vars = [];
$varsArrayItemNode = $templateDoctrineAnnotationTagValueNode->getValue('vars');
if ($varsArrayItemNode instanceof ArrayItemNode && $varsArrayItemNode->value instanceof CurlyListNode) {
$vars = $varsArrayItemNode->value->getValues();
}
if (\is_array($vars) && $vars !== []) {
return $this->createArrayFromVars($vars);
if ($vars !== []) {
return $this->createArrayFromArrayItemNodes($vars);
}
if ($return === null) {
return null;
@ -106,13 +108,13 @@ final class ThisRenderFactory
return null;
}
/**
* @param string[] $vars
* @param ArrayItemNode[] $arrayItemNodes
*/
private function createArrayFromVars(array $vars) : Array_
private function createArrayFromArrayItemNodes(array $arrayItemNodes) : Array_
{
$arrayItems = [];
foreach ($vars as $var) {
$arrayItems[] = new ArrayItem(new Variable($var), new String_($var));
foreach ($arrayItemNodes as $arrayItemNode) {
$arrayItems[] = new ArrayItem(new Variable($arrayItemNode->value), new String_($arrayItemNode->value));
}
return new Array_($arrayItems);
}
@ -127,12 +129,12 @@ final class ThisRenderFactory
private function resolveTemplate(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : ?string
{
$templateParameter = $doctrineAnnotationTagValueNode->getValue('template');
if (\is_string($templateParameter)) {
return $templateParameter;
if ($templateParameter instanceof ArrayItemNode && \is_string($templateParameter->value)) {
return $templateParameter->value;
}
$silentValue = $doctrineAnnotationTagValueNode->getSilentValue();
if (\is_string($silentValue)) {
return $silentValue;
$arrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();
if ($arrayItemNode instanceof ArrayItemNode && \is_string($arrayItemNode->value)) {
return $arrayItemNode->value;
}
return null;
}

View File

@ -4,15 +4,17 @@ declare (strict_types=1);
namespace Rector\Symfony\PhpDocNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\Symfony\Enum\SymfonyAnnotation;
final class SymfonyRouteTagValueNodeFactory
{
/**
* @param array<string, mixed> $items
* @param ArrayItemNode[] $arrayItemNodes
*/
public function createFromItems(array $items) : DoctrineAnnotationTagValueNode
public function createFromItems(array $arrayItemNodes) : DoctrineAnnotationTagValueNode
{
return new DoctrineAnnotationTagValueNode(new IdentifierTypeNode(SymfonyAnnotation::ROUTE), null, $items, 'path');
$identifierTypeNode = new IdentifierTypeNode(SymfonyAnnotation::ROUTE);
return new DoctrineAnnotationTagValueNode($identifierTypeNode, null, $arrayItemNodes, 'path');
}
}

View File

@ -4,13 +4,15 @@ declare (strict_types=1);
namespace Rector\Symfony\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\Core\Rector\AbstractRector;
use Rector\Symfony\Contract\Bridge\Symfony\Routing\SymfonyRoutesProviderInterface;
use Rector\Symfony\Enum\SymfonyAnnotation;
use Rector\Symfony\NodeFactory\Annotations\ValueQuoteWrapper;
use Rector\Symfony\PhpDocNode\SymfonyRouteTagValueNodeFactory;
use Rector\Symfony\ValueObject\SymfonyRouteMetadata;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -32,14 +34,14 @@ final class AddRouteAnnotationRector extends AbstractRector
private $symfonyRouteTagValueNodeFactory;
/**
* @readonly
* @var \Rector\Symfony\NodeFactory\Annotations\ValueQuoteWrapper
* @var \Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser
*/
private $valueQuoteWrapper;
public function __construct(SymfonyRoutesProviderInterface $symfonyRoutesProvider, SymfonyRouteTagValueNodeFactory $symfonyRouteTagValueNodeFactory, ValueQuoteWrapper $valueQuoteWrapper)
private $arrayParser;
public function __construct(SymfonyRoutesProviderInterface $symfonyRoutesProvider, SymfonyRouteTagValueNodeFactory $symfonyRouteTagValueNodeFactory, ArrayParser $arrayParser)
{
$this->symfonyRoutesProvider = $symfonyRoutesProvider;
$this->symfonyRouteTagValueNodeFactory = $symfonyRouteTagValueNodeFactory;
$this->valueQuoteWrapper = $valueQuoteWrapper;
$this->arrayParser = $arrayParser;
}
public function getNodeTypes() : array
{
@ -115,35 +117,42 @@ CODE_SAMPLE
return $className . '::' . $methodName;
}
/**
* @return array{path: string, name: string, defaults?: CurlyListNode, host?: string, methods?: CurlyListNode, condition?: string, options?: CurlyListNode}
* @return ArrayItemNode[]
*/
private function createRouteItems(SymfonyRouteMetadata $symfonyRouteMetadata) : array
{
$items = ['path' => $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getPath()), 'name' => $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getName())];
$arrayItemNodes = [];
$arrayItemNodes[] = new ArrayItemNode($symfonyRouteMetadata->getPath(), 'path', String_::KIND_DOUBLE_QUOTED);
$arrayItemNodes[] = new ArrayItemNode($symfonyRouteMetadata->getName(), 'name', String_::KIND_DOUBLE_QUOTED);
$defaultsWithoutController = $symfonyRouteMetadata->getDefaultsWithoutController();
if ($defaultsWithoutController !== []) {
$items['defaults'] = $this->valueQuoteWrapper->wrap($defaultsWithoutController);
$defaultsWithoutControllerCurlyList = $this->createCurlyQuoted($defaultsWithoutController);
$arrayItemNodes[] = new ArrayItemNode($defaultsWithoutControllerCurlyList, 'defaults');
}
if ($symfonyRouteMetadata->getHost() !== '') {
$items['host'] = $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getHost());
$arrayItemNodes[] = new ArrayItemNode($symfonyRouteMetadata->getHost(), 'host');
}
if ($symfonyRouteMetadata->getSchemes() !== []) {
$items['schemes'] = $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getSchemes());
$schemesArrayItemNodes = $this->createCurlyQuoted($symfonyRouteMetadata->getSchemes());
$arrayItemNodes[] = new ArrayItemNode($schemesArrayItemNodes, 'schemes');
}
if ($symfonyRouteMetadata->getMethods() !== []) {
$items['methods'] = $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getMethods());
$methodsCurlyList = $this->createCurlyQuoted($symfonyRouteMetadata->getMethods());
$arrayItemNodes[] = new ArrayItemNode($methodsCurlyList, 'methods');
}
if ($symfonyRouteMetadata->getCondition() !== '') {
$items['condition'] = $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getCondition());
$arrayItemNodes[] = new ArrayItemNode($symfonyRouteMetadata->getCondition(), 'condition', String_::KIND_DOUBLE_QUOTED);
}
if ($symfonyRouteMetadata->getRequirements() !== []) {
$items['requirements'] = $this->valueQuoteWrapper->wrap($symfonyRouteMetadata->getRequirements());
$requriementsCurlyList = $this->createCurlyQuoted($symfonyRouteMetadata->getRequirements());
$arrayItemNodes[] = new ArrayItemNode($requriementsCurlyList, 'requirements');
}
$optionsWithoutDefaultCompilerClass = $symfonyRouteMetadata->getOptionsWithoutDefaultCompilerClass();
if ($optionsWithoutDefaultCompilerClass !== []) {
$items['options'] = $this->valueQuoteWrapper->wrap($optionsWithoutDefaultCompilerClass);
$optionsCurlyQuoted = $this->createCurlyQuoted($optionsWithoutDefaultCompilerClass);
$arrayItemNodes[] = new ArrayItemNode($optionsCurlyQuoted, 'options');
}
return $items;
return $arrayItemNodes;
}
private function matchSymfonyRouteMetadataByControllerReference(string $controllerReference) : ?SymfonyRouteMetadata
{
@ -154,4 +163,21 @@ CODE_SAMPLE
}
return null;
}
/**
* @param mixed[] $values
*/
private function createCurlyQuoted(array $values) : CurlyListNode
{
$methodsArrayItems = $this->arrayParser->createArrayFromValues($values);
$curlyListNode = new CurlyListNode($methodsArrayItems);
foreach ($curlyListNode->values as $nestedMethodsArrayItem) {
if (!\is_numeric($nestedMethodsArrayItem->value)) {
$nestedMethodsArrayItem->kindValueQuoted = String_::KIND_DOUBLE_QUOTED;
}
if (\is_string($nestedMethodsArrayItem->key)) {
$nestedMethodsArrayItem->kindKeyQuoted = String_::KIND_DOUBLE_QUOTED;
}
}
return $curlyListNode;
}
}

View File

@ -6,6 +6,7 @@ namespace Rector\Symfony\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter;
@ -89,7 +90,7 @@ CODE_SAMPLE
return null;
}
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$sensioDoctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClass('Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Method');
$sensioDoctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClass(SymfonyAnnotation::SENSIO_METHOD);
if (!$sensioDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
@ -97,11 +98,16 @@ CODE_SAMPLE
if (!$symfonyDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$methods = $this->resolveMethods($sensioDoctrineAnnotationTagValueNode);
if ($methods === null) {
$sensioMethods = $this->resolveMethods($sensioDoctrineAnnotationTagValueNode);
if ($sensioMethods === null) {
return null;
}
$symfonyDoctrineAnnotationTagValueNode->changeValue('methods', $methods);
$symfonyMethodsArrayItemNode = $symfonyDoctrineAnnotationTagValueNode->getValue('methods');
// value is already filled, do not enter anythign
if ($symfonyMethodsArrayItemNode instanceof ArrayItemNode) {
return null;
}
$symfonyDoctrineAnnotationTagValueNode->values[] = new ArrayItemNode($sensioMethods, 'methods');
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $sensioDoctrineAnnotationTagValueNode);
$this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo);
return $node;
@ -112,18 +118,12 @@ CODE_SAMPLE
private function resolveMethods(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode)
{
$methodsParameter = $doctrineAnnotationTagValueNode->getValue('methods');
if (\is_array($methodsParameter)) {
return $methodsParameter;
if ($methodsParameter instanceof ArrayItemNode && $methodsParameter->value instanceof CurlyListNode) {
return $methodsParameter->value;
}
if ($methodsParameter instanceof CurlyListNode) {
return $methodsParameter;
}
$silentValue = $doctrineAnnotationTagValueNode->getSilentValue();
if (\is_array($silentValue)) {
return $silentValue;
}
if ($silentValue instanceof CurlyListNode) {
return $silentValue;
$arrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();
if ($arrayItemNode instanceof ArrayItemNode) {
return $arrayItemNode->value;
}
return null;
}

View File

@ -0,0 +1,76 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\Rector\StaticPropertyFetch;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Name;
use Rector\Core\Rector\AbstractRector;
use Rector\Symfony\NodeAnalyzer\SymfonyTestCaseAnalyzer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Symfony\Tests\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector\KernelTestCaseContainerPropertyDeprecationRectorTest
*/
class KernelTestCaseContainerPropertyDeprecationRector extends AbstractRector
{
/**
* @readonly
* @var \Rector\Symfony\NodeAnalyzer\SymfonyTestCaseAnalyzer
*/
private $symfonyTestCaseAnalyzer;
public function __construct(SymfonyTestCaseAnalyzer $symfonyTestCaseAnalyzer)
{
$this->symfonyTestCaseAnalyzer = $symfonyTestCaseAnalyzer;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Simplify use of assertions in WebTestCase', [new CodeSample(<<<'CODE_SAMPLE'
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SomeTest extends KernelTestCase
{
protected function setUp(): void
{
$container = self::$container;
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SomeTest extends KernelTestCase
{
protected function setUp(): void
{
$container = self::getContainer();
}
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes() : array
{
return [StaticPropertyFetch::class];
}
/**
* @param StaticPropertyFetch $node
*/
public function refactor(Node $node) : ?Node
{
if (!$this->symfonyTestCaseAnalyzer->isInKernelTestCase($node)) {
return null;
}
if ($this->nodeNameResolver->getName($node->name) !== 'container') {
return null;
}
if (!$node->class instanceof Name || (string) $node->class !== 'self') {
return null;
}
return $this->nodeFactory->createStaticCall('self', 'getContainer');
}
}

View File

@ -8,6 +8,7 @@ use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Core\Exception\ShouldNotHappenException;
@ -99,13 +100,13 @@ final class JMSDITypeResolver
}
private function resolveServiceName(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, Property $property) : string
{
$serviceNameParameter = $doctrineAnnotationTagValueNode->getValueWithoutQuotes('serviceName');
if (\is_string($serviceNameParameter)) {
return $serviceNameParameter;
$serviceNameParameter = $doctrineAnnotationTagValueNode->getValue('serviceName');
if ($serviceNameParameter instanceof ArrayItemNode && \is_string($serviceNameParameter->value)) {
return $serviceNameParameter->value;
}
$silentValue = $doctrineAnnotationTagValueNode->getSilentValue();
if (\is_string($silentValue)) {
return $silentValue;
$arrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();
if ($arrayItemNode instanceof ArrayItemNode && \is_string($arrayItemNode->value)) {
return $arrayItemNode->value;
}
return $this->nodeNameResolver->getName($property);
}