beberlei/assert
Lightweight assertion library for validating method arguments and input data in PHP. Provides a fluent, readable API with many built-in rules (string, numeric, email, UUID, collection, etc.), clear exceptions, and easy extensibility for custom constraints.
Installation:
composer require beberlei/assert
Add to composer.json under require-dev if only for testing.
First Use Case: Validate a user input in a Laravel controller or service:
use Assert\Assertion;
public function store(Request $request)
{
$name = $request->input('name');
Assertion::notEmpty($name, 'Name cannot be empty');
Assertion::string($name, 'Name must be a string');
// Proceed with logic...
}
Where to Look First:
Replace repetitive if checks with assertions in models/services:
// In UserService.php
public function createUser(array $data)
{
Assertion::keyExists($data, 'email');
Assertion::email($data['email']);
Assert::that($data['age'])->integer()->greaterThan(18);
// Create user...
}
Use Assert::that() for multi-condition validation:
// In RequestValidator.php
public function validateRequest(array $request)
{
Assert::that($request['price'])->greaterThan(0)->numeric();
Assert::that($request['name'])->notEmpty()->string()->maxLength(100);
}
Collect all validation errors before responding:
// In FormHandler.php
public function handleForm(array $data)
{
$errors = [];
Assert::lazy()
->that($data['email'], 'email')->email()
->that($data['password'], 'password')->minLength(8)
->that($data['age'], 'age')->integer()->between(18, 120)
->verifyNow($errors); // $errors now contains all failures
if (!empty($errors)) {
return response()->json(['errors' => $errors], 422);
}
// Process data...
}
Validate optional or array values concisely:
// In DataSanitizer.php
public function sanitize(array $data)
{
Assertion::nullOrString($data['optional_field']);
Assertion::allIsInstanceOf($data['tags'], 'string');
}
Use assertions in FormRequest validation rules:
// In CreatePostRequest.php
public function rules()
{
return [
'title' => ['required', 'string', function ($attribute, $value, $fail) {
Assertion::maxLength($value, 255) or $fail('Title too long.');
}],
];
}
Extend for domain-specific rules:
// In CustomAssertions.php
use Assert\Assertion;
class CustomAssertions extends Assertion
{
public static function validSlug(string $slug)
{
self::string($slug);
self::regex($slug, '/^[a-z0-9\-]+$/i');
}
}
// Usage:
CustomAssertions::validSlug($request->slug);
Exception Handling:
AssertionFailedException by default. Ensure you catch and handle them gracefully in APIs:
try {
Assertion::email($email);
} catch (AssertionFailedException $e) {
return response()->json(['error' => $e->getMessage()], 400);
}
Lazy Assertions:
verifyNow() must be called to trigger exceptions. Forgetting this will silently collect errors.that($value, 'path') must match your error reporting (e.g., for API responses).Performance:
satisfy() for custom callbacks:
Assertion::satisfy($items, fn($item) => is_array($item));
Type Safety:
integer() rejects float values even if they are whole numbers. Use integerish() if needed.Custom Exceptions:
Assertion class requires reimplementing all static methods. Prefer dependency injection for exceptions:
// In AssertionServiceProvider.php
$this->app->bind(Assertion::class, function () {
return new CustomAssertion(new CustomExceptionHandler());
});
Inspect Failed Assertions:
$e->getConstraints() to debug complex assertions (e.g., between()):
catch (AssertionFailedException $e) {
dd($e->getValue(), $e->getConstraints());
}
Lazy Assertion Errors:
getErrorExceptions() to inspect all failures before verifyNow():
$lazy = Assert::lazy()->that($data['field'], 'field')->email();
if ($lazy->hasErrors()) {
dd($lazy->getErrorExceptions());
}
Custom Stringification:
stringify() in a custom Assertion class to improve error messages for your domain types:
protected static function stringify($value)
{
if ($value instanceof YourModel) {
return "YourModel[{$value->id}]";
}
return parent::stringify($value);
}
Custom Assertions:
Assertion and using satisfy():
public static function validPhone(string $phone)
{
self::satisfy($phone, fn($p) => preg_match('/^\+?[0-9\s\-\(\)]{10,}$/', $p));
}
Exception Customization:
InvalidArgumentException with a custom exception class:
class ValidationException extends \RuntimeException {}
// Then bind it in your DI container.
Integration with Laravel:
Assertion to add Laravel-specific helpers:
// In AppServiceProvider.php
Assertion::macro('activeUser', function ($user) {
Assertion::notNull($user);
Assertion::true($user->is_active);
});
// Usage: Assertion::activeUser(auth()->user());
Testing:
$this->expectException(AssertionFailedException::class);
Assertion::email('invalid-email');
stringify().lazy().satisfy() with a custom callback for complex logic:
Assertion::satisfy($items, fn($item) => $item->isValid());
Using Assertions for Input Filtering:
Str::of() or Filter packages for cleaning input.Overusing satisfy():
email()) over satisfy() for maintainability.Ignoring Lazy Assertions:
verifyNow() or check hasErrors() to avoid silent failures.How can I help you explore Laravel packages today?