Skip to content

Commit 4a57953

Browse files
committed
Fix constants allowed in parameters for multi-variant functions
1 parent 7c7411b commit 4a57953

6 files changed

Lines changed: 93 additions & 3 deletions

File tree

resources/constantToFunctionParameterMap.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,7 +2019,7 @@
20192019

20202020
'PDOStatement::fetchAll' => [
20212021
'mode' => [
2022-
'type' => 'single',
2022+
'type' => 'bitmask',
20232023
'constants' => [
20242024
'PDO::FETCH_DEFAULT',
20252025
'PDO::FETCH_LAZY',
@@ -2040,12 +2040,29 @@
20402040
'PDO::FETCH_PROPS_LATE',
20412041
'PDO::FETCH_NAMED',
20422042
],
2043+
'exclusiveGroups' => [
2044+
[
2045+
'PDO::FETCH_DEFAULT',
2046+
'PDO::FETCH_LAZY',
2047+
'PDO::FETCH_ASSOC',
2048+
'PDO::FETCH_NUM',
2049+
'PDO::FETCH_BOTH',
2050+
'PDO::FETCH_OBJ',
2051+
'PDO::FETCH_BOUND',
2052+
'PDO::FETCH_COLUMN',
2053+
'PDO::FETCH_CLASS',
2054+
'PDO::FETCH_INTO',
2055+
'PDO::FETCH_FUNC',
2056+
'PDO::FETCH_KEY_PAIR',
2057+
'PDO::FETCH_NAMED',
2058+
],
2059+
],
20432060
],
20442061
],
20452062

20462063
'PDOStatement::setFetchMode' => [
20472064
'mode' => [
2048-
'type' => 'single',
2065+
'type' => 'bitmask',
20492066
'constants' => [
20502067
'PDO::FETCH_DEFAULT',
20512068
'PDO::FETCH_LAZY',
@@ -2066,6 +2083,23 @@
20662083
'PDO::FETCH_PROPS_LATE',
20672084
'PDO::FETCH_NAMED',
20682085
],
2086+
'exclusiveGroups' => [
2087+
[
2088+
'PDO::FETCH_DEFAULT',
2089+
'PDO::FETCH_LAZY',
2090+
'PDO::FETCH_ASSOC',
2091+
'PDO::FETCH_NUM',
2092+
'PDO::FETCH_BOTH',
2093+
'PDO::FETCH_OBJ',
2094+
'PDO::FETCH_BOUND',
2095+
'PDO::FETCH_COLUMN',
2096+
'PDO::FETCH_CLASS',
2097+
'PDO::FETCH_INTO',
2098+
'PDO::FETCH_FUNC',
2099+
'PDO::FETCH_KEY_PAIR',
2100+
'PDO::FETCH_NAMED',
2101+
],
2102+
],
20692103
],
20702104
],
20712105

src/Reflection/ParameterAllowedConstants.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ public function getExclusiveGroups(): array
4747
return $this->exclusiveGroups;
4848
}
4949

50+
public function equals(self $other): bool
51+
{
52+
return $this->type === $other->type
53+
&& $this->constants === $other->constants
54+
&& $this->exclusiveGroups === $other->exclusiveGroups;
55+
}
56+
5057
/**
5158
* @param list<ConstantReflection> $constants
5259
*/

src/Reflection/ParametersAcceptorSelector.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,14 @@ public static function combineAcceptors(array $acceptors): ExtendedParametersAcc
812812
$closureThisType = null;
813813
}
814814

815+
$allowedConstants = $parameters[$i]->getAllowedConstants();
816+
if ($allowedConstants !== null) {
817+
$otherAllowedConstants = $parameter instanceof ExtendedParameterReflection ? $parameter->getAllowedConstants() : null;
818+
if ($otherAllowedConstants === null || !$allowedConstants->equals($otherAllowedConstants)) {
819+
$allowedConstants = null;
820+
}
821+
}
822+
815823
$parameters[$i] = new ExtendedDummyParameter(
816824
$parameters[$i]->getName() !== $parameter->getName() ? sprintf('%s|%s', $parameters[$i]->getName(), $parameter->getName()) : $parameter->getName(),
817825
$type,
@@ -825,7 +833,7 @@ public static function combineAcceptors(array $acceptors): ExtendedParametersAcc
825833
$immediatelyInvokedCallable,
826834
$closureThisType,
827835
$attributes,
828-
null,
836+
$allowedConstants,
829837
);
830838

831839
if ($isVariadic) {

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,14 @@ public function testCallInterfaceMethods(): void
969969
]);
970970
}
971971

972+
public function testBug14719(): void
973+
{
974+
$this->checkThisOnly = false;
975+
$this->checkNullables = true;
976+
$this->checkUnionTypes = true;
977+
$this->analyse([__DIR__ . '/data/bug-14719.php'], []);
978+
}
979+
972980
public function testClosureBind(): void
973981
{
974982
$this->checkThisOnly = false;
@@ -4010,6 +4018,14 @@ public function testConstantParameterCheckMethods(): void
40104018
'Constant Collator::FRENCH_COLLATION is not allowed for parameter #2 $flags of method Collator::sort().',
40114019
25,
40124020
],
4021+
[
4022+
'Constant PDO::ATTR_ERRMODE is not allowed for parameter $mode of method PDOStatement::fetch().',
4023+
28,
4024+
],
4025+
[
4026+
'Constants PDO::FETCH_ASSOC, PDO::FETCH_NUM cannot be combined for parameter $mode of method PDOStatement::setFetchMode().',
4027+
31,
4028+
],
40134029
]);
40144030
}
40154031

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Bug14719;
4+
5+
use PDO;
6+
7+
function (): void {
8+
$pdo = new PDO(
9+
dsn: "mysql:host=servername;dbname=dbname;charset=utf8mb4",
10+
username: "username",
11+
password: "password"
12+
);
13+
14+
$db_stmt = $pdo->prepare("SELECT * FROM table;");
15+
$db_stmt->fetchAll(\PDO::FETCH_GROUP | \PDO::FETCH_ASSOC);
16+
$db_stmt->fetchAll(mode: \PDO::FETCH_GROUP | \PDO::FETCH_ASSOC);
17+
$db_stmt->setFetchMode(\PDO::FETCH_GROUP | \PDO::FETCH_ASSOC);
18+
$db_stmt->setFetchMode(mode: \PDO::FETCH_GROUP | \PDO::FETCH_ASSOC);
19+
};

tests/PHPStan/Rules/Methods/data/constant-parameter-check-methods.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@
2323

2424
// Collator::sort - wrong class constant
2525
$collator->sort($arr, \Collator::FRENCH_COLLATION);
26+
27+
// PDOStatement::fetch - wrong class constant via named argument (multi-variant method)
28+
$stmt->fetch(mode: \PDO::ATTR_ERRMODE);
29+
30+
// PDOStatement::setFetchMode - exclusive base modes via named argument (multi-variant method)
31+
$stmt->setFetchMode(mode: \PDO::FETCH_ASSOC | \PDO::FETCH_NUM);

0 commit comments

Comments
 (0)