Skip to content

Commit 4c7aa6e

Browse files
staabmclxmstaab
andauthored
Don't error on prepared statements containing unspecified arrays (#158)
Co-authored-by: Markus Staab <[email protected]>
1 parent 8b0f08a commit 4c7aa6e

File tree

6 files changed

+304
-5
lines changed

6 files changed

+304
-5
lines changed

.phpstan-dba.cache

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,66 @@
10101010
)),
10111011
),
10121012
),
1013+
'SELECT adaid FROM ada WHERE adaid IN (\'1\') AND email LIKE NULL' =>
1014+
array (
1015+
'error' => NULL,
1016+
'result' =>
1017+
array (
1018+
3 =>
1019+
PHPStan\Type\Constant\ConstantArrayType::__set_state(array(
1020+
'keyType' =>
1021+
PHPStan\Type\UnionType::__set_state(array(
1022+
'types' =>
1023+
array (
1024+
0 =>
1025+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1026+
'value' => 0,
1027+
)),
1028+
1 =>
1029+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1030+
'value' => 'adaid',
1031+
'isClassString' => false,
1032+
)),
1033+
),
1034+
)),
1035+
'itemType' =>
1036+
PHPStan\Type\IntegerRangeType::__set_state(array(
1037+
'min' => 0,
1038+
'max' => 4294967295,
1039+
)),
1040+
'allArrays' => NULL,
1041+
'keyTypes' =>
1042+
array (
1043+
0 =>
1044+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1045+
'value' => 'adaid',
1046+
'isClassString' => false,
1047+
)),
1048+
1 =>
1049+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1050+
'value' => 0,
1051+
)),
1052+
),
1053+
'valueTypes' =>
1054+
array (
1055+
0 =>
1056+
PHPStan\Type\IntegerRangeType::__set_state(array(
1057+
'min' => 0,
1058+
'max' => 4294967295,
1059+
)),
1060+
1 =>
1061+
PHPStan\Type\IntegerRangeType::__set_state(array(
1062+
'min' => 0,
1063+
'max' => 4294967295,
1064+
)),
1065+
),
1066+
'nextAutoIndex' => 1,
1067+
'optionalKeys' =>
1068+
array (
1069+
),
1070+
)),
1071+
),
1072+
),
10131073
'SELECT adaid FROM ada WHERE adaid IN (:adaids)' =>
10141074
array (
10151075
'error' =>
@@ -1022,6 +1082,78 @@
10221082
3 => NULL,
10231083
),
10241084
),
1085+
'SELECT adaid FROM ada WHERE adaid IN (:ids) AND email LIKE :time' =>
1086+
array (
1087+
'error' =>
1088+
staabm\PHPStanDba\Error::__set_state(array(
1089+
'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \':ids) AND email LIKE :time LIMIT 0\' at line 1',
1090+
'code' => 1064,
1091+
)),
1092+
'result' =>
1093+
array (
1094+
3 => NULL,
1095+
),
1096+
),
1097+
'SELECT adaid FROM ada WHERE adaid IN (NULL) AND email LIKE NULL' =>
1098+
array (
1099+
'error' => NULL,
1100+
'result' =>
1101+
array (
1102+
3 =>
1103+
PHPStan\Type\Constant\ConstantArrayType::__set_state(array(
1104+
'keyType' =>
1105+
PHPStan\Type\UnionType::__set_state(array(
1106+
'types' =>
1107+
array (
1108+
0 =>
1109+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1110+
'value' => 0,
1111+
)),
1112+
1 =>
1113+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1114+
'value' => 'adaid',
1115+
'isClassString' => false,
1116+
)),
1117+
),
1118+
)),
1119+
'itemType' =>
1120+
PHPStan\Type\IntegerRangeType::__set_state(array(
1121+
'min' => 0,
1122+
'max' => 4294967295,
1123+
)),
1124+
'allArrays' => NULL,
1125+
'keyTypes' =>
1126+
array (
1127+
0 =>
1128+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1129+
'value' => 'adaid',
1130+
'isClassString' => false,
1131+
)),
1132+
1 =>
1133+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1134+
'value' => 0,
1135+
)),
1136+
),
1137+
'valueTypes' =>
1138+
array (
1139+
0 =>
1140+
PHPStan\Type\IntegerRangeType::__set_state(array(
1141+
'min' => 0,
1142+
'max' => 4294967295,
1143+
)),
1144+
1 =>
1145+
PHPStan\Type\IntegerRangeType::__set_state(array(
1146+
'min' => 0,
1147+
'max' => 4294967295,
1148+
)),
1149+
),
1150+
'nextAutoIndex' => 1,
1151+
'optionalKeys' =>
1152+
array (
1153+
),
1154+
)),
1155+
),
1156+
),
10251157
'SELECT adaid FROM ada WHERE email LIKE ' =>
10261158
array (
10271159
'error' =>

.phpunit-phpstan-dba.cache

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,66 @@
10691069
)),
10701070
),
10711071
),
1072+
'SELECT adaid FROM ada WHERE adaid IN (\'1\') AND email LIKE NULL' =>
1073+
array (
1074+
'error' => NULL,
1075+
'result' =>
1076+
array (
1077+
3 =>
1078+
PHPStan\Type\Constant\ConstantArrayType::__set_state(array(
1079+
'keyType' =>
1080+
PHPStan\Type\UnionType::__set_state(array(
1081+
'types' =>
1082+
array (
1083+
0 =>
1084+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1085+
'value' => 0,
1086+
)),
1087+
1 =>
1088+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1089+
'value' => 'adaid',
1090+
'isClassString' => false,
1091+
)),
1092+
),
1093+
)),
1094+
'itemType' =>
1095+
PHPStan\Type\IntegerRangeType::__set_state(array(
1096+
'min' => 0,
1097+
'max' => 4294967295,
1098+
)),
1099+
'allArrays' => NULL,
1100+
'keyTypes' =>
1101+
array (
1102+
0 =>
1103+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1104+
'value' => 'adaid',
1105+
'isClassString' => false,
1106+
)),
1107+
1 =>
1108+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1109+
'value' => 0,
1110+
)),
1111+
),
1112+
'valueTypes' =>
1113+
array (
1114+
0 =>
1115+
PHPStan\Type\IntegerRangeType::__set_state(array(
1116+
'min' => 0,
1117+
'max' => 4294967295,
1118+
)),
1119+
1 =>
1120+
PHPStan\Type\IntegerRangeType::__set_state(array(
1121+
'min' => 0,
1122+
'max' => 4294967295,
1123+
)),
1124+
),
1125+
'nextAutoIndex' => 1,
1126+
'optionalKeys' =>
1127+
array (
1128+
),
1129+
)),
1130+
),
1131+
),
10721132
'SELECT adaid FROM ada WHERE adaid IN (:adaids)' =>
10731133
array (
10741134
'error' =>
@@ -1081,6 +1141,78 @@
10811141
3 => NULL,
10821142
),
10831143
),
1144+
'SELECT adaid FROM ada WHERE adaid IN (:ids) AND email LIKE :time' =>
1145+
array (
1146+
'error' =>
1147+
staabm\PHPStanDba\Error::__set_state(array(
1148+
'message' => 'You have an error in your SQL syntax; check the manual that corresponds to your MySQL/MariaDB server version for the right syntax to use near \':ids) AND email LIKE :time LIMIT 0\' at line 1',
1149+
'code' => 1064,
1150+
)),
1151+
'result' =>
1152+
array (
1153+
3 => NULL,
1154+
),
1155+
),
1156+
'SELECT adaid FROM ada WHERE adaid IN (NULL) AND email LIKE NULL' =>
1157+
array (
1158+
'error' => NULL,
1159+
'result' =>
1160+
array (
1161+
3 =>
1162+
PHPStan\Type\Constant\ConstantArrayType::__set_state(array(
1163+
'keyType' =>
1164+
PHPStan\Type\UnionType::__set_state(array(
1165+
'types' =>
1166+
array (
1167+
0 =>
1168+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1169+
'value' => 0,
1170+
)),
1171+
1 =>
1172+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1173+
'value' => 'adaid',
1174+
'isClassString' => false,
1175+
)),
1176+
),
1177+
)),
1178+
'itemType' =>
1179+
PHPStan\Type\IntegerRangeType::__set_state(array(
1180+
'min' => 0,
1181+
'max' => 4294967295,
1182+
)),
1183+
'allArrays' => NULL,
1184+
'keyTypes' =>
1185+
array (
1186+
0 =>
1187+
PHPStan\Type\Constant\ConstantStringType::__set_state(array(
1188+
'value' => 'adaid',
1189+
'isClassString' => false,
1190+
)),
1191+
1 =>
1192+
PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
1193+
'value' => 0,
1194+
)),
1195+
),
1196+
'valueTypes' =>
1197+
array (
1198+
0 =>
1199+
PHPStan\Type\IntegerRangeType::__set_state(array(
1200+
'min' => 0,
1201+
'max' => 4294967295,
1202+
)),
1203+
1 =>
1204+
PHPStan\Type\IntegerRangeType::__set_state(array(
1205+
'min' => 0,
1206+
'max' => 4294967295,
1207+
)),
1208+
),
1209+
'nextAutoIndex' => 1,
1210+
'optionalKeys' =>
1211+
array (
1212+
),
1213+
)),
1214+
),
1215+
),
10841216
'SELECT adaid FROM ada WHERE email LIKE ":gesperrt%"' =>
10851217
array (
10861218
'error' => NULL,

phpstan.neon.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ parameters:
2525
-
2626
message: '#.*return type has no value type specified in iterable type iterable.#'
2727
path: tests/*
28+
-
29+
message: '#.*with no value type specified in iterable type array.#'
30+
path: tests/*
2831

2932
excludePaths:
3033
analyseAndScan:

src/QueryReflection/QuerySimulation.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
namespace staabm\PHPStanDba\QueryReflection;
66

7+
use PHPStan\Type\ArrayType;
78
use PHPStan\Type\BooleanType;
8-
use PHPStan\Type\Constant\ConstantArrayType;
99
use PHPStan\Type\ConstantScalarType;
1010
use PHPStan\Type\FloatType;
1111
use PHPStan\Type\IntegerType;
@@ -28,10 +28,8 @@ public static function simulateParamValueType(Type $paramType): ?string
2828
return (string) $paramType->getValue();
2929
}
3030

31-
if ($paramType instanceof ConstantArrayType) {
32-
$valueTypes = $paramType->getValueTypes();
33-
34-
return self::simulateParamValueType($valueTypes[0]);
31+
if ($paramType instanceof ArrayType) {
32+
return self::simulateParamValueType($paramType->getItemType());
3533
}
3634

3735
$integerType = new IntegerType();

tests/data/pdo-prepare.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,29 @@ public function arrayParam(PDO $pdo)
8181
$stmt->execute(['adaids' => [1, 2, 3]]);
8282
assertType('PDOStatement<array{adaid: int<0, 4294967295>, 0: int<0, 4294967295>}>', $stmt);
8383
}
84+
85+
public function unspecifiedArray(PDO $pdo, array $idsToUpdate, string $time)
86+
{
87+
$query = 'SELECT adaid FROM ada WHERE adaid IN (:ids) AND email LIKE :time';
88+
$stmt = $pdo->prepare($query);
89+
$stmt->execute([
90+
'ids' => $idsToUpdate,
91+
'time' => $time,
92+
]);
93+
assertType('PDOStatement<array{adaid: int<0, 4294967295>, 0: int<0, 4294967295>}>', $stmt);
94+
}
95+
96+
/**
97+
* @param int[] $idsToUpdate
98+
*/
99+
public function specifiedArray(PDO $pdo, array $idsToUpdate, string $time)
100+
{
101+
$query = 'SELECT adaid FROM ada WHERE adaid IN (:ids) AND email LIKE :time';
102+
$stmt = $pdo->prepare($query);
103+
$stmt->execute([
104+
'ids' => $idsToUpdate,
105+
'time' => $time,
106+
]);
107+
assertType('PDOStatement<array{adaid: int<0, 4294967295>, 0: int<0, 4294967295>}>', $stmt);
108+
}
84109
}

tests/data/syntax-error-in-prepared-statement.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,15 @@ public function arrayParam(Connection $connection)
174174
$connection->preparedQuery($query, ['adaids' => [1, 2, 3]]);
175175
}
176176

177+
public function noErrorInBug156(Connection $connection, array $idsToUpdate, string $time)
178+
{
179+
$query = 'UPDATE package SET indexedAt=:indexed WHERE id IN (:ids) AND (indexedAt IS NULL OR indexedAt <= crawledAt)';
180+
$connection->preparedQuery($query, [
181+
'ids' => $idsToUpdate,
182+
'indexed' => $time,
183+
]);
184+
}
185+
177186
public function noErrorInBug94(Connection $connection)
178187
{
179188
// XXX with proper sql parsing, we should better detect the placeholders and therefore could validate this query

0 commit comments

Comments
 (0)