Skip to content

Commit d807830

Browse files
committed
Merge branch 2.2.x into 2.3.x
2 parents c230eb0 + d88544a commit d807830

9 files changed

Lines changed: 405 additions & 5 deletions

‎resources/functionMap.php‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7792,7 +7792,7 @@
77927792
'pg_copy_from' => ['bool', 'connection'=>'resource', 'table_name'=>'string', 'rows'=>'array', 'delimiter='=>'string', 'null_as='=>'string'],
77937793
'pg_copy_to' => ['array|false', 'connection'=>'resource', 'table_name'=>'string', 'delimiter='=>'string', 'null_as='=>'string'],
77947794
'pg_dbname' => ['string', 'connection='=>'resource'],
7795-
'pg_delete' => ['mixed', 'db'=>'resource', 'table'=>'string', 'ids'=>'array', 'options='=>'int'],
7795+
'pg_delete' => ['string|bool', 'db'=>'resource', 'table'=>'string', 'ids'=>'array', 'options='=>'int'],
77967796
'pg_end_copy' => ['bool', 'connection='=>'resource'],
77977797
'pg_escape_bytea' => ['string', 'connection'=>'resource', 'data'=>'string'],
77987798
'pg_escape_bytea\'1' => ['string', 'data'=>'string'],
@@ -7829,7 +7829,7 @@
78297829
'pg_get_pid' => ['int|false', 'connection'=>'resource'],
78307830
'pg_get_result' => ['resource|false', 'connection='=>'resource'],
78317831
'pg_host' => ['string', 'connection='=>'resource'],
7832-
'pg_insert' => ['mixed', 'db'=>'resource', 'table'=>'string', 'values'=>'array', 'options='=>'int'],
7832+
'pg_insert' => ['resource|string|bool', 'db'=>'resource', 'table'=>'string', 'values'=>'array', 'options='=>'int'],
78337833
'pg_last_error' => ['string', 'connection='=>'resource', 'operation='=>'int'],
78347834
'pg_last_notice' => ['string', 'connection'=>'resource', 'option='=>'int'],
78357835
'pg_last_oid' => ['string|false', 'result'=>'resource'],
@@ -7867,8 +7867,8 @@
78677867
'pg_result_error' => ['string|false', 'result'=>'resource'],
78687868
'pg_result_error_field' => ['string|false|null', 'result'=>'resource', 'fieldcode'=>'int'],
78697869
'pg_result_seek' => ['bool', 'result'=>'resource', 'offset'=>'int'],
7870-
'pg_result_status' => ['mixed', 'result'=>'resource', 'result_type='=>'int'],
7871-
'pg_select' => ['mixed', 'db'=>'resource', 'table'=>'string', 'ids'=>'array', 'options='=>'int', 'result_type='=>'int'],
7870+
'pg_result_status' => ['string|int', 'result'=>'resource', 'result_type='=>'int'],
7871+
'pg_select' => ['array<int, array>|string|false', 'db'=>'resource', 'table'=>'string', 'ids'=>'array', 'options='=>'int', 'result_type='=>'int'],
78727872
'pg_send_execute' => ['bool', 'connection'=>'resource', 'stmtname'=>'string', 'params'=>'array'],
78737873
'pg_send_prepare' => ['bool', 'connection'=>'resource', 'stmtname'=>'string', 'query'=>'string'],
78747874
'pg_send_query' => ['bool', 'connection'=>'resource', 'query'=>'string'],
@@ -7885,7 +7885,7 @@
78857885
'pg_unescape_bytea' => ['string', 'data'=>'string'],
78867886
'pg_untrace' => ['bool', 'connection='=>'resource'],
78877887
'pg_untrace\'1' => ['bool'],
7888-
'pg_update' => ['mixed', 'db'=>'resource', 'table'=>'string', 'fields'=>'array', 'ids'=>'array', 'options='=>'int'],
7888+
'pg_update' => ['string|bool', 'db'=>'resource', 'table'=>'string', 'fields'=>'array', 'ids'=>'array', 'options='=>'int'],
78897889
'pg_version' => ['array', 'connection='=>'resource'],
78907890
'Phar::__construct' => ['void', 'fname'=>'string', 'flags='=>'int', 'alias='=>'string'],
78917891
'Phar::addEmptyDir' => ['', 'dirname'=>'string'],

