Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ Method | Description
-------------------------------------------------- | ------------------------------------------------------------------
`keyExists($array, $key, $message = '')` | Check that a key exists in an array
`keyNotExists($array, $key, $message = '')` | Check that a key does not exist in an array
`keysExist($array, $keys, $message = '')` | Check that all the given keys exist in an array
`validArrayKey($key, $message = '')` | Check that a value is a valid array key (int or string)
`count($array, $number, $message = '')` | Check that an array contains a specific number of elements
`minCount($array, $min, $message = '')` | Check that an array contains at least a certain number of elements
Expand Down
34 changes: 34 additions & 0 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -2146,6 +2146,40 @@ public static function keyExists(mixed $array, string|int $key, string|callable
return $array;
}

/**
* @psalm-pure
*
* @param iterable<string|int> $keys
* @param string|callable():string $message
*
* @throws InvalidArgumentException
*/
public static function keysExist(mixed $array, mixed $keys, string|callable $message = ''): array
{
static::isArray($array, $message);
static::isIterable($keys);

$missing = [];

foreach ($keys as $key) {
static::validArrayKey($key, 'Expected an array key. Got: %s');

if (!(isset($array[$key]) || \array_key_exists($key, $array))) {
$missing[] = $key;
}
}

if ([] !== $missing) {
$message = self::resolveMessage($message);
static::reportInvalidArgument(\sprintf(
$message ?: 'Expected the keys %s to exist.',
\implode(', ', \array_map(static::valueToString(...), $missing))
));
}

return $array;
}

/**
* @psalm-pure
*
Expand Down
59 changes: 59 additions & 0 deletions src/Mixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -5021,6 +5021,65 @@ public static function allNullOrKeyExists(mixed $array, string|int $key, callabl
return $array;
}

/**
* @psalm-pure
*
* @param iterable<string|int> $keys
* @param string|callable():string $message
*
* @return array|null
*
* @throws InvalidArgumentException
*/
public static function nullOrKeysExist(mixed $array, mixed $keys, callable|string $message = ''): ?array
{
null === $array || static::keysExist($array, $keys, $message);

return $array;
}

/**
* @psalm-pure
*
* @param iterable<string|int> $keys
* @param string|callable():string $message
*
* @return iterable<array>
*
* @throws InvalidArgumentException
*/
public static function allKeysExist(mixed $array, mixed $keys, callable|string $message = ''): iterable
{
static::isIterable($array);

foreach ($array as $entry) {
static::keysExist($entry, $keys, $message);
}

return $array;
}

/**
* @psalm-pure
*
* @param iterable<string|int> $keys
* @param string|callable():string $message
*
* @return iterable<array|null>
*
* @throws InvalidArgumentException
*/
public static function allNullOrKeysExist(mixed $array, mixed $keys, callable|string $message = ''): iterable
{
static::isIterable($array);

foreach ($array as $entry) {
null === $entry || static::keysExist($entry, $keys, $message);
}

return $array;
}

/**
* @psalm-pure
*
Expand Down
10 changes: 10 additions & 0 deletions tests/AssertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,11 @@ public static function getTests(): array
['keyNotExists', [['key' => 0], 'key'], false],
['keyNotExists', [['key' => null], 'key'], false],
['keyNotExists', [['key' => null], 'foo'], true],
['keysExist', [['a' => 0, 'b' => null], ['a', 'b']], true],
['keysExist', [['a' => 0, 'b' => null], []], true],
['keysExist', [['a' => 0], ['a', 'b']], false],
['keysExist', [['a' => 0], ['b', 'c']], false],
['keysExist', [['key' => null], new ArrayIterator(['key'])], true],
['validArrayKey', ['abcd'], true],
['validArrayKey', [1], true],
['validArrayKey', [false], false],
Expand Down Expand Up @@ -1078,6 +1083,11 @@ public static function getMethodsThatUseOtherMethods(): array
'args' => [111, 'test', 'Value must be an array without key test. Got: %s'],
'exceptionMessage' => 'Value must be an array without key test. Got: integer',
],
[
'method' => 'keysExist',
'args' => [111, ['test'], 'Value must be an array with keys test. Got: %s'],
'exceptionMessage' => 'Value must be an array with keys test. Got: integer',
],
[
'method' => 'isInstanceOf',
'args' => [111, 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
Expand Down
49 changes: 49 additions & 0 deletions tests/static-analysis/assert-keysExist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis;

use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @param iterable<array-key> $keys
*/
function keysExist(array $array, iterable $keys): array
{
return Assert::keysExist($array, $keys);
}

/**
* @psalm-pure
*
* @param iterable<array-key> $keys
*/
function nullOrKeysExist(?array $array, iterable $keys): ?array
{
return Assert::nullOrKeysExist($array, $keys);
}

/**
* @psalm-pure
*
* @param iterable<array> $array
* @param iterable<array-key> $keys
*/
function allKeysExist(iterable $array, iterable $keys): iterable
{
return Assert::allKeysExist($array, $keys);
}

/**
* @psalm-pure
*
* @param iterable<array|null> $array
* @param iterable<array-key> $keys
*/
function allNullOrKeysExist(iterable $array, iterable $keys): iterable
{
return Assert::allNullOrKeysExist($array, $keys);
}
57 changes: 57 additions & 0 deletions tests/static-analysis/non-return/assert-keysExist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis\NonReturn;

use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @param iterable<array-key> $keys
*/
function keysExist(array $array, iterable $keys): array
{
Assert::keysExist($array, $keys);

return $array;
}

/**
* @psalm-pure
*
* @param iterable<array-key> $keys
*/
function nullOrKeysExist(?array $array, iterable $keys): ?array
{
Assert::nullOrKeysExist($array, $keys);

return $array;
}

/**
* @psalm-pure
*
* @param iterable<array> $array
* @param iterable<array-key> $keys
*/
function allKeysExist(iterable $array, iterable $keys): iterable
{
Assert::allKeysExist($array, $keys);

return $array;
}

/**
* @psalm-pure
*
* @param iterable<array|null> $array
* @param iterable<array-key> $keys
*/
function allNullOrKeysExist(iterable $array, iterable $keys): iterable
{
Assert::allNullOrKeysExist($array, $keys);

return $array;
}