diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index f85e079a42d..d796ba11395 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -19,12 +19,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '0f09b7a0b9f9f3a445a8c4014c8d951579d1de03'; + public const PACKAGE_VERSION = '147c96190e99909c71c2d920a83ae96cc9b3bd57'; /** * @api * @var string */ - public const RELEASE_DATE = '2024-08-06 11:27:51'; + public const RELEASE_DATE = '2024-08-06 12:31:33'; /** * @var int */ diff --git a/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php b/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php index 432ecc42235..858b6e0b855 100644 --- a/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php +++ b/src/PHPStanStaticTypeMapper/Utils/TypeUnwrapper.php @@ -21,7 +21,7 @@ public function unwrapFirstObjectTypeFromUnionType(Type $type) : Type } return $type; } - public function removeNullTypeFromUnionType(UnionType $unionType) : UnionType + public function removeNullTypeFromUnionType(UnionType $unionType) : Type { $unionedTypesWithoutNullType = []; foreach ($unionType->getTypes() as $type) { @@ -30,6 +30,9 @@ public function removeNullTypeFromUnionType(UnionType $unionType) : UnionType } $unionedTypesWithoutNullType[] = $type; } + if ($unionedTypesWithoutNullType !== []) { + return $unionedTypesWithoutNullType[0]; + } return new UnionType($unionedTypesWithoutNullType); } } diff --git a/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php b/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php new file mode 100644 index 00000000000..3932c2aee7c --- /dev/null +++ b/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php @@ -0,0 +1,96 @@ +reflectionProvider = $reflectionProvider; + } + /** + * @param Arg[] $args + */ + public function castAttributeTypes(AnnotationToAttribute $annotationToAttribute, array $args) : void + { + Assert::allIsInstanceOf($args, Arg::class); + if (!$this->reflectionProvider->hasClass($annotationToAttribute->getAttributeClass())) { + return; + } + $attributeClassReflection = $this->reflectionProvider->getClass($annotationToAttribute->getAttributeClass()); + if (!$attributeClassReflection->hasConstructor()) { + return; + } + $parameterReflections = $this->resolveConstructorParameterReflections($attributeClassReflection); + foreach ($parameterReflections as $parameterReflection) { + foreach ($args as $arg) { + if (!$arg->value instanceof ArrayItem) { + continue; + } + $arrayItem = $arg->value; + if (!$arrayItem->key instanceof String_) { + continue; + } + $keyString = $arrayItem->key; + if ($keyString->value !== $parameterReflection->getName()) { + continue; + } + // ensure type is casted to integer + if (!$arrayItem->value instanceof String_) { + continue; + } + if (!$this->containsInteger($parameterReflection->getType())) { + continue; + } + $valueString = $arrayItem->value; + if (!\is_numeric($valueString->value)) { + continue; + } + $arrayItem->value = new LNumber((int) $valueString->value); + } + } + } + private function containsInteger(Type $type) : bool + { + if ($type instanceof IntegerType) { + return \true; + } + if (!$type instanceof UnionType) { + return \false; + } + foreach ($type->getTypes() as $unionedType) { + if ($unionedType instanceof IntegerType) { + return \true; + } + } + return \false; + } + /** + * @return ParameterReflection[] + */ + private function resolveConstructorParameterReflections(ClassReflection $classReflection) : array + { + $extendedMethodReflection = $classReflection->getConstructor(); + $parametersAcceptorWithPhpDocs = ParametersAcceptorSelector::selectSingle($extendedMethodReflection->getVariants()); + return $parametersAcceptorWithPhpDocs->getParameters(); + } +} diff --git a/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php b/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php index fc6fa0b02e2..8539249b317 100644 --- a/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php +++ b/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php @@ -45,12 +45,18 @@ final class PhpAttributeGroupFactory * @var \Rector\PhpAttribute\AttributeArrayNameInliner */ private $attributeArrayNameInliner; - public function __construct(AnnotationToAttributeMapper $annotationToAttributeMapper, \Rector\PhpAttribute\NodeFactory\AttributeNameFactory $attributeNameFactory, \Rector\PhpAttribute\NodeFactory\NamedArgsFactory $namedArgsFactory, AttributeArrayNameInliner $attributeArrayNameInliner) + /** + * @readonly + * @var \Rector\PhpAttribute\NodeFactory\AnnotationToAttributeIntegerValueCaster + */ + private $annotationToAttributeIntegerValueCaster; + public function __construct(AnnotationToAttributeMapper $annotationToAttributeMapper, \Rector\PhpAttribute\NodeFactory\AttributeNameFactory $attributeNameFactory, \Rector\PhpAttribute\NodeFactory\NamedArgsFactory $namedArgsFactory, AttributeArrayNameInliner $attributeArrayNameInliner, \Rector\PhpAttribute\NodeFactory\AnnotationToAttributeIntegerValueCaster $annotationToAttributeIntegerValueCaster) { $this->annotationToAttributeMapper = $annotationToAttributeMapper; $this->attributeNameFactory = $attributeNameFactory; $this->namedArgsFactory = $namedArgsFactory; $this->attributeArrayNameInliner = $attributeArrayNameInliner; + $this->annotationToAttributeIntegerValueCaster = $annotationToAttributeIntegerValueCaster; } public function createFromSimpleTag(AnnotationToAttribute $annotationToAttribute) : AttributeGroup { @@ -72,7 +78,7 @@ public function createFromClass(string $attributeClass) : AttributeGroup public function createFromClassWithItems(string $attributeClass, array $items) : AttributeGroup { $fullyQualified = new FullyQualified($attributeClass); - $args = $this->createArgsFromItems($items, $attributeClass); + $args = $this->createArgsFromItems($items); $attribute = new Attribute($fullyQualified, $args); return new AttributeGroup([$attribute]); } @@ -82,7 +88,8 @@ public function createFromClassWithItems(string $attributeClass, array $items) : public function create(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, AnnotationToAttribute $annotationToAttribute, array $uses) : AttributeGroup { $values = $doctrineAnnotationTagValueNode->getValuesWithSilentKey(); - $args = $this->createArgsFromItems($values, $annotationToAttribute->getAttributeClass(), $annotationToAttribute->getClassReferenceFields()); + $args = $this->createArgsFromItems($values, '', $annotationToAttribute->getClassReferenceFields()); + $this->annotationToAttributeIntegerValueCaster->castAttributeTypes($annotationToAttribute, $args); $args = $this->attributeArrayNameInliner->inlineArrayToArgs($args); $attributeName = $this->attributeNameFactory->create($annotationToAttribute, $doctrineAnnotationTagValueNode, $uses); // keep FQN in the attribute, so it can be easily detected later @@ -100,9 +107,10 @@ public function create(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValu * * @param ArrayItemNode[]|mixed[] $items * @param string[] $classReferencedFields + * * @return Arg[] */ - public function createArgsFromItems(array $items, string $attributeClass, array $classReferencedFields = []) : array + public function createArgsFromItems(array $items, string $attributeClass = '', array $classReferencedFields = []) : array { $mappedItems = $this->annotationToAttributeMapper->map($items); $this->mapClassReferences($mappedItems, $classReferencedFields); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index b801cece607..17bef66bdf2 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -1976,6 +1976,7 @@ 'Rector\\PhpAttribute\\AttributeArrayNameInliner' => $baseDir . '/src/PhpAttribute/AttributeArrayNameInliner.php', 'Rector\\PhpAttribute\\Contract\\AnnotationToAttributeMapperInterface' => $baseDir . '/src/PhpAttribute/Contract/AnnotationToAttributeMapperInterface.php', 'Rector\\PhpAttribute\\Enum\\DocTagNodeState' => $baseDir . '/src/PhpAttribute/Enum/DocTagNodeState.php', + 'Rector\\PhpAttribute\\NodeFactory\\AnnotationToAttributeIntegerValueCaster' => $baseDir . '/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php', 'Rector\\PhpAttribute\\NodeFactory\\AttributeNameFactory' => $baseDir . '/src/PhpAttribute/NodeFactory/AttributeNameFactory.php', 'Rector\\PhpAttribute\\NodeFactory\\NamedArgsFactory' => $baseDir . '/src/PhpAttribute/NodeFactory/NamedArgsFactory.php', 'Rector\\PhpAttribute\\NodeFactory\\PhpAttributeGroupFactory' => $baseDir . '/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 3368d097e5d..254abfbfcc8 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -2195,6 +2195,7 @@ class ComposerStaticInite9e62f8370526a5f08ea47a490713455 'Rector\\PhpAttribute\\AttributeArrayNameInliner' => __DIR__ . '/../..' . '/src/PhpAttribute/AttributeArrayNameInliner.php', 'Rector\\PhpAttribute\\Contract\\AnnotationToAttributeMapperInterface' => __DIR__ . '/../..' . '/src/PhpAttribute/Contract/AnnotationToAttributeMapperInterface.php', 'Rector\\PhpAttribute\\Enum\\DocTagNodeState' => __DIR__ . '/../..' . '/src/PhpAttribute/Enum/DocTagNodeState.php', + 'Rector\\PhpAttribute\\NodeFactory\\AnnotationToAttributeIntegerValueCaster' => __DIR__ . '/../..' . '/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php', 'Rector\\PhpAttribute\\NodeFactory\\AttributeNameFactory' => __DIR__ . '/../..' . '/src/PhpAttribute/NodeFactory/AttributeNameFactory.php', 'Rector\\PhpAttribute\\NodeFactory\\NamedArgsFactory' => __DIR__ . '/../..' . '/src/PhpAttribute/NodeFactory/NamedArgsFactory.php', 'Rector\\PhpAttribute\\NodeFactory\\PhpAttributeGroupFactory' => __DIR__ . '/../..' . '/src/PhpAttribute/NodeFactory/PhpAttributeGroupFactory.php',