88use PDOStatement ;
99use PhpParser \Node \Expr ;
1010use PhpParser \Node \Expr \MethodCall ;
11+ use PHPStan \Type \ArrayType ;
1112use PHPStan \Type \Constant \ConstantArrayType ;
1213use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
1314use PHPStan \Type \Constant \ConstantIntegerType ;
2021use staabm \PHPStanDba \QueryReflection \QueryReflection ;
2122use staabm \PHPStanDba \QueryReflection \QueryReflector ;
2223
24+ // XXX move into a "Reflection" package on next major version
2325final class PdoStatementReflection
2426{
27+ // XXX move into separate class on next major version
2528 public function findPrepareQueryStringExpression (MethodCall $ methodCall ): ?Expr
2629 {
2730 $ exprFinder = new ExpressionFinder ();
@@ -37,6 +40,7 @@ public function findPrepareQueryStringExpression(MethodCall $methodCall): ?Expr
3740 return null ;
3841 }
3942
43+ // XXX move into separate class on next major version
4044 /**
4145 * @return MethodCall[]
4246 */
@@ -58,7 +62,9 @@ public function getFetchType(Type $fetchModeType): ?int
5862 return null ;
5963 }
6064
61- if (PDO ::FETCH_ASSOC === $ fetchModeType ->getValue ()) {
65+ if (PDO ::FETCH_KEY_PAIR === $ fetchModeType ->getValue ()) {
66+ return QueryReflector::FETCH_TYPE_KEY_VALUE ;
67+ } elseif (PDO ::FETCH_ASSOC === $ fetchModeType ->getValue ()) {
6268 return QueryReflector::FETCH_TYPE_ASSOC ;
6369 } elseif (PDO ::FETCH_NUM === $ fetchModeType ->getValue ()) {
6470 return QueryReflector::FETCH_TYPE_NUMERIC ;
@@ -153,10 +159,17 @@ public function getColumnRowType(Type $statementType, int $columnIndex): ?Type
153159 */
154160 private function reduceStatementResultType (Type $ bothType , int $ fetchType ): Type
155161 {
162+ if (!$ bothType instanceof ConstantArrayType) {
163+ return $ bothType ;
164+ }
165+
166+ if (\count ($ bothType ->getValueTypes ()) <= 0 ) {
167+ return $ bothType ;
168+ }
169+
156170 // turn a BOTH typed statement into either NUMERIC or ASSOC
157171 if (
158- (QueryReflector::FETCH_TYPE_NUMERIC === $ fetchType || QueryReflector::FETCH_TYPE_ASSOC === $ fetchType ) &&
159- $ bothType instanceof ConstantArrayType && \count ($ bothType ->getValueTypes ()) > 0
172+ QueryReflector::FETCH_TYPE_NUMERIC === $ fetchType || QueryReflector::FETCH_TYPE_ASSOC === $ fetchType
160173 ) {
161174 $ builder = ConstantArrayTypeBuilder::createEmpty ();
162175
@@ -174,6 +187,13 @@ private function reduceStatementResultType(Type $bothType, int $fetchType): Type
174187 return $ builder ->getArray ();
175188 }
176189
190+ // both types contains numeric and string keys, therefore the count is doubled
191+ if (QueryReflector::FETCH_TYPE_KEY_VALUE === $ fetchType && \count ($ bothType ->getValueTypes ()) >= 4 ) {
192+ $ valueTypes = $ bothType ->getValueTypes ();
193+
194+ return new ArrayType ($ valueTypes [0 ], $ valueTypes [2 ]);
195+ }
196+
177197 // not yet supported fetch type - or $fetchType == BOTH
178198 return $ bothType ;
179199 }
0 commit comments