Skip to content

Commit

Permalink
Make key_exists be treated like array_key_exists
Browse files Browse the repository at this point in the history
  • Loading branch information
takaram authored and ondrejmirtes committed Oct 7, 2022
1 parent acbb55b commit 3552fe3
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 1 deletion.
Expand Up @@ -21,6 +21,7 @@
use PHPStan\Type\MixedType;
use PHPStan\Type\TypeCombinator;
use function count;
use function in_array;

class ArrayKeyExistsFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
{
Expand All @@ -38,7 +39,7 @@ public function isFunctionSupported(
TypeSpecifierContext $context,
): bool
{
return $functionReflection->getName() === 'array_key_exists'
return in_array($functionReflection->getName(), ['array_key_exists', 'key_exists'], true)
&& !$context->null();
}

Expand Down
Expand Up @@ -39,6 +39,7 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
{
return in_array($functionReflection->getName(), [
'array_key_exists',
'key_exists',
'in_array',
'is_numeric',
'is_int',
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -1011,6 +1011,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6170.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-key-exists.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/key-exists.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7909.php');

if (PHP_VERSION_ID >= 80000) {
Expand Down
48 changes: 48 additions & 0 deletions tests/PHPStan/Analyser/TypeSpecifierTest.php
Expand Up @@ -954,6 +954,54 @@ public function dataCondition(): array
'$array' => '~hasOffset(\'foo\')',
],
],
[
new Expr\BinaryOp\BooleanOr(
new FuncCall(new Name('key_exists'), [
new Arg(new String_('foo')),
new Arg(new Variable('array')),
]),
new FuncCall(new Name('key_exists'), [
new Arg(new String_('bar')),
new Arg(new Variable('array')),
]),
),
[
'$array' => 'array',
],
[
'$array' => '~hasOffset(\'bar\')|hasOffset(\'foo\')',
],
],
[
new BooleanNot(new Expr\BinaryOp\BooleanOr(
new FuncCall(new Name('key_exists'), [
new Arg(new String_('foo')),
new Arg(new Variable('array')),
]),
new FuncCall(new Name('key_exists'), [
new Arg(new String_('bar')),
new Arg(new Variable('array')),
]),
)),
[
'$array' => '~hasOffset(\'bar\')|hasOffset(\'foo\')',
],
[
'$array' => 'array',
],
],
[
new FuncCall(new Name('key_exists'), [
new Arg(new String_('foo')),
new Arg(new Variable('array')),
]),
[
'$array' => 'array&hasOffset(\'foo\')',
],
[
'$array' => '~hasOffset(\'foo\')',
],
],
[
new FuncCall(new Name('is_subclass_of'), [
new Arg(new Variable('string')),
Expand Down
29 changes: 29 additions & 0 deletions tests/PHPStan/Analyser/data/key-exists.php
@@ -0,0 +1,29 @@
<?php

namespace KeyExists;

use function key_exists;
use function PHPStan\Testing\assertType;

class KeyExists
{
/**
* @param array<string, string> $a
* @return void
*/
public function doFoo(array $a, string $key): void
{
assertType('false', key_exists(2, $a));
assertType('bool', key_exists('foo', $a));
assertType('false', key_exists('2', $a));

$a = ['foo' => 2, 3 => 'bar'];
assertType('true', key_exists('foo', $a));
assertType('true', key_exists('3', $a));
assertType('false', key_exists(4, $a));

$empty = [];
assertType('false', key_exists('foo', $empty));
assertType('false', key_exists($key, $empty));
}
}

0 comments on commit 3552fe3

Please sign in to comment.