55use  PhpParser \Node \Expr \FuncCall ;
66use  PHPStan \Analyser \Scope ;
77use  PHPStan \Reflection \FunctionReflection ;
8+ use  PHPStan \Reflection \ParametersAcceptorSelector ;
9+ use  PHPStan \Type \Accessory \AccessoryArrayListType ;
10+ use  PHPStan \Type \Accessory \NonEmptyArrayType ;
811use  PHPStan \Type \ArrayType ;
12+ use  PHPStan \Type \Constant \ConstantBooleanType ;
913use  PHPStan \Type \DynamicFunctionReturnTypeExtension ;
10- use  PHPStan \Type \IntegerType ;
14+ use  PHPStan \Type \NeverType ;
1115use  PHPStan \Type \StringType ;
1216use  PHPStan \Type \Type ;
17+ use  PHPStan \Type \TypeCombinator ;
18+ use  PHPStan \Type \UnionType ;
19+ use  function  count ;
1320
1421final  class  MbConvertEncodingFunctionReturnTypeExtension implements  DynamicFunctionReturnTypeExtension
1522{
@@ -30,16 +37,54 @@ public function getTypeFromFunctionCall(
3037}
3138
3239$ argType  = $ scope ->getType ($ functionCall ->getArgs ()[0 ]->value );
33- $ isString  = $ argType ->isString ();
34- $ isArray  = $ argType ->isArray ();
35- $ compare  = $ isString ->compareTo ($ isArray );
36- if  ($ compare  === $ isString ) {
40+ 
41+ $ initialReturnType  = ParametersAcceptorSelector::selectFromArgs (
42+ $ scope ,
43+ $ functionCall ->getArgs (),
44+ $ functionReflection ->getVariants (),
45+ )->getReturnType ();
46+ 
47+ $ result  = TypeCombinator::intersect ($ initialReturnType , $ this  ->generalizeStringType ($ argType ));
48+ if  ($ result  instanceof  NeverType) {
49+ return  null ;
50+ }
51+ 
52+ return  TypeCombinator::union ($ result , new  ConstantBooleanType (false ));
53+ }
54+ 
55+ public  function  generalizeStringType (Type   $ type ): Type 
56+ {
57+ if  ($ type  instanceof  UnionType) {
58+ return  $ type ->traverse ([$ this  , 'generalizeStringType ' ]);
59+ }
60+ 
61+ if  ($ type ->isString ()->yes ()) {
3762return  new  StringType ();
38- } elseif  ($ compare  === $ isArray ) {
39- return  new  ArrayType (new  IntegerType (), new  StringType ());
4063}
4164
42- return  null ;
65+ $ constantArrays  = $ type ->getConstantArrays ();
66+ if  (count ($ constantArrays ) > 0 ) {
67+ $ types  = [];
68+ foreach  ($ constantArrays  as  $ constantArray ) {
69+ $ types [] = $ constantArray ->traverse ([$ this  , 'generalizeStringType ' ]);
70+ }
71+ 
72+ return  TypeCombinator::union (...$ types );
73+ }
74+ 
75+ if  ($ type ->isArray ()->yes ()) {
76+ $ newArrayType  = new  ArrayType ($ type ->getIterableKeyType (), $ this  ->generalizeStringType ($ type ->getIterableValueType ()));
77+ if  ($ type ->isIterableAtLeastOnce ()->yes ()) {
78+ $ newArrayType  = TypeCombinator::intersect ($ newArrayType , new  NonEmptyArrayType ());
79+ }
80+ if  ($ type ->isList ()->yes ()) {
81+ $ newArrayType  = TypeCombinator::intersect ($ newArrayType , new  AccessoryArrayListType ());
82+ }
83+ 
84+ return  $ newArrayType ;
85+ }
86+ 
87+ return  $ type ;
4388}
4489
4590}
0 commit comments