Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Changelog
- All assertion methods now return the checked value.
- Added `notInArray` and `notOneOf`.
- Added `isInitialized`, to check if a class property is initialized.
- Added `notNegativeInteger` and `negativeInteger`

### Fixed

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ Method | Description
`integer($value, $message = '')` | Check that a value is an integer
`integerish($value, $message = '')` | Check that a value casts to an integer
`positiveInteger($value, $message = '')` | Check that a value is a positive (non-zero) integer
`negativeInteger($value, $message = '')` | Check that a value is a negative integer
`notNegativeInteger($value, $message = '')` | Check that a value is a non-negative integer
`float($value, $message = '')` | Check that a value is a float
`numeric($value, $message = '')` | Check that a value is numeric
`natural($value, $message = '')` | Check that a value is a non-negative integer
Expand Down
50 changes: 48 additions & 2 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ public static function integerish(mixed $value, string $message = ''): int|float
*/
public static function positiveInteger(mixed $value, string $message = ''): int
{
if (!(\is_int($value) && $value > 0)) {
self::integer($value);

if ($value < 1) {
static::reportInvalidArgument(\sprintf(
$message ?: 'Expected a positive integer. Got: %s',
static::valueToString($value)
Expand All @@ -127,6 +129,50 @@ public static function positiveInteger(mixed $value, string $message = ''): int
return $value;
}

/**
* @psalm-pure
* @psalm-assert non-negative-int $value
*
* @psalm-return non-negative-int
*
* @throws InvalidArgumentException
*/
public static function notNegativeInteger(mixed $value, string $message = ''): int
{
self::integer($value);

if ($value < 0) {
static::reportInvalidArgument(\sprintf(
$message ?: 'Expected a non negative integer. Got: %s',
static::valueToString($value)
));
}

return $value;
}

/**
* @psalm-pure
* @psalm-assert negative-int $value
*
* @psalm-return negative-int
*
* @throws InvalidArgumentException
*/
public static function negativeInteger(mixed $value, string $message = ''): int
{
self::integer($value);

if ($value >= 0) {
static::reportInvalidArgument(\sprintf(
$message ?: 'Expected a negative integer. Got: %s',
static::valueToString($value)
));
}

return $value;
}

/**
* @psalm-pure
*
Expand Down Expand Up @@ -1741,7 +1787,7 @@ public static function propertyNotExists(mixed $classOrObject, mixed $property,
));
}

return $value;
return $classOrObject;
}

/**
Expand Down
112 changes: 112 additions & 0 deletions src/Mixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,118 @@ public static function allNullOrPositiveInteger(?iterable $value, string $messag
return $value;
}

/**
* @psalm-pure
*
* @psalm-assert non-negative-int|null $value
*
* @return non-negative-int|null
*
* @throws InvalidArgumentException
*/
public static function nullOrNotNegativeInteger(mixed $value, string $message = ''): mixed
{
null === $value || static::notNegativeInteger($value, $message);

return $value;
}

/**
* @psalm-pure
*
* @psalm-assert iterable<non-negative-int> $value
*
* @return iterable<non-negative-int>
*
* @throws InvalidArgumentException
*/
public static function allNotNegativeInteger(iterable $value, string $message = ''): iterable
{
static::isIterable($value);

foreach ($value as $entry) {
static::notNegativeInteger($entry, $message);
}

return $value;
}

/**
* @psalm-pure
*
* @psalm-assert iterable<non-negative-int|null> $value
*
* @return iterable<non-negative-int|null>
*
* @throws InvalidArgumentException
*/
public static function allNullOrNotNegativeInteger(?iterable $value, string $message = ''): ?iterable
{
static::isIterable($value);

foreach ($value as $entry) {
null === $entry || static::notNegativeInteger($entry, $message);
}

return $value;
}

/**
* @psalm-pure
*
* @psalm-assert negative-int|null $value
*
* @return negative-int|null
*
* @throws InvalidArgumentException
*/
public static function nullOrNegativeInteger(mixed $value, string $message = ''): mixed
{
null === $value || static::negativeInteger($value, $message);

return $value;
}

/**
* @psalm-pure
*
* @psalm-assert iterable<negative-int> $value
*
* @return iterable<negative-int>
*
* @throws InvalidArgumentException
*/
public static function allNegativeInteger(iterable $value, string $message = ''): iterable
{
static::isIterable($value);

foreach ($value as $entry) {
static::negativeInteger($entry, $message);
}

return $value;
}

/**
* @psalm-pure
*
* @psalm-assert iterable<negative-int|null> $value
*
* @return iterable<negative-int|null>
*
* @throws InvalidArgumentException
*/
public static function allNullOrNegativeInteger(?iterable $value, string $message = ''): ?iterable
{
static::isIterable($value);

foreach ($value as $entry) {
null === $entry || static::negativeInteger($entry, $message);
}

return $value;
}

/**
* @psalm-pure
*
Expand Down
20 changes: 20 additions & 0 deletions tests/AssertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,26 @@ public static function getTests(): array
['positiveInteger', ['0'], false],
['positiveInteger', [1.0], false],
['positiveInteger', [1.23], false],
['notNegativeInteger', [123], true],
['notNegativeInteger', [1], true],
['notNegativeInteger', [-123], false],
['notNegativeInteger', [0], true],
['notNegativeInteger', [0.0], false],
['notNegativeInteger', ['123'], false],
['notNegativeInteger', ['-123'], false],
['notNegativeInteger', ['0'], false],
['notNegativeInteger', [1.0], false],
['notNegativeInteger', [1.23], false],
['negativeInteger', [123], false],
['negativeInteger', [1], false],
['negativeInteger', [-123], true],
['negativeInteger', [0], false],
['negativeInteger', [0.0], false],
['negativeInteger', ['123'], false],
['negativeInteger', ['-123'], false],
['negativeInteger', ['0'], false],
['negativeInteger', [1.0], false],
['negativeInteger', [1.23], false],
['float', [1.0], true],
['float', [1.23], true],
['float', [123], false],
Expand Down
63 changes: 63 additions & 0 deletions tests/static-analysis/assert-negativeInteger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis;

use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @param mixed $value
*
* @psalm-return negative-int
*/
function negativeInteger(mixed $value): int
{
Assert::negativeInteger($value);

return $value;
}

/**
* @psalm-pure
*
* @param mixed $value
*
* @psalm-return negative-int|null
*/
function nullOrNegativeInteger(mixed $value): ?int
{
Assert::nullOrNegativeInteger($value);

return $value;
}

/**
* @psalm-pure
*
* @param mixed $value
*
* @return iterable<negative-int>
*/
function allNegativeInteger(mixed $value): iterable
{
Assert::allNegativeInteger($value);

return $value;
}

/**
* @psalm-pure
*
* @param mixed $value
*
* @return iterable<negative-int|null>
*/
function allNullOrNegativeInteger(mixed $value): iterable
{
Assert::allNullOrNegativeInteger($value);

return $value;
}
65 changes: 65 additions & 0 deletions tests/static-analysis/assert-notNegativeInteger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Webmozart\Assert\Tests\StaticAnalysis;

use Webmozart\Assert\Assert;

/**
* @psalm-pure
*
* @param mixed $value
*
* @psalm-return non-negative-int
*/
function nonNegativeInteger(mixed $value): int
{
Assert::notNegativeInteger($value);

$value *= -1;

return $value;
}

/**
* @psalm-pure
*
* @param mixed $value
*
* @psalm-return non-negative-int|null
*/
function nullOrNonNegativeInteger(mixed $value): ?int
{
Assert::nullOrNotNegativeInteger($value);

return $value;
}

/**
* @psalm-pure
*
* @param mixed $value
*
* @return iterable<non-negative-int>
*/
function allNonNegativeInteger(mixed $value): iterable
{
Assert::allNotNegativeInteger($value);

return $value;
}

/**
* @psalm-pure
*
* @param mixed $value
*
* @return iterable<non-negative-int|null>
*/
function allNullOrNonNegativeInteger(mixed $value): iterable
{
Assert::allNullOrPositiveInteger($value);

return $value;
}