5
5
use PhpParser \Node \Expr \FuncCall ;
6
6
use PHPStan \Analyser \Scope ;
7
7
use PHPStan \Reflection \FunctionReflection ;
8
+ use PHPStan \Reflection \ParametersAcceptorSelector ;
9
+ use PHPStan \TrinaryLogic ;
10
+ use PHPStan \Type \Accessory \AccessoryArrayListType ;
11
+ use PHPStan \Type \Accessory \NonEmptyArrayType ;
8
12
use PHPStan \Type \ArrayType ;
13
+ use PHPStan \Type \Constant \ConstantArrayType ;
14
+ use PHPStan \Type \Constant \ConstantBooleanType ;
9
15
use PHPStan \Type \DynamicFunctionReturnTypeExtension ;
10
16
use PHPStan \Type \IntegerType ;
17
+ use PHPStan \Type \MixedType ;
11
18
use PHPStan \Type \StringType ;
12
19
use PHPStan \Type \Type ;
20
+ use PHPStan \Type \TypeCombinator ;
21
+ use PHPStan \Type \TypeTraverser ;
22
+ use PHPStan \Type \UnionType ;
23
+ use PHPStan \Type \VerbosityLevel ;
13
24
14
25
final class MbConvertEncodingFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
15
26
{
@@ -30,16 +41,62 @@ public function getTypeFromFunctionCall(
30
41
}
31
42
32
43
$ argType = $ scope ->getType ($ functionCall ->getArgs ()[0 ]->value );
33
- $ isString = $ argType ->isString ();
34
- $ isArray = $ argType ->isArray ();
35
- $ compare = $ isString ->compareTo ($ isArray );
36
- if ($ compare === $ isString ) {
44
+
45
+ $ initialReturnType = ParametersAcceptorSelector::selectFromArgs (
46
+ $ scope ,
47
+ $ functionCall ->getArgs (),
48
+ $ functionReflection ->getVariants (),
49
+ )->getReturnType ();
50
+
51
+ $ computedReturnType = $ this ->generalizeStringType ($ argType );
52
+
53
+ return TypeCombinator::intersect (
54
+ $ initialReturnType ,
55
+ TypeCombinator::union ($ computedReturnType , new ConstantBooleanType (false ))
56
+ );
57
+ }
58
+
59
+ private function generalizeStringType (Type $ type ): Type
60
+ {
61
+ if ($ type instanceof UnionType) {
62
+ return $ type ->traverse ($ this ->generalizeStringType (...));
63
+ }
64
+
65
+ if ($ type ->isString ()->yes ()) {
37
66
return new StringType ();
38
- } elseif ($ compare === $ isArray ) {
39
- return new ArrayType (new IntegerType (), new StringType ());
40
67
}
41
68
42
- return null ;
69
+ $ constantArrays = $ type ->getConstantArrays ();
70
+ if (count ($ constantArrays ) > 0 ) {
71
+ $ types = [];
72
+ foreach ($ constantArrays as $ constantArray ) {
73
+ $ c = new ConstantArrayType (
74
+ $ constantArray ->getKeyTypes (),
75
+ array_map ($ this ->generalizeStringType (...), $ constantArray ->getValueTypes ()),
76
+ $ constantArray ->getNextAutoIndexes (),
77
+ $ constantArray ->getOptionalKeys (),
78
+ $ constantArray ->isList (),
79
+ );
80
+
81
+ $ types [] = $ c ;
82
+ }
83
+
84
+ return TypeCombinator::union (...$ types );
85
+ }
86
+
87
+ if ($ type ->isArray ()->yes ()) {
88
+ $ newArrayType = new ArrayType ($ type ->getIterableKeyType (), $ this ->generalizeStringType ($ type ->getIterableValueType ()));
89
+ if ($ type ->isIterableAtLeastOnce ()->yes ()) {
90
+ $ newArrayType = TypeCombinator::intersect ($ newArrayType , new NonEmptyArrayType ());
91
+ }
92
+ if ($ type ->isList ()->yes ()) {
93
+ $ newArrayType = TypeCombinator::intersect ($ newArrayType , new AccessoryArrayListType ());
94
+ }
95
+
96
+ return $ newArrayType ;
97
+ }
98
+
99
+ return $ type ;
43
100
}
44
101
45
102
}
0 commit comments