Skip to content

Commit 3552fe3

Browse files
takaramondrejmirtes
authored andcommitted
Make key_exists be treated like array_key_exists
1 parent acbb55b commit 3552fe3

File tree

5 files changed

+81
-1
lines changed

5 files changed

+81
-1
lines changed

src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use PHPStan\Type\MixedType;
2222
use PHPStan\Type\TypeCombinator;
2323
use function count;
24+
use function in_array;
2425

2526
class ArrayKeyExistsFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
2627
{
@@ -38,7 +39,7 @@ public function isFunctionSupported(
3839
TypeSpecifierContext $context,
3940
): bool
4041
{
41-
return $functionReflection->getName() === 'array_key_exists'
42+
return in_array($functionReflection->getName(), ['array_key_exists', 'key_exists'], true)
4243
&& !$context->null();
4344
}
4445

src/Type/Php/TypeSpecifyingFunctionsDynamicReturnTypeExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
3939
{
4040
return in_array($functionReflection->getName(), [
4141
'array_key_exists',
42+
'key_exists',
4243
'in_array',
4344
'is_numeric',
4445
'is_int',

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ public function dataFileAsserts(): iterable
10111011
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6170.php');
10121012
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php');
10131013
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-key-exists.php');
1014+
yield from $this->gatherAssertTypes(__DIR__ . '/data/key-exists.php');
10141015
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7909.php');
10151016

10161017
if (PHP_VERSION_ID >= 80000) {

tests/PHPStan/Analyser/TypeSpecifierTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,54 @@ public function dataCondition(): array
954954
'$array' => '~hasOffset(\'foo\')',
955955
],
956956
],
957+
[
958+
new Expr\BinaryOp\BooleanOr(
959+
new FuncCall(new Name('key_exists'), [
960+
new Arg(new String_('foo')),
961+
new Arg(new Variable('array')),
962+
]),
963+
new FuncCall(new Name('key_exists'), [
964+
new Arg(new String_('bar')),
965+
new Arg(new Variable('array')),
966+
]),
967+
),
968+
[
969+
'$array' => 'array',
970+
],
971+
[
972+
'$array' => '~hasOffset(\'bar\')|hasOffset(\'foo\')',
973+
],
974+
],
975+
[
976+
new BooleanNot(new Expr\BinaryOp\BooleanOr(
977+
new FuncCall(new Name('key_exists'), [
978+
new Arg(new String_('foo')),
979+
new Arg(new Variable('array')),
980+
]),
981+
new FuncCall(new Name('key_exists'), [
982+
new Arg(new String_('bar')),
983+
new Arg(new Variable('array')),
984+
]),
985+
)),
986+
[
987+
'$array' => '~hasOffset(\'bar\')|hasOffset(\'foo\')',
988+
],
989+
[
990+
'$array' => 'array',
991+
],
992+
],
993+
[
994+
new FuncCall(new Name('key_exists'), [
995+
new Arg(new String_('foo')),
996+
new Arg(new Variable('array')),
997+
]),
998+
[
999+
'$array' => 'array&hasOffset(\'foo\')',
1000+
],
1001+
[
1002+
'$array' => '~hasOffset(\'foo\')',
1003+
],
1004+
],
9571005
[
9581006
new FuncCall(new Name('is_subclass_of'), [
9591007
new Arg(new Variable('string')),
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace KeyExists;
4+
5+
use function key_exists;
6+
use function PHPStan\Testing\assertType;
7+
8+
class KeyExists
9+
{
10+
/**
11+
* @param array<string, string> $a
12+
* @return void
13+
*/
14+
public function doFoo(array $a, string $key): void
15+
{
16+
assertType('false', key_exists(2, $a));
17+
assertType('bool', key_exists('foo', $a));
18+
assertType('false', key_exists('2', $a));
19+
20+
$a = ['foo' => 2, 3 => 'bar'];
21+
assertType('true', key_exists('foo', $a));
22+
assertType('true', key_exists('3', $a));
23+
assertType('false', key_exists(4, $a));
24+
25+
$empty = [];
26+
assertType('false', key_exists('foo', $empty));
27+
assertType('false', key_exists($key, $empty));
28+
}
29+
}

0 commit comments

Comments
 (0)