Skip to content

Commit

Permalink
Create move "that*" methods
Browse files Browse the repository at this point in the history
Those methods can be handy in a variety of scenarios. Hence I decided to
implement them.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
  • Loading branch information
henriquemoody committed Apr 11, 2023
1 parent d340b7a commit 557a4fb
Show file tree
Hide file tree
Showing 23 changed files with 983 additions and 165 deletions.
177 changes: 151 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Assert::between(42, 1, 10, new DomainException('Something is not right'));
That can be very useful if you want to throw specific exceptions for your
application. That was a great idea from [Malukenho][]!

### Chained assertions
### Chained assertions (`that()`)

You can chain assertions using `Assert::that($input)`, which allows you to
perform multiple assertions to the same input with less duplication.
Expand Down Expand Up @@ -123,11 +123,106 @@ Assert::that(3, 'The number must be valid')
Note that the customization on a specific assertion will overwrite the
customization on the whole chain.

You can also apply the effect of the prefixes listed below to the whole chain.

```php
// will throw an exception => 3 (the length of the input) must equal 4
Assert::that(['names' => ['Respect', 'Assertion'], 'options' => [1, 2, 3]])
->all()->arrayType()
->key('names')->allStringType()
->key('options')->lengthEquals(4);
```

There are also some special methods that allow you to create a chain of
assertions.

* `thatAll()`: assert all elements in the input with the subsequent assertions.
* `thatNot()`: assert the input inverting the subsequent assertions.
* `thatNullOr()`: assert the input if it is not `null` with the subsequent assertions.
* `thatKey()`: assert a key from the input with the subsequent assertions.
* `thatProperty()`: assert a property from the input with the subsequent assertions.

## Prefixes

With Assertion, you can use any [Validation][] rule, but it also allows
you to use them with prefixes that simplify some operations.

### `all*()`: asserting all elements in an input

Assertions can be executed with the `all` prefix which will assert all elements
in the input with the prefixed assertion:

```php
// will throw an exception => "3" (like all items of the input) must be of type integer
Assert::allIntType([1, 2, '3']);
```

In some cases, you might want to perform multiple assertions to all elements. You
can use `thatAll()` chain of assertions that will assert all elements in the input
with the subsequent assertions:

```php
// will throw an exception => 3 (like all items of the input) must be between 1 and 2
Assert::thatAll([1, 2, 2, 1, 3])
->intVal()
->between(1, 2);
```

If you want to perform multiple assertions to all elements, but you also want to
perform other assertions to the input, you can `that()->all()`:

```php
// will throw an exception => 5 (the length of the input) must be less than 4
Assert::that([1, 2, 2, 1, 3])
->arrayType()
->notEmpty()
->lengthGreaterThan(3)
->all()->intVal()->between(1, 2);
```

### `nullOr*()`: asserting the value of an input or null

Assertions can be executed with the `nullOr` prefix which will assert only if
the value of the input it not null.

```php
// will throw an exception => 42 must be negative
Assert::nullOrNegative(42);

// will not throw an exception
Assert::nullOrNegative(null);

// will throw an exception => 5 must be between 1 and 4
Assert::nullOrBetween(5, 1, 4);

// will not throw an exception
Assert::nullOrBetween(null, 1, 4);
```

In some cases, you might want to perform multiple assertions to a value in case
it is not null. In this case, you can use `thatNullOr()`:

```php
// will throw an exception => 6 must be a valid prime number
Assert::thatNullOr(6)
->positive()
->between(1, 10)
->primeNumber();

// will not throw an exception
Assert::thatNullOr(null)
->positive()
->between(1, 10)
->primeNumber();
```

For convenience, you might also use the `that()->nullOr()`:

```php
Assert::that(6)
->nullOr()->positive()->between(1, 10)->primeNumber();
```

### `not*()`: inverting assertions

