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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## Unreleased

### Added

- Added `isListOf` assertion.
- Added `isNonEmptyListOf` assertion.

## 2.4.1

### Fixed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ 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
`isListOf($array, $class, $message = '')` | Check that an array is a non-associative list of instances of a given class
`isNonEmptyListOf($array, $class, $message = '')` | Check that an array is a non-associative list of instances of a given class, and not empty
`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
47 changes: 47 additions & 0 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -2328,6 +2328,53 @@ public static function isNonEmptyList(mixed $array, string|callable $message = '
return $array;
}

/**
* @psalm-pure
*
* @template T of object
*
* @psalm-assert list<T> $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return list<T>
*
* @throws InvalidArgumentException
*/
public static function isListOf(mixed $array, mixed $class, string|callable $message = ''): array
{
static::isList($array, $message);

foreach ($array as $entry) {
static::isInstanceOf($entry, $class, $message);
}

return $array;
}

/**
* @psalm-pure
*
* @template T of object
*
* @psalm-assert non-empty-list<T> $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return non-empty-list<T>
*
* @throws InvalidArgumentException
*/
public static function isNonEmptyListOf(mixed $array, mixed $class, string|callable $message = ''): array
{
static::isListOf($array, $class, $message);
static::notEmpty($array, $message);

return $array;
}

/**
* @psalm-pure
*
Expand Down
8 changes: 8 additions & 0 deletions src/HasAssert.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ final class HasAssert
'nullorisnonemptylist' => true,
'allisnonemptylist' => true,
'allnullorisnonemptylist' => true,
'islistof' => true,
'nullorislistof' => true,
'allislistof' => true,
'allnullorislistof' => true,
'isnonemptylistof' => true,
'nullorisnonemptylistof' => true,
'allisnonemptylistof' => true,
'allnullorisnonemptylistof' => true,
'ismap' => true,
'nullorismap' => true,
'allismap' => true,
Expand Down
136 changes: 136 additions & 0 deletions src/Mixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -5466,6 +5466,142 @@ public static function allNullOrIsNonEmptyList(mixed $array, callable|string $me
return $array;
}

/**
* @psalm-pure
*
* @template T of object
* @psalm-assert list<T>|null $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return list<T>|null
*
* @throws InvalidArgumentException
*/
public static function nullOrIsListOf(mixed $array, mixed $class, callable|string $message = ''): ?array
{
null === $array || static::isListOf($array, $class, $message);

return $array;
}

/**
* @psalm-pure
*
* @template T of object
* @psalm-assert iterable<list<T>> $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return iterable<list<T>>
*
* @throws InvalidArgumentException
*/
public static function allIsListOf(mixed $array, mixed $class, callable|string $message = ''): iterable
{
static::isIterable($array);

foreach ($array as $entry) {
static::isListOf($entry, $class, $message);
}

return $array;
}

/**
* @psalm-pure
*
* @template T of object|null
* @psalm-assert iterable<list<T>|null> $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return iterable<list<T>|null>
*
* @throws InvalidArgumentException
*/
public static function allNullOrIsListOf(mixed $array, mixed $class, callable|string $message = ''): iterable
{
static::isIterable($array);

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

return $array;
}

/**
* @psalm-pure
*
* @template T of object
* @psalm-assert non-empty-list<T>|null $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return non-empty-list<T>|null
*
* @throws InvalidArgumentException
*/
public static function nullOrIsNonEmptyListOf(mixed $array, mixed $class, callable|string $message = ''): ?array
{
null === $array || static::isNonEmptyListOf($array, $class, $message);

return $array;
}

/**
* @psalm-pure
*
* @template T of object
* @psalm-assert iterable<non-empty-list<T>> $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return iterable<non-empty-list<T>>
*
* @throws InvalidArgumentException
*/
public static function allIsNonEmptyListOf(mixed $array, mixed $class, callable|string $message = ''): iterable
{
static::isIterable($array);

foreach ($array as $entry) {
static::isNonEmptyListOf($entry, $class, $message);
}

return $array;
}

/**
* @psalm-pure
*
* @template T of object|null
* @psalm-assert iterable<non-empty-list<T>|null> $array
*
* @param class-string<T> $class
* @param string|callable():string $message
*
* @return iterable<non-empty-list<T>|null>
*
* @throws InvalidArgumentException
*/
public static function allNullOrIsNonEmptyListOf(mixed $array, mixed $class, callable|string $message = ''): iterable
{
static::isIterable($array);

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

return $array;
}

/**
* @psalm-pure
*
Expand Down
21 changes: 21 additions & 0 deletions tests/AssertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,17 @@ public static function getTests(): array
['isNonEmptyList', [[false]], true],
['isNonEmptyList', [[[1], [2]]], true],
['isNonEmptyList', [[['foo' => 'bar'], ['baz' => 'tab']]], true],
['isListOf', [[new stdClass(), new stdClass()], 'stdClass'], true],
['isListOf', [[], 'stdClass'], true],
['isListOf', [[new stdClass(), new Exception()], 'stdClass'], false],
['isListOf', [['foo' => new stdClass()], 'stdClass'], false],
['isListOf', [[0 => new stdClass(), 2 => new stdClass()], 'stdClass'], false],
['isListOf', [true, 'stdClass'], false],
['isNonEmptyListOf', [[new stdClass(), new stdClass()], 'stdClass'], true],
['isNonEmptyListOf', [[], 'stdClass'], false],
['isNonEmptyListOf', [[new stdClass(), new Exception()], 'stdClass'], false],
['isNonEmptyListOf', [['foo' => new stdClass()], 'stdClass'], false],
['isNonEmptyListOf', [true, 'stdClass'], false],
['isMap', [['key' => 1, 'foo' => 2]], true],
['isMap', [[0 => 1, 2 => 3]], true],
['isMap', [[]], true],
Expand Down Expand Up @@ -1083,6 +1094,16 @@ public static function getMethodsThatUseOtherMethods(): array
'args' => [111, 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'isListOf',
'args' => [[111], 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'isNonEmptyListOf',
'args' => [[111], 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
'exceptionMessage' => 'Value must be an instance of stdClass. Got: integer',
],
[
'method' => 'notInstanceOf',
'args' => [111, 'stdClass', 'Value must be an instance of stdClass. Got: %s'],
Expand Down
44 changes: 44 additions & 0 deletions tests/static-analysis/assert-isListOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis;

use stdClass;
use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @return list<stdClass>
*/
function isListOf(mixed $value): array
{
return Assert::isListOf($value, stdClass::class);
}

/**
* @psalm-pure
*
* @return null|list<stdClass>
*/
function nullOrIsListOf(mixed $value): ?array
{
return Assert::nullOrIsListOf($value, stdClass::class);
}

/**
* @psalm-pure
*/
function allIsListOf(mixed $value): iterable
{
return Assert::allIsListOf($value, stdClass::class);
}

/**
* @psalm-pure
*/
function allNullOrIsListOf(mixed $value): iterable
{
return Assert::allNullOrIsListOf($value, stdClass::class);
}
44 changes: 44 additions & 0 deletions tests/static-analysis/assert-isNonEmptyListOf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis;

use stdClass;
use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @return non-empty-list<stdClass>
*/
function isNonEmptyListOf(mixed $value): array
{
return Assert::isNonEmptyListOf($value, stdClass::class);
}

/**
* @psalm-pure
*
* @return null|non-empty-list<stdClass>
*/
function nullOrIsNonEmptyListOf(mixed $value): ?array
{
return Assert::nullOrIsNonEmptyListOf($value, stdClass::class);
}

/**
* @psalm-pure
*/
function allIsNonEmptyListOf(mixed $value): iterable
{
return Assert::allIsNonEmptyListOf($value, stdClass::class);
}

/**
* @psalm-pure
*/
function allNullOrIsNonEmptyListOf(mixed $value): iterable
{
return Assert::allNullOrIsNonEmptyListOf($value, stdClass::class);
}
Loading
Loading