37
37
use PHPStan \Type \Traits \NonGenericTypeTrait ;
38
38
use PHPStan \Type \Traits \UndecidedComparisonTypeTrait ;
39
39
use Traversable ;
40
+ use function array_key_exists ;
40
41
use function array_keys ;
41
42
use function array_map ;
42
43
use function array_values ;
43
44
use function count ;
45
+ use function implode ;
44
46
use function in_array ;
45
47
use function sprintf ;
46
48
use function strtolower ;
@@ -71,10 +73,10 @@ class ObjectType implements TypeWithClassName, SubtractableType
71
73
/** @var array<string, array<string, array<string, UnresolvedPropertyPrototypeReflection>>> */
72
74
private static array $ properties = [];
73
75
74
- /** @var array<string, array<string, self>> */
76
+ /** @var array<string, array<string, self|null >> */
75
77
private static array $ ancestors = [];
76
78
77
- /** @var array<string, self> */
79
+ /** @var array<string, self|null > */
78
80
private array $ currentAncestors = [];
79
81
80
82
/** @api */
@@ -436,6 +438,16 @@ private function describeCache(): string
436
438
}
437
439
438
440
$ description = $ this ->className ;
441
+
442
+ if ($ this instanceof GenericObjectType) {
443
+ $ description .= '< ' ;
444
+ $ typeDescriptions = [];
445
+ foreach ($ this ->getTypes () as $ type ) {
446
+ $ typeDescriptions [] = $ type ->describe (VerbosityLevel::cache ());
447
+ }
448
+ $ description .= '< ' . implode (', ' , $ typeDescriptions ) . '> ' ;
449
+ }
450
+
439
451
if ($ this ->subtractedType !== null ) {
440
452
$ description .= sprintf ('~%s ' , $ this ->subtractedType ->describe (VerbosityLevel::cache ()));
441
453
}
@@ -1019,35 +1031,41 @@ public function getTypeWithoutSubtractedType(): Type
1019
1031
1020
1032
public function changeSubtractedType (?Type $ subtractedType ): Type
1021
1033
{
1022
- $ classReflection = $ this ->getClassReflection ();
1023
- if ($ classReflection !== null && $ classReflection ->isEnum () && $ subtractedType !== null ) {
1024
- $ cases = [];
1025
- foreach (array_keys ($ classReflection ->getEnumCases ()) as $ name ) {
1026
- $ cases [$ name ] = new EnumCaseObjectType ($ classReflection ->getName (), $ name );
1027
- }
1028
-
1029
- foreach (TypeUtils::flattenTypes ($ subtractedType ) as $ subType ) {
1030
- if (!$ subType instanceof EnumCaseObjectType) {
1031
- return new self ($ this ->className , $ subtractedType );
1034
+ if ($ subtractedType !== null ) {
1035
+ $ classReflection = $ this ->getClassReflection ();
1036
+ if ($ classReflection !== null && $ classReflection ->isEnum ()) {
1037
+ $ cases = [];
1038
+ foreach (array_keys ($ classReflection ->getEnumCases ()) as $ name ) {
1039
+ $ cases [$ name ] = new EnumCaseObjectType ($ classReflection ->getName (), $ name );
1032
1040
}
1033
1041
1034
- if ($ subType ->getClassName () !== $ this ->getClassName ()) {
1035
- return new self ($ this ->className , $ subtractedType );
1042
+ foreach (TypeUtils::flattenTypes ($ subtractedType ) as $ subType ) {
1043
+ if (!$ subType instanceof EnumCaseObjectType) {
1044
+ return new self ($ this ->className , $ subtractedType );
1045
+ }
1046
+
1047
+ if ($ subType ->getClassName () !== $ this ->getClassName ()) {
1048
+ return new self ($ this ->className , $ subtractedType );
1049
+ }
1050
+
1051
+ unset($ cases [$ subType ->getEnumCaseName ()]);
1036
1052
}
1037
1053
1038
- unset($ cases [$ subType ->getEnumCaseName ()]);
1039
- }
1054
+ $ cases = array_values ($ cases );
1055
+ if (count ($ cases ) === 0 ) {
1056
+ return new NeverType ();
1057
+ }
1040
1058
1041
- $ cases = array_values ($ cases );
1042
- if (count ($ cases ) === 0 ) {
1043
- return new NeverType ();
1044
- }
1059
+ if (count ($ cases ) === 1 ) {
1060
+ return $ cases [0 ];
1061
+ }
1045
1062
1046
- if (count ($ cases ) === 1 ) {
1047
- return $ cases [0 ];
1063
+ return new UnionType (array_values ($ cases ));
1048
1064
}
1065
+ }
1049
1066
1050
- return new UnionType (array_values ($ cases ));
1067
+ if ($ this ->subtractedType === null && $ subtractedType === null ) {
1068
+ return $ this ;
1051
1069
}
1052
1070
1053
1071
return new self ($ this ->className , $ subtractedType );
@@ -1110,26 +1128,32 @@ public function getClassReflection(): ?ClassReflection
1110
1128
*/
1111
1129
public function getAncestorWithClassName (string $ className ): ?TypeWithClassName
1112
1130
{
1113
- if (isset ( $ this ->currentAncestors [ $ className ] )) {
1131
+ if (array_key_exists ( $ className , $ this ->currentAncestors )) {
1114
1132
return $ this ->currentAncestors [$ className ];
1115
1133
}
1116
1134
1117
- $ thisReflection = $ this ->getClassReflection ();
1118
- if ($ thisReflection === null ) {
1119
- return null ;
1135
+ $ description = $ this ->describeCache ();
1136
+ if (
1137
+ array_key_exists ($ description , self ::$ ancestors )
1138
+ && array_key_exists ($ className , self ::$ ancestors [$ description ])
1139
+ ) {
1140
+ return self ::$ ancestors [$ description ][$ className ];
1120
1141
}
1121
1142
1122
- $ description = $ this ->describeCache () . '- ' . $ thisReflection ->getCacheKey ();
1123
- if (isset (self ::$ ancestors [$ description ][$ className ])) {
1124
- return self ::$ ancestors [$ description ][$ className ];
1143
+ if ($ this ->className === $ className ) {
1144
+ return self ::$ ancestors [$ description ][$ className ] = $ this ->currentAncestors [$ className ] = $ this ;
1125
1145
}
1126
1146
1127
1147
$ reflectionProvider = ReflectionProviderStaticAccessor::getInstance ();
1128
1148
if (!$ reflectionProvider ->hasClass ($ className )) {
1129
- return null ;
1149
+ return self :: $ ancestors [ $ description ][ $ className ] = $ this -> currentAncestors [ $ className ] = null ;
1130
1150
}
1131
1151
$ theirReflection = $ reflectionProvider ->getClass ($ className );
1132
1152
1153
+ $ thisReflection = $ this ->getClassReflection ();
1154
+ if ($ thisReflection === null ) {
1155
+ return self ::$ ancestors [$ description ][$ className ] = $ this ->currentAncestors [$ className ] = null ;
1156
+ }
1133
1157
if ($ theirReflection ->getName () === $ thisReflection ->getName ()) {
1134
1158
return self ::$ ancestors [$ description ][$ className ] = $ this ->currentAncestors [$ className ] = $ this ;
1135
1159
}
@@ -1149,7 +1173,7 @@ public function getAncestorWithClassName(string $className): ?TypeWithClassName
1149
1173
}
1150
1174
}
1151
1175
1152
- return null ;
1176
+ return self :: $ ancestors [ $ description ][ $ className ] = $ this -> currentAncestors [ $ className ] = null ;
1153
1177
}
1154
1178
1155
1179
private function getParent (): ?ObjectType
0 commit comments