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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 33 additions & 2 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> $array
*
* @param mixed|array<array-key, T> $array
*
* @psalm-return list
* @return list<T>
*
* @throws InvalidArgumentException
*/
Expand Down Expand Up @@ -2018,6 +2022,32 @@ public static function isNonEmptyList(mixed $array, string $message = ''): array
return $array;
}

/**
* @psalm-pure
*
* @template T
*
* @psalm-assert array<array-key, T> $array
*
* @param mixed|array<array-key, T> $array
*
* @return array<array-key, T>
*
* @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
*
Expand All @@ -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(
Expand Down
86 changes: 80 additions & 6 deletions src/Mixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>|null $array
*
* @param mixed|array<array-key, T>|null $array
*
* @return list|null
* @return list<T>|null
*
* @throws InvalidArgumentException
*/
Expand All @@ -4797,9 +4800,12 @@ public static function nullOrIsList(mixed $array, string $message = ''): mixed
/**
* @psalm-pure
*
* @psalm-assert iterable<list> $array
* @template T
* @psalm-assert iterable<list<T>> $array
*
* @param iterable<mixed|array<array-key, T>> $array
*
* @return iterable<list>
* @return iterable<list<T>>
*
* @throws InvalidArgumentException
*/
Expand All @@ -4817,9 +4823,12 @@ public static function allIsList(mixed $array, string $message = ''): mixed
/**
* @psalm-pure
*
* @psalm-assert iterable<list|null> $array
* @template T
* @psalm-assert iterable<list<T>|null> $array
*
* @param iterable<mixed|array<array-key, T>|null> $array
*
* @return iterable<list|null>
* @return iterable<list<T>|null>
*
* @throws InvalidArgumentException
*/
Expand Down Expand Up @@ -4890,6 +4899,71 @@ public static function allNullOrIsNonEmptyList(mixed $array, string $message = '
return $array;
}

/**
* @psalm-pure
*
* @template T
* @psalm-assert array<array-key, T>|null $array
*
* @param mixed|array<array-key, T>|null $array
*
* @return array<array-key, T>|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<array-key, T>> $array
*
* @param iterable<mixed|array<array-key, T>> $array
*
* @return iterable<array<array-key, T>>
*
* @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<array<array-key, T>|null> $array
*
* @param iterable<mixed|array<array-key, T>|null> $array
*
* @return iterable<array<array-key, T>|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
*
Expand Down
10 changes: 7 additions & 3 deletions tests/AssertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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],
Expand Down
72 changes: 72 additions & 0 deletions tests/static-analysis/assert-notList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis;

use stdClass;
use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @param mixed $value
*
* @return array<array-key, mixed>
*/
function notList(mixed $value): array
{
Assert::notList($value);

return $value;
}

/**
* @psalm-pure
*
* @param array<array-key, stdClass> $value
*
* @return array<array-key, stdClass>
*/
function isNotListWithKnownType(array $value): array
{
Assert::notList($value);

return $value;
}

/**
* @psalm-pure
*
* @return null|array<array-key, mixed>
*/
function nullOrIsNotList(mixed $value): ?array
{
Assert::nullOrNotList($value);

return $value;
}

/**
* @psalm-pure
*
* @return iterable<array-key, array<array-key, mixed>>
*/
function allNotList(mixed $value): iterable
{
Assert::allNotList($value);

return $value;
}

/**
* @psalm-pure
*
* @return iterable<array-key, null|array<array-key, mixed>>
*/
function allNullOrNotList(mixed $value): iterable
{
Assert::allNullOrNotList($value);

return $value;
}