77use PhpParser \Node \Expr ;
88use PhpParser \Node \Expr \BinaryOp \Concat ;
99use PHPStan \Analyser \Scope ;
10+ use PHPStan \ShouldNotHappenException ;
1011use PHPStan \Type \Constant \ConstantArrayType ;
1112use PHPStan \Type \Constant \ConstantIntegerType ;
1213use PHPStan \Type \Constant \ConstantStringType ;
@@ -204,7 +205,7 @@ public static function getQueryType(string $query): ?string
204205 *
205206 * @throws UnresolvableQueryException
206207 *
207- * @return array<string|int, scalar|null >|null
208+ * @return array<string|int, Parameter >|null
208209 */
209210 public function resolveParameters (Type $ parameterTypes ): ?array
210211 {
@@ -213,18 +214,35 @@ public function resolveParameters(Type $parameterTypes): ?array
213214 if ($ parameterTypes instanceof ConstantArrayType) {
214215 $ keyTypes = $ parameterTypes ->getKeyTypes ();
215216 $ valueTypes = $ parameterTypes ->getValueTypes ();
217+ $ optionalKeys = $ parameterTypes ->getOptionalKeys ();
216218
217219 foreach ($ keyTypes as $ i => $ keyType ) {
220+ $ isOptional = \in_array ($ i , $ optionalKeys , true );
221+
218222 if ($ keyType instanceof ConstantStringType) {
219223 $ placeholderName = $ keyType ->getValue ();
220224
221- if (! str_starts_with ( $ placeholderName , ' : ' ) ) {
222- $ placeholderName = ' : ' . $ placeholderName ;
225+ if ('' === $ placeholderName ) {
226+ throw new ShouldNotHappenException ( ' Empty placeholder name ' ) ;
223227 }
224228
225- $ parameters [$ placeholderName ] = QuerySimulation::simulateParamValueType ($ valueTypes [$ i ], true );
229+ $ param = new Parameter (
230+ $ placeholderName ,
231+ $ valueTypes [$ i ],
232+ QuerySimulation::simulateParamValueType ($ valueTypes [$ i ], true ),
233+ $ isOptional
234+ );
235+
236+ $ parameters [$ param ->name ] = $ param ;
226237 } elseif ($ keyType instanceof ConstantIntegerType) {
227- $ parameters [$ keyType ->getValue ()] = QuerySimulation::simulateParamValueType ($ valueTypes [$ i ], true );
238+ $ param = new Parameter (
239+ null ,
240+ $ valueTypes [$ i ],
241+ QuerySimulation::simulateParamValueType ($ valueTypes [$ i ], true ),
242+ $ isOptional
243+ );
244+
245+ $ parameters [$ keyType ->getValue ()] = $ param ;
228246 }
229247 }
230248
@@ -235,7 +253,7 @@ public function resolveParameters(Type $parameterTypes): ?array
235253 }
236254
237255 /**
238- * @param array<string|int, scalar|null > $parameters
256+ * @param array<string|int, Parameter > $parameters
239257 */
240258 private function replaceParameters (string $ queryString , array $ parameters ): string
241259 {
@@ -248,7 +266,9 @@ private function replaceParameters(string $queryString, array $parameters): stri
248266 return $ haystack ;
249267 };
250268
251- foreach ($ parameters as $ placeholderKey => $ value ) {
269+ foreach ($ parameters as $ placeholderKey => $ parameter ) {
270+ $ value = $ parameter ->simulatedValue ;
271+
252272 if (\is_string ($ value )) {
253273 // XXX escaping
254274 $ value = "' " .$ value ."' " ;
@@ -258,10 +278,10 @@ private function replaceParameters(string $queryString, array $parameters): stri
258278 $ value = (string ) $ value ;
259279 }
260280
261- if (\is_int ($ placeholderKey )) {
262- $ queryString = $ replaceFirst ($ queryString , '? ' , $ value );
263- } else {
281+ if (\is_string ($ placeholderKey )) {
264282 $ queryString = str_replace ($ placeholderKey , $ value , $ queryString );
283+ } else {
284+ $ queryString = $ replaceFirst ($ queryString , '? ' , $ value );
265285 }
266286 }
267287
0 commit comments