‎resources/functionMap_php81delta.php‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
'UnitEnum::cases' => ['list<static>'],
3131
],
3232
'old' => [
33+
'pg_delete' => ['string|bool', 'db'=>'resource', 'table'=>'string', 'ids'=>'array', 'options='=>'int'],
3334
'pg_escape_bytea' => ['string', 'connection'=>'resource', 'data'=>'string'],
3435
'pg_escape_bytea\'1' => ['string', 'data'=>'string'],
3536
'pg_escape_identifier' => ['string|false', 'connection'=>'resource', 'data'=>'string'],
@@ -48,6 +49,7 @@
4849
'pg_field_is_null\'1' => ['int', 'result'=>'', 'row'=>'int', 'field_name_or_number'=>'string|int'],
4950
'pg_field_prtlen' => ['int|false', 'result'=>'', 'field_name_or_number'=>''],
5051
'pg_field_prtlen\'1' => ['int', 'result'=>'', 'row'=>'int', 'field_name_or_number'=>'string|int'],
52+
'pg_insert' => ['resource|string|bool', 'db'=>'resource', 'table'=>'string', 'values'=>'array', 'options='=>'int'],
5153
'pg_lo_export' => ['bool', 'connection'=>'resource', 'oid'=>'int', 'filename'=>'string'],
5254
'pg_lo_export\'1' => ['bool', 'oid'=>'int', 'pathname'=>'string'],
5355
'pg_lo_import' => ['int|string|false', 'connection'=>'resource', 'pathname'=>'string', 'oid'=>''],
@@ -62,6 +64,7 @@
6264
'pg_query\'1' => ['resource|false', 'query'=>'string'],
6365
'pg_query_params' => ['resource|false', 'connection'=>'resource', 'query'=>'string', 'params'=>'array'],
6466
'pg_query_params\'1' => ['resource|false', 'query'=>'string', 'params'=>'array'],
67+
'pg_select' => ['array<int, array>|string|false', 'db'=>'resource', 'table'=>'string', 'ids'=>'array', 'options='=>'int', 'result_type='=>'int'],
6568
'pg_set_client_encoding' => ['int', 'connection'=>'resource', 'encoding'=>'string'],
6669
'pg_set_client_encoding\'1' => ['int', 'encoding'=>'string'],
6770
'pg_set_error_verbosity' => ['int|false', 'connection'=>'resource', 'verbosity'=>'int'],
@@ -70,5 +73,6 @@
7073
'pg_tty\'1' => ['string'],
7174
'pg_untrace' => ['bool', 'connection='=>'resource'],
7275
'pg_untrace\'1' => ['bool'],
76+
'pg_update' => ['string|bool', 'db'=>'resource', 'table'=>'string', 'fields'=>'array', 'ids'=>'array', 'options='=>'int'],
7377
]
7478
];
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\AutowiredService;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\ParametersAcceptorSelector;
10+
use PHPStan\TrinaryLogic;
11+
use PHPStan\Type\BitwiseFlagHelper;
12+
use PHPStan\Type\BooleanType;
13+
use PHPStan\Type\Constant\ConstantBooleanType;
14+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
15+
use PHPStan\Type\StringType;
16+
use PHPStan\Type\Type;
17+
use PHPStan\Type\TypeCombinator;
18+
use PHPStan\Type\UnionType;
19+
use function array_key_exists;
20+
21+
#[AutowiredService]
22+
final class PgDmlDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
23+
{
24+
25+
/** @var array<string, int> */
26+
private array $flagsArgPositions = [
27+
'pg_insert' => 3,
28+
'pg_update' => 4,
29+
'pg_delete' => 3,
30+
'pg_select' => 3,
31+
];
32+
33+
public function __construct(private BitwiseFlagHelper $bitwiseFlagAnalyser)
34+
{
35+
}
36+
37+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
38+
{
39+
return array_key_exists($functionReflection->getName(), $this->flagsArgPositions);
40+
}
41+
42+
public function getTypeFromFunctionCall(
43+
FunctionReflection $functionReflection,
44+
FuncCall $functionCall,
45+
Scope $scope,
46+
): ?Type
47+
{
48+
$functionName = $functionReflection->getName();
49+
$args = $functionCall->getArgs();
50+
$getDefaultReturnType = static fn (): Type => ParametersAcceptorSelector::selectFromArgs(
51+
$scope,
52+
$args,
53+
$functionReflection->getVariants(),
54+
)->getReturnType();
55+
56+
$flagsArgPosition = $this->flagsArgPositions[$functionName];
57+
if (!isset($args[$flagsArgPosition])) {
58+
// default flags are PGSQL_DML_EXEC
59+
return $this->getTypeFromFlags($functionName, TrinaryLogic::createNo(), TrinaryLogic::createYes(), $getDefaultReturnType);
60+
}
61+
62+
$flagsExpr = $args[$flagsArgPosition]->value;
63+
64+
return $this->getTypeFromFlags(
65+
$functionName,
66+
$this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($flagsExpr, $scope, 'PGSQL_DML_STRING'),
67+
$this->bitwiseFlagAnalyser->bitwiseOrContainsConstant($flagsExpr, $scope, 'PGSQL_DML_EXEC'),
68+
$getDefaultReturnType,
69+
);
70+
}
71+
72+
/**
73+
* @param callable(): Type $getDefaultReturnType
74+
*/
75+
private function getTypeFromFlags(string $functionName, TrinaryLogic $containsString, TrinaryLogic $containsExec, callable $getDefaultReturnType): ?Type
76+
{
77+
if ($functionName === 'pg_insert') {
78+
// with PGSQL_DML_EXEC the result object is returned even when PGSQL_DML_STRING is also set
79+
if ($containsExec->yes()) {
80+
return TypeCombinator::remove(
81+
$getDefaultReturnType(),
82+
new UnionType([new StringType(), new ConstantBooleanType(true)]),
83+
);
84+
}
85+
if (!$containsExec->no()) {
86+
return null;
87+
}
88+
if ($containsString->yes()) {
89+
return new UnionType([new StringType(), new ConstantBooleanType(false)]);
90+
}
91+
if ($containsString->no()) {
92+
return new BooleanType();
93+
}
94+
95+
return null;
96+
}
97+
98+
if ($containsString->yes()) {
99+
return new UnionType([new StringType(), new ConstantBooleanType(false)]);
100+
}
101+
if (!$containsString->no()) {
102+
return null;
103+
}
104+
105+
return TypeCombinator::remove($getDefaultReturnType(), new StringType());
106+
}
107+
108+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\AutowiredService;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Type\Accessory\AccessoryArrayListType;
10+
use PHPStan\Type\ArrayType;
11+
use PHPStan\Type\Constant\ConstantBooleanType;
12+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
13+
use PHPStan\Type\IntegerType;
14+
use PHPStan\Type\StringType;
15+
use PHPStan\Type\Type;
16+
use PHPStan\Type\TypeCombinator;
17+
18+
#[AutowiredService]
19+
final class PgLastNoticeDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
20+
{
21+
22+
private const NOTICE_LAST = 1;
23+
private const NOTICE_ALL = 2;
24+
private const NOTICE_CLEAR = 3;
25+
26+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
27+
{
28+
return $functionReflection->getName() === 'pg_last_notice';
29+
}
30+
31+
public function getTypeFromFunctionCall(
32+
FunctionReflection $functionReflection,
33+
FuncCall $functionCall,
34+
Scope $scope,
35+
): Type
36+
{
37+
$args = $functionCall->getArgs();
38+
if (!isset($args[1])) {
39+
return new StringType();
40+
}
41+
42+
$types = [];
43+
foreach ($scope->getType($args[1]->value)->getConstantScalarValues() as $value) {
44+
if ($value === self::NOTICE_LAST) {
45+
$types[] = new StringType();
46+
} elseif ($value === self::NOTICE_ALL) {
47+
$types[] = $this->createAllNoticesType();
48+
} elseif ($value === self::NOTICE_CLEAR) {
49+
$types[] = new ConstantBooleanType(true);
50+
} else {
51+
return $this->createUnknownModeType();
52+
}
53+
}
54+
55+
if ($types === []) {
56+
return $this->createUnknownModeType();
57+
}
58+
59+
return TypeCombinator::union(...$types);
60+
}
61+
62+
private function createAllNoticesType(): Type
63+
{
64+
return TypeCombinator::intersect(
65+
new ArrayType(new IntegerType(), new StringType()),
66+
new AccessoryArrayListType(),
67+
);
68+
}
69+
70+
private function createUnknownModeType(): Type
71+
{
72+
return TypeCombinator::union(
73+
new StringType(),
74+
$this->createAllNoticesType(),
75+
new ConstantBooleanType(true),
76+
);
77+
}
78+
79+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\DependencyInjection\AutowiredService;
8+
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
10+
use PHPStan\Type\IntegerType;
11+
use PHPStan\Type\StringType;
12+
use PHPStan\Type\Type;
13+
use PHPStan\Type\TypeCombinator;
14+
15+
#[AutowiredService]
16+
final class PgResultStatusDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
17+
{
18+
19+
private const STATUS_LONG = 1;
20+
private const STATUS_STRING = 2;
21+
22+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
23+
{
24+
return $functionReflection->getName() === 'pg_result_status';
25+
}
26+
27+
public function getTypeFromFunctionCall(
28+
FunctionReflection $functionReflection,
29+
FuncCall $functionCall,
30+
Scope $scope,
31+
): ?Type
32+
{
33+
$args = $functionCall->getArgs();
34+
if (!isset($args[1])) {
35+
return new IntegerType();
36+
}
37+
38+
$types = [];
39+
foreach ($scope->getType($args[1]->value)->getConstantScalarValues() as $value) {
40+
if ($value === self::STATUS_LONG) {
41+
$types[] = new IntegerType();
42+
} elseif ($value === self::STATUS_STRING) {
43+
$types[] = new StringType();
44+
} else {
45+
return null;
46+
}
47+
}
48+
49+
if ($types === []) {
50+
return null;
51+
}
52+
53+
return TypeCombinator::union(...$types);
54+
}
55+
56+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php // lint < 8.1
2+
3+
namespace PgDmlResource;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param resource $connection
12+
*/
13+
public function doInsert($connection, int $flags): void
14+
{
15+
assertType('resource|false', pg_insert($connection, 'table', []));
16+
assertType('resource|false', pg_insert($connection, 'table', [], PGSQL_DML_EXEC));
17+
assertType('resource|false', pg_insert($connection, 'table', [], PGSQL_DML_EXEC | PGSQL_DML_STRING));
18+
assertType('string|false', pg_insert($connection, 'table', [], PGSQL_DML_STRING));
19+
assertType('string|false', pg_insert($connection, 'table', [], PGSQL_DML_NO_CONV | PGSQL_DML_STRING));
20+
assertType('bool', pg_insert($connection, 'table', [], PGSQL_DML_ASYNC));
21+
assertType('bool|resource|string', pg_insert($connection, 'table', [], $flags));
22+
}
23+
24+
/**
25+
* @param resource $connection
26+
*/
27+
public function doUpdate($connection, int $flags): void
28+
{
29+
assertType('bool', pg_update($connection, 'table', [], []));
30+
assertType('bool', pg_update($connection, 'table', [], [], PGSQL_DML_EXEC));
31+
assertType('string|false', pg_update($connection, 'table', [], [], PGSQL_DML_STRING));
32+
assertType('string|false', pg_update($connection, 'table', [], [], PGSQL_DML_EXEC | PGSQL_DML_STRING));
33+
assertType('bool|string', pg_update($connection, 'table', [], [], $flags));
34+
}
35+
36+
/**
37+
* @param resource $connection
38+
*/
39+
public function doDelete($connection, int $flags): void
40+
{
41+
assertType('bool', pg_delete($connection, 'table', []));
42+
assertType('bool', pg_delete($connection, 'table', [], PGSQL_DML_EXEC));
43+
assertType('string|false', pg_delete($connection, 'table', [], PGSQL_DML_STRING));
44+
assertType('bool|string', pg_delete($connection, 'table', [], $flags));
45+
}
46+
47+
/**
48+
* @param resource $connection
49+
*/
50+
public function doSelect($connection, int $flags): void
51+
{
52+
assertType('array<int, array>|false', pg_select($connection, 'table', []));
53+
assertType('array<int, array>|false', pg_select($connection, 'table', [], PGSQL_DML_EXEC));
54+
assertType('string|false', pg_select($connection, 'table', [], PGSQL_DML_STRING));
55+
assertType('array<int, array>|string|false', pg_select($connection, 'table', [], $flags));
56+
}
57+
58+
}

0 commit comments

Comments
 (0)