55namespace Rector \TypeDeclaration \Rector \Class_ ;
66
77use PhpParser \Node ;
8+ use PhpParser \Node \Expr ;
89use PhpParser \Node \Expr \ConstFetch ;
910use PhpParser \Node \Identifier ;
1011use PhpParser \Node \Name ;
1112use PhpParser \Node \Name \FullyQualified ;
1213use PhpParser \Node \NullableType ;
1314use PhpParser \Node \Stmt \Class_ ;
15+ use PhpParser \Node \Stmt \Property ;
1416use PHPStan \Reflection \ClassReflection ;
1517use PHPStan \Type \MixedType ;
1618use PHPStan \Type \ObjectType ;
17- use PHPStan \Type \Type ;
1819use Rector \Doctrine \CodeQuality \Enum \CollectionMapping ;
1920use Rector \Enum \ClassName ;
2021use Rector \Php74 \Guard \MakePropertyTypedGuard ;
2526use Rector \Reflection \ReflectionResolver ;
2627use Rector \StaticTypeMapper \Mapper \ScalarStringToTypeMapper ;
2728use Rector \StaticTypeMapper \StaticTypeMapper ;
28- use Rector \TypeDeclaration \AlreadyAssignDetector \ConstructorAssignDetector ;
29- use Rector \TypeDeclaration \TypeInferer \PropertyTypeInferer \AllAssignNodePropertyTypeInferer ;
3029use Rector \Util \StringUtils ;
3130use Rector \ValueObject \PhpVersionFeature ;
3231use Rector \VersionBonding \Contract \MinPhpVersionInterface ;
3938final class TypedPropertyFromJMSSerializerAttributeTypeRector extends AbstractRector implements MinPhpVersionInterface
4039{
4140 public function __construct (
42- private readonly AllAssignNodePropertyTypeInferer $ allAssignNodePropertyTypeInferer ,
4341 private readonly MakePropertyTypedGuard $ makePropertyTypedGuard ,
4442 private readonly ReflectionResolver $ reflectionResolver ,
4543 private readonly ValueResolver $ valueResolver ,
4644 private readonly PhpAttributeAnalyzer $ phpAttributeAnalyzer ,
4745 private readonly ScalarStringToTypeMapper $ scalarStringToTypeMapper ,
48- private readonly StaticTypeMapper $ staticTypeMapper ,
49- private readonly ConstructorAssignDetector $ constructorAssignDetector
46+ private readonly StaticTypeMapper $ staticTypeMapper
5047 ) {
5148 }
5249
@@ -96,7 +93,7 @@ public function refactor(Node $node): ?Node
9693 $ classReflection = null ;
9794
9895 foreach ($ node ->getProperties () as $ property ) {
99- if ($ property ->type instanceof Node) {
96+ if ($ property ->type instanceof Node || $ property -> props [ 0 ]-> default instanceof Expr ) {
10097 continue ;
10198 }
10299
@@ -124,39 +121,11 @@ public function refactor(Node $node): ?Node
124121 continue ;
125122 }
126123
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-
137- if ($ property ->props [0 ]->default instanceof Node) {
138- continue ;
139- }
140-
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-
124+ $ typeValue = $ this ->resolveAttributeType ($ property );
151125 if (! is_string ($ typeValue )) {
152126 continue ;
153127 }
154128
155- if (StringUtils::isMatch ($ typeValue , '#DateTime\<(.*?)\># ' )) {
156- // special case for DateTime, which is not a scalar type
157- $ typeValue = 'DateTime ' ;
158- }
159-
160129 // skip generic iterable types
161130 if (str_contains ($ typeValue , '< ' )) {
162131 continue ;
@@ -174,20 +143,10 @@ public function refactor(Node $node): ?Node
174143 return null ;
175144 }
176145
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- }
146+ $ property ->type = new NullableType ($ propertyType );
147+ $ property ->props [0 ]->default = new ConstFetch (new Name ('null ' ));
188148
189149 $ hasChanged = true ;
190- // }
191150 }
192151
193152 if ($ hasChanged ) {
@@ -196,4 +155,29 @@ public function refactor(Node $node): ?Node
196155
197156 return null ;
198157 }
158+
159+ private function resolveAttributeType (Property $ property ): ?string
160+ {
161+ foreach ($ property ->attrGroups as $ attrGroup ) {
162+ foreach ($ attrGroup ->attrs as $ attr ) {
163+ if (! $ this ->isName ($ attr ->name , ClassName::JMS_TYPE )) {
164+ continue ;
165+ }
166+
167+ $ typeValue = $ this ->valueResolver ->getValue ($ attr ->args [0 ]->value );
168+ if (! is_string ($ typeValue )) {
169+ return null ;
170+ }
171+
172+ if (StringUtils::isMatch ($ typeValue , '#DateTime\<(.*?)\># ' )) {
173+ // special case for DateTime, which is not a scalar type
174+ return 'DateTime ' ;
175+ }
176+
177+ return $ typeValue ;
178+ }
179+ }
180+
181+ return null ;
182+ }
199183}
0 commit comments