You can execute assertions with the `not` prefix, which will assert the opposite
Expand All @@ -141,14 +236,20 @@ Assert::notEven(2);
Assert::notIn(3, [1, 2, 3, 4]);
```

### `all*()`: asserting all elements in an input

Assertions can be executed with the `all` prefix which will assert all elements
in the input with the prefixed assertion:
If you need to invert more than a few rules, it might be easier to use `thatNot()`
and `that()->not()`:

```php
// will throw an exception => "3" (like all items of the input) must be of type integer
Assert::allIntType([1, 2, '3']);
// will throw an exception => "1" must not be positive
Assert::thatNot('1')
->intType()
->positive()
->between(1, 3);


// will throw an exception => "1" must not be positive
Assert::that('1')
->not()->intType()->positive()->between(1, 3);
```

### `key*()`: asserting a key in an array
Expand Down Expand Up @@ -190,6 +291,28 @@ Assert::keyExists(['foo' => '/path/to/file.txt'], 'foo');
Not that `keyExists` assertion, will assert whether the value of key `foo` exists
in the Filesystem.

If you want to perform multiple assertions to the key of an input, you can use
`thatKey()`:

```php
// will throw an exception => 9 (the length of the input) must be less than 4
Assert::thatKey(['foo' => 'my-string'], 'foo')
->stringType()
->startsWith('my-')
->lengthLessThan(4);
```

If you want to perform multiple key assertions to the same input, you can use
`that()->key()`:

```php
// will throw an exception => bar must be less than 40
Assert::that(['foo' => 'my-string', 'bar' => 42])
->arrayType()
->key('foo')->stringType()->startsWith('my-')
->key('bar')->intType()->positive()->lessThan(40);
```

### `property*()`: asserting a property in an object

We'll use the object below as input in the examples that follow.
Expand Down Expand Up @@ -237,6 +360,27 @@ Assert::propertyExists($input, 'foo');
Note that the `propertyExists` assertion will assert whether the value of
property `foo` exists in the FileSystem.

If you want to perform multiple assertions to a property of an object, you can
use `thatProperty()`:

```php
// will throw an exception => foo must be greater than 5
Assert::thatProperty($input, 'foo')
->intType()
->positive()
->greaterThan(5);
```

If you want to perform multiple key assertions to the same input, you can use
`that()->property()`:

```php
// will throw an exception => foo must be greater than 5
Assert::that($input)
->instance(stdClass::class)
->property('foo')->intType()->positive()->greaterThan(5);
```

### `length*()`: asserting the length of an input

Assertions can be executed with the `length` prefix which will assert the length
Expand Down Expand Up @@ -318,25 +462,6 @@ This library also allows you to use the `not` prefix after the `min` prefix:
Assert::minNotPositive([23, 7, 20]);
```

### `nullOr*()`: asserting the value of an input or null

Assertions can be executed with the `nullOr` prefix which will assert only if the
value of the input it not null.

```php
// will throw an exception => 42 must be negative
Assert::nullOrNegative(42);

// will not throw an exception
Assert::nullOrNegative(null);

// will throw an exception => 5 must be between 1 and 4
Assert::nullOrBetween(5, 1, 4);

// will not throw an exception
Assert::nullOrBetween(null, 1, 4);
```

[beberlei/assert]: https://github.com/beberlei/assert
[Composer]: http://getcomposer.org
[Countable]: http://php.net/countable
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ parameters:
fileExtensions:
- php
- phpt
ignoreErrors:
- '/Call to an undefined method Respect\\Assertion\\Mixin\\Chain\\Mixin::[a-zA-Z]+\(\)/'
38 changes: 36 additions & 2 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
namespace Respect\Assertion;

use Exception;
use Respect\Assertion\Chain\AssociativeSimpleChain;
use Respect\Assertion\Chain\RegularFullChain;
use Respect\Assertion\Chain\RegularSimpleChain;
use Respect\Assertion\Creator\CompositeCreator;
use Respect\Assertion\Exception\CannotCreateAssertionException;
use Respect\Assertion\Mixin\Static\Mixin;
Expand All @@ -28,9 +31,40 @@ final class Assert
{
private static ?Creator $creator = null;

public static function that(mixed $input, null|string|Throwable $description = null): ChainAssert
public static function that(mixed $input, null|string|Throwable $description = null): RegularFullChain
{
return new ChainAssert($input, $description);
return new RegularFullChain(self::getCreator(), $input, $description, '');
}

public static function thatAll(mixed $input, null|string|Throwable $description = null): RegularSimpleChain
{
return new RegularSimpleChain(self::getCreator(), $input, $description, 'all');
}

public static function thatNot(mixed $input, null|string|Throwable $description = null): RegularSimpleChain
{
return new RegularSimpleChain(self::getCreator(), $input, $description, 'not');
}

public static function thatNullOr(mixed $input, null|string|Throwable $description = null): RegularSimpleChain
{
return new RegularSimpleChain(self::getCreator(), $input, $description, 'nullOr');
}

public static function thatKey(
mixed $input,
int|string $key,
null|string|Throwable $description = null
): AssociativeSimpleChain {
return new AssociativeSimpleChain(self::getCreator(), $key, $input, $description, 'key');
}

public static function thatProperty(
mixed $input,
string $property,
null|string|Throwable $description = null
): AssociativeSimpleChain {
return new AssociativeSimpleChain(self::getCreator(), $property, $input, $description, 'property');
}

private static function getCreator(): Creator
Expand Down
34 changes: 34 additions & 0 deletions src/Chain/AssociativeFullChain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of Respect/Assertion.
*
* (c) Henrique Moody <henriquemoody@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

declare(strict_types=1);

namespace Respect\Assertion\Chain;

use Respect\Assertion\Creator;
use Respect\Assertion\Mixin\Chain\Mixin;
use Throwable;

/**
* @mixin Mixin
*/
final class AssociativeFullChain extends FullChain
{
public function __construct(
Creator $creator,
int|string $reference,
mixed $input,
null|string|Throwable $description,
string $prefix = ''
) {
parent::__construct($creator, $input, $description, $prefix, $reference);
}
}
34 changes: 34 additions & 0 deletions src/Chain/AssociativeSimpleChain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of Respect/Assertion.
*
* (c) Henrique Moody <henriquemoody@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

declare(strict_types=1);

namespace Respect\Assertion\Chain;

use Respect\Assertion\Creator;
use Respect\Assertion\Mixin\Chain\Mixin;
use Throwable;

/**
* @mixin Mixin
*/
final class AssociativeSimpleChain extends SimpleChain
{
public function __construct(
Creator $creator,
int|string $reference,
mixed $input,
null|string|Throwable $description,
string $prefix
) {
parent::__construct($creator, $input, $description, $prefix, $reference);
}
}
44 changes: 44 additions & 0 deletions src/Chain/FullChain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of Respect/Assertion.
*
* (c) Henrique Moody <henriquemoody@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

declare(strict_types=1);

namespace Respect\Assertion\Chain;

use Throwable;

abstract class FullChain extends SimpleChain
{
public function all(null|string|Throwable $description = null): RegularFullChain
{
return new RegularFullChain($this->creator, $this->input, $description, 'all');
}

public function not(null|string|Throwable $description = null): RegularFullChain
{
return new RegularFullChain($this->creator, $this->input, $description, 'not');
}

public function nullOr(null|string|Throwable $description = null): RegularFullChain
{
return new RegularFullChain($this->creator, $this->input, $description, 'nullOr');
}

public function key(string $key, null|string|Throwable $description = null): AssociativeFullChain
{
return new AssociativeFullChain($this->creator, $key, $this->input, $description, 'key');
}

public function property(string $key, null|string|Throwable $description = null): AssociativeFullChain
{
return new AssociativeFullChain($this->creator, $key, $this->input, $description, 'property');
}
}

0 comments on commit 557a4fb

Please sign in to comment.