nexusphp / assert
Chainable type-safety assertions library.
Requires
- php: ^8.2
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.88
- kubawerlos/php-cs-fixer-custom-fixers: ^3.35
- nexusphp/cs-config: ^3.26
- nexusphp/tachycardia: ^2.4
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.5 || ^12.5
This package is auto-updated.
Last update: 2026-05-16 07:48:48 UTC
README
Nexus Assert
This library provides efficient type assertions for input validation in a chainable, fluent, natural language way. This also provides static analysis support ensuring PHPStan can understand the asserted type.
Installation
composer require nexusphp/assert
Installing the PHPStan extension
You're all set if you are using phpstan/extension-installer.
Manual installation
If you don't want to use phpstan/extension-installer, include extension.neon in your project's PHPStan config:
includes:
- vendor/nexusphp/assert/extension.neon
Usage
Use the static that() method of Nexus\Assert\Assert to start chaining expectations.
<?php use Nexus\Assert\Assert; function test(mixed $a, mixed $b, mixed $c): void { Assert::that($a)->isString(); // $a is now understood as string Assert::that($b)->isString()->isNumeric(); // $b is now understood as numeric string Assert::that($c)->isInt()->not()->isNegativeInt(); // $c is understood as int<0, max> }
When an expectation fails, the method call will throw a Nexus\Assert\ExpectationFailedException object
with the message formatted depending on the available context. By default, there are two available context:
| Name | Description |
|---|---|
value |
The exported value of the argument passed to that() |
type |
The exported type of the argument passed to that() |
List of Expectations
| Methods | Available Context |
|---|---|
contains(string $needle, ?string $message = null): self |
value, needle |
endsWith(string $needle, ?string $message = null): self |
value, needle |
hasMaxLength(int $max, ?string $message = null): self |
value, max |
hasMethod(string $method, ?string $message = null): self |
value+, method= |
hasMinLength(int $min, ?string $message = null): self |
value, min |
hasOffset(int|string $key, ?string $message = null): self |
value, key= |
hasProperty(string $property, ?string $message = null): self |
value+, property= |
isArray(?string $message = null): self |
value, type |
isArrayKey(?string $message = null): self |
value, type |
isBetween(float|int $min, float|int $max, bool $inclusive = true, ?string $message = null): self |
value, min, max |
isBool(?string $message = null): self |
value, type |
isCallable(?string $message = null): self |
value, type |
isCountable(?string $message = null): self |
value, type |
isFalse(?string $message = null): self |
value, type |
isFloat(?string $message = null): self |
value, type |
isIdentical(mixed $other, ?string $message = null): self |
value, other, type |
isInstanceOf(object|string $class, ?string $message = null): self |
value, class, type |
isInt(?string $message = null): self |
value, type |
isIterable(?string $message = null): self |
value, type |
isList(?string $message = null): self |
value, type |
isLowercaseString(?string $message = null): self |
value, type |
isMap(?string $message = null): self |
value, type |
isNaturalInt(?string $message = null): self |
value, type |
isNegativeInt(?string $message = null): self |
value, type |
isNonEmptyString(?string $message = null): self |
value, type |
isNull(?string $message = null): self |
value, type |
isNumeric(?string $message = null): self |
value, type |
isObject(?string $message = null): self |
value, type |
isOneOf(array $choices, ?string $message = null): self |
value, choices |
isPositiveInt(?string $message = null): self |
value, type |
isResource(?string $message = null): self |
value, type |
isScalar(?string $message = null): self |
value, type |
isString(?string $message = null): self |
value, type |
isTrue(?string $message = null): self |
value, type |
isUppercaseString(?string $message = null): self |
value, type |
isUrl(?string $message = null): self |
value |
matchesRegularExpression(string $pattern, ?string $message = null): self |
value, pattern= |
startsWith(string $needle, ?string $message = null): self |
value, needle |
Note
- The
valuecontext is always value-exported except when appended by+which means it is type-exported instead. - The
typecontext is always type-exported. In negated expectations, this context is omitted. - Other context values are value-exported except when appended by
=which means it is integrated as-is.
Available Expectation Classes
Nexus\Assert\Assert::that() returns an instance of Nexus\Assert\Expectation which is the base implementation
of the Nexus\Assert\Expectable interface.
If you want to have a negated expectation, you can invoke not() on the expectation to return an instance of
Nexus\Assert\NegatedExpectation.
<?php use Nexus\Assert\Assert; function test(mixed $a): void { Assert::that($a)->isNumeric()->not()->isString(); // $a is narrowed as either int or float }
If you want to have a nullable expectation, that is, proceed with the expectation only if the input is not
null, then you can invoke nullOr() on the base expectation.
<?php use Nexus\Assert\Assert; function test(mixed $a): void { Assert::that($a)->nullOr()->isString(); // $a is now understood as either string or null }
If you want to assert against each key or each value of an iterable input, you can invoke keys() or
values() on the base expectation. These return a Nexus\Assert\KeysIteratingExpectation or
Nexus\Assert\ValuesIteratingExpectation respectively, on which any expectation method is applied to
every element.
<?php use Nexus\Assert\Assert; function test(mixed $a, mixed $b): void { Assert::that($a)->values()->isInt(); // $a is now understood as iterable<mixed, int> Assert::that($b)->keys()->isString(); // $b is now understood as iterable<string, mixed> }
Note
PHP arrays only allow int|string keys. Calling key-iterating methods whose predicate is unreachable
on array keys (e.g. keys()->isFloat(), keys()->isObject(), keys()->hasMethod()) throws a
LogicException at runtime when the wrapped value is an array. They remain valid on non-array
iterables (Iterator, Generator, IteratorAggregate), whose keys are not so constrained.
Note
Currently, the not(), nullOr(), keys(), and values() methods can only be invoked on the base
Expectation object. They are not yet available on the variant expectations. This can be considered
in future versions.
Customising the Exporter
Nexus\Assert\Assert utilises the default implementation of Nexus\Assert\ExporterInterface - Exporter -
to nicely export the value and type of the asserted input. If you need to do some customisations for your
use case, you can implement your own exporter and let Assert use that.
<?php use App\Exporter\MyExporter; use Nexus\Assert\Assert; Assert::setExporter(new MyExporter()); function foo(mixed $value): void { Assert::that($value)->isBool(); }
Formatting the Exception Message
ExpectationFailedException accepts a templated message and the context when it is instantiated. You can
modify the message by passing a template message as last argument to any expectation method. The context
values are wrapped in curly braces with no in-between spaces.
<?php use Nexus\Assert\Assert; function test(mixed $a): void { $message = 'Value "{value}" is not of type string'. Assert::that($a)->isString($message); // if this fails, the message would be something like: // Value "42" is not of type string. }
When creating your custom messages, you are not compelled to use all available context. However, adding an unknown context for an expectation method will be useless as that won't be interpolated.
Resources
License
This library is licensed under MIT.