diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e414182..3f263997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## 2.1.0 +### Added + +- Added `notList` to check if an array is associative, with string _or_ integer keys. + ### Fixed - Corrected `@param` declaration for `isMap`. diff --git a/README.md b/README.md index 7c10111b..c7535b2b 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,7 @@ Method | Description `countBetween($array, $min, $max, $message = '')` | Check that an array has a count in the given range `isList($array, $message = '')` | Check that an array is a non-associative list `isNonEmptyList($array, $message = '')` | Check that an array is a non-associative list, and not empty +`notList($array, $message = '')` | Check that an array is associative `isMap($array, $message = '')` | Check that an array is associative and has strings as keys `isNonEmptyMap($array, $message = '')` | Check that an array is associative and has strings as keys, and is not empty diff --git a/src/Assert.php b/src/Assert.php index 20116cc1..59723417 100644 --- a/src/Assert.php +++ b/src/Assert.php @@ -1984,9 +1984,13 @@ public static function countBetween(mixed $array, mixed $min, mixed $max, string /** * @psalm-pure * - * @psalm-assert list $array + * @template T + * + * @psalm-assert list $array + * + * @param mixed|array $array * - * @psalm-return list + * @return list * * @throws InvalidArgumentException */ @@ -2018,6 +2022,32 @@ public static function isNonEmptyList(mixed $array, string $message = ''): array return $array; } + /** + * @psalm-pure + * + * @template T + * + * @psalm-assert array $array + * + * @param mixed|array $array + * + * @return array + * + * @throws InvalidArgumentException + */ + public static function notList(mixed $array, string $message = ''): array + { + static::isArray($array, $message); + + if (\array_is_list($array)) { + static::reportInvalidArgument( + $message ?: 'Expected non-list - associative array.' + ); + } + + return $array; + } + /** * @psalm-pure * @@ -2034,6 +2064,7 @@ public static function isNonEmptyList(mixed $array, string $message = ''): array public static function isMap(mixed $array, string $message = ''): array { static::isArray($array, $message); + static::allString(array_keys($array), $message); if (\count($array) > 0 && \array_is_list($array)) { static::reportInvalidArgument( diff --git a/src/Mixin.php b/src/Mixin.php index ad0aeec8..464248a8 100644 --- a/src/Mixin.php +++ b/src/Mixin.php @@ -4781,9 +4781,12 @@ public static function allNullOrCountBetween(mixed $array, mixed $min, mixed $ma /** * @psalm-pure * - * @psalm-assert list|null $array + * @template T + * @psalm-assert list|null $array + * + * @param mixed|array|null $array * - * @return list|null + * @return list|null * * @throws InvalidArgumentException */ @@ -4797,9 +4800,12 @@ public static function nullOrIsList(mixed $array, string $message = ''): mixed /** * @psalm-pure * - * @psalm-assert iterable $array + * @template T + * @psalm-assert iterable> $array + * + * @param iterable> $array * - * @return iterable + * @return iterable> * * @throws InvalidArgumentException */ @@ -4817,9 +4823,12 @@ public static function allIsList(mixed $array, string $message = ''): mixed /** * @psalm-pure * - * @psalm-assert iterable $array + * @template T + * @psalm-assert iterable|null> $array + * + * @param iterable|null> $array * - * @return iterable + * @return iterable|null> * * @throws InvalidArgumentException */ @@ -4890,6 +4899,71 @@ public static function allNullOrIsNonEmptyList(mixed $array, string $message = ' return $array; } + /** + * @psalm-pure + * + * @template T + * @psalm-assert array|null $array + * + * @param mixed|array|null $array + * + * @return array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotList(mixed $array, string $message = ''): mixed + { + null === $array || static::notList($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert iterable> $array + * + * @param iterable> $array + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allNotList(mixed $array, string $message = ''): mixed + { + static::isIterable($array); + + foreach ($array as $entry) { + static::notList($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert iterable|null> $array + * + * @param iterable|null> $array + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotList(mixed $array, string $message = ''): mixed + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::notList($entry, $message); + } + + return $array; + } + /** * @psalm-pure * diff --git a/tests/AssertTest.php b/tests/AssertTest.php index 7ec8c6fe..1339fccb 100644 --- a/tests/AssertTest.php +++ b/tests/AssertTest.php @@ -536,6 +536,10 @@ public static function getTests(): array ['isList', [[['foo' => 'bar'], ['baz' => 'tab']]], true], ['isList', [$nanList], true], ['isList', [$normalList], true], + ['notList', [[]], false], + ['notList', [[0 => 1, 2 => 3]], true], + ['notList', [['key' => 1, 'foo' => 2]], true], + ['notList', [[1, 2, 3]], false], ['isNonEmptyList', [[1, 2, 3]], true], ['isNonEmptyList', [[]], false], ['isNonEmptyList', [[0 => 1, 2 => 3]], false], @@ -546,12 +550,12 @@ public static function getTests(): array ['isNonEmptyList', [[false]], true], ['isNonEmptyList', [[[1], [2]]], true], ['isNonEmptyList', [[['foo' => 'bar'], ['baz' => 'tab']]], true], - ['isMap', [['key' => 1, 'foo' => 2]], true], - ['isMap', [[0 => 1, 2 => 3]], true], ['isMap', [[]], true], + ['isMap', [['key' => 1, 'foo' => 2]], true], + ['isMap', [[0 => 1, 2 => 3]], false], ['isMap', [[1, 2, 3]], false], ['isNonEmptyMap', [['key' => 1, 'foo' => 2]], true], - ['isNonEmptyMap', [[0 => 1, 2 => 3]], true], + ['isNonEmptyMap', [[0 => 1, 2 => 3]], false], ['isNonEmptyMap', [[]], false], ['isNonEmptyMap', [[1, 2, 3]], false], ['uuid', ['00000000-0000-0000-0000-000000000000'], true], diff --git a/tests/static-analysis/assert-notList.php b/tests/static-analysis/assert-notList.php new file mode 100644 index 00000000..d9019eb1 --- /dev/null +++ b/tests/static-analysis/assert-notList.php @@ -0,0 +1,72 @@ + + */ +function notList(mixed $value): array +{ + Assert::notList($value); + + return $value; +} + +/** + * @psalm-pure + * + * @param array $value + * + * @return array + */ +function isNotListWithKnownType(array $value): array +{ + Assert::notList($value); + + return $value; +} + +/** + * @psalm-pure + * + * @return null|array + */ +function nullOrIsNotList(mixed $value): ?array +{ + Assert::nullOrNotList($value); + + return $value; +} + +/** + * @psalm-pure + * + * @return iterable> + */ +function allNotList(mixed $value): iterable +{ + Assert::allNotList($value); + + return $value; +} + +/** + * @psalm-pure + * + * @return iterable> + */ +function allNullOrNotList(mixed $value): iterable +{ + Assert::allNullOrNotList($value); + + return $value; +}