Skip to content

Commit e76d45d

Browse files
committed
always add nullable on TypedPropertyFromJMSSerializerAttributeTypeRector as serializer does not use constructor
1 parent 1193412 commit e76d45d

5 files changed

Lines changed: 105 additions & 45 deletions

File tree

rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned_by_constructor.php.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSeriali
2222
final class NonTypedAssignedByConstructor
2323
{
2424
#[\JMS\Serializer\Annotation\Type('string')]
25-
private string $name;
25+
private ?string $name = null;
2626

2727
public function __construct($name)
2828
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
4+
5+
class NullableInt
6+
{
7+
#[\JMS\Serializer\Annotation\Type('int')]
8+
protected $number;
9+
}
10+
11+
?>
12+
-----
13+
<?php
14+
15+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
16+
17+
class NullableInt
18+
{
19+
#[\JMS\Serializer\Annotation\Type('int')]
20+
protected ?int $number = null;
21+
}
22+
23+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
4+
5+
final class NullableIntWithConstructor
6+
{
7+
#[\JMS\Serializer\Annotation\Type('int')]
8+
protected $number;
9+
10+
public function __construct(int $number)
11+
{
12+
$this->number = $number;
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
21+
22+
final class NullableIntWithConstructor
23+
{
24+
#[\JMS\Serializer\Annotation\Type('int')]
25+
protected ?int $number = null;
26+
27+
public function __construct(int $number)
28+
{
29+
$this->number = $number;
30+
}
31+
}
32+
33+
?>

rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_typed_assigned.php.inc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
44

5-
final class SkipTypedAssigned
5+
final class ExactTypedAssigned
66
{
77
#[\JMS\Serializer\Annotation\Type('string')]
88
private $name;
@@ -12,3 +12,22 @@ final class SkipTypedAssigned
1212
$this->name = $name;
1313
}
1414
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
21+
22+
final class ExactTypedAssigned
23+
{
24+
#[\JMS\Serializer\Annotation\Type('string')]
25+
private ?string $name = null;
26+
27+
public function setName(string $name): void
28+
{
29+
$this->name = $name;
30+
}
31+
}
32+
33+
?>

rules/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector.php

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
use PhpParser\Node\Name\FullyQualified;
1212
use PhpParser\Node\NullableType;
1313
use PhpParser\Node\Stmt\Class_;
14+
use PhpParser\Node\Stmt\Property;
1415
use PHPStan\Reflection\ClassReflection;
1516
use PHPStan\Type\MixedType;
1617
use PHPStan\Type\ObjectType;
17-
use PHPStan\Type\Type;
1818
use Rector\Doctrine\CodeQuality\Enum\CollectionMapping;
1919
use Rector\Enum\ClassName;
2020
use Rector\Php74\Guard\MakePropertyTypedGuard;
@@ -25,8 +25,6 @@
2525
use Rector\Reflection\ReflectionResolver;
2626
use Rector\StaticTypeMapper\Mapper\ScalarStringToTypeMapper;
2727
use Rector\StaticTypeMapper\StaticTypeMapper;
28-
use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector;
29-
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\AllAssignNodePropertyTypeInferer;
3028
use Rector\Util\StringUtils;
3129
use Rector\ValueObject\PhpVersionFeature;
3230
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@@ -39,14 +37,12 @@
3937
final class TypedPropertyFromJMSSerializerAttributeTypeRector extends AbstractRector implements MinPhpVersionInterface
4038
{
4139
public function __construct(
42-
private readonly AllAssignNodePropertyTypeInferer $allAssignNodePropertyTypeInferer,
4340
private readonly MakePropertyTypedGuard $makePropertyTypedGuard,
4441
private readonly ReflectionResolver $reflectionResolver,
4542
private readonly ValueResolver $valueResolver,
4643
private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer,
4744
private readonly ScalarStringToTypeMapper $scalarStringToTypeMapper,
48-
private readonly StaticTypeMapper $staticTypeMapper,
49-
private readonly ConstructorAssignDetector $constructorAssignDetector
45+
private readonly StaticTypeMapper $staticTypeMapper
5046
) {
5147
}
5248

@@ -124,39 +120,15 @@ public function refactor(Node $node): ?Node
124120
continue;
125121
}
126122

127-
$inferredType = $this->allAssignNodePropertyTypeInferer->inferProperty(
128-
$property,
129-
$classReflection,
130-
$this->file
131-
);
132-
// has assigned with type
133-
if ($inferredType instanceof Type) {
134-
continue;
135-
}
136-
137123
if ($property->props[0]->default instanceof Node) {
138124
continue;
139125
}
140126

141-
$typeValue = null;
142-
foreach ($property->attrGroups as $attrGroup) {
143-
foreach ($attrGroup->attrs as $attr) {
144-
if ($attr->name->toString() === ClassName::JMS_TYPE) {
145-
$typeValue = $this->valueResolver->getValue($attr->args[0]->value);
146-
break;
147-
}
148-
}
149-
}
150-
127+
$typeValue = $this->resolveAttributeType($property);
151128
if (! is_string($typeValue)) {
152129
continue;
153130
}
154131

155-
if (StringUtils::isMatch($typeValue, '#DateTime\<(.*?)\>#')) {
156-
// special case for DateTime, which is not a scalar type
157-
$typeValue = 'DateTime';
158-
}
159-
160132
// skip generic iterable types
161133
if (str_contains($typeValue, '<')) {
162134
continue;
@@ -174,20 +146,10 @@ public function refactor(Node $node): ?Node
174146
return null;
175147
}
176148

177-
$isInConstructorAssigned = $this->constructorAssignDetector->isPropertyAssigned(
178-
$node,
179-
$this->getName($property)
180-
);
181-
$type = $isInConstructorAssigned ? $propertyType : new NullableType($propertyType);
182-
183-
$property->type = $type;
184-
185-
if (! $isInConstructorAssigned) {
186-
$property->props[0]->default = new ConstFetch(new Name('null'));
187-
}
149+
$property->type = new NullableType($propertyType);
150+
$property->props[0]->default = new ConstFetch(new Name('null'));
188151

189152
$hasChanged = true;
190-
// }
191153
}
192154

193155
if ($hasChanged) {
@@ -196,4 +158,27 @@ public function refactor(Node $node): ?Node
196158

197159
return null;
198160
}
161+
162+
private function resolveAttributeType(Property $property): ?string
163+
{
164+
foreach ($property->attrGroups as $attrGroup) {
165+
foreach ($attrGroup->attrs as $attr) {
166+
if (! $this->isName($attr->name, ClassName::JMS_TYPE)) {
167+
continue;
168+
}
169+
170+
$typeValue = $this->valueResolver->getValue($attr->args[0]->value);
171+
if (! is_string($typeValue)) {
172+
return null;
173+
}
174+
175+
if (StringUtils::isMatch($typeValue, '#DateTime\<(.*?)\>#')) {
176+
// special case for DateTime, which is not a scalar type
177+
return 'DateTime';
178+
}
179+
}
180+
}
181+
182+
return null;
183+
}
199184
}

0 commit comments

Comments
 (0)