simplesamlphp/assert
Fork of webmozart/assert that lets every assertion throw your chosen exception (or a default AssertionFailedException) instead of always InvalidArgumentException. Adds a few custom assertions aimed at XML/SAML2 use cases.
Installation Add the package via Composer:
composer require simplesamlphp/assert:^1.5.0
No additional configuration is required—it’s a drop-in wrapper around webmozart/assert with minor enhancements.
First Use Case Replace basic PHP assertions with fluent, expressive checks. Example:
use SimpleSAML\Assert\Assert;
$userInput = ['name' => 'John', 'age' => 30, 'role' => UserRole::Admin];
// Validate structure
Assert::keyExists($userInput, 'name');
Assert::keyExists($userInput, 'age');
// Validate types (including enums)
Assert::string($userInput['name']);
Assert::numeric($userInput['age']);
Assert::enum($userInput['role'], [UserRole::Admin, UserRole::User]); // New in 1.5.0
// Validate constraints
Assert::minLength($userInput['name'], 2);
Assert::max($userInput['age'], 120);
Where to Look First
Replace Laravel’s validate() with Assert for granular control:
use SimpleSAML\Assert\Assert;
use Illuminate\Http\Request;
public function rules()
{
return [
'email' => 'required|email',
'age' => 'required|integer',
'role' => 'required|string',
];
}
public function withValidator($validator)
{
$data = $validator->getData();
Assert::email($data['email']);
Assert::min($data['age'], 18);
Assert::max($data['age'], 99);
Assert::enum($data['role'], [UserRole::Admin, UserRole::User]); // New enum check
}
Enforce API input contracts in middleware:
use SimpleSAML\Assert\Assert;
use Closure;
public function handle($request, Closure $next)
{
$payload = $request->json()->all();
Assert::keyExists($payload, 'user_id');
Assert::uuid($payload['user_id']);
Assert::array($payload['metadata']);
Assert::enum($payload['status'], [OrderStatus::Pending, OrderStatus::Completed]); // New enum check
return $next($request);
}
Validate method arguments in services:
use SimpleSAML\Assert\Assert;
class UserService {
public function updateProfile(array $data, string $userId, UserRole $role)
{
Assert::keyExists($data, 'email');
Assert::notEmpty($data['email']);
Assert::uuid($userId);
Assert::enum($role, [UserRole::Admin, UserRole::User]); // New enum check
// Business logic...
}
}
Validate config or dependencies:
public function register()
{
$this->app->singleton(QueueWorker::class, function ($app) {
$config = config('queue.worker');
Assert::keyExists($config, 'connections');
Assert::array($config['connections']);
Assert::enum($config['connections']['default'], ['database', 'redis']); // New enum check
return new QueueWorker($config);
});
}
Extend with custom logic, including enum validation:
use SimpleSAML\Assert\Assert;
Assert::custom(function ($value, $message = 'Value is invalid') {
if (!preg_match('/^[A-Z0-9]{8,}$/', $value)) {
throw new \InvalidArgumentException($message);
}
}, $value, 'Custom validation failed');
Assert::enum($value, [UserRole::Admin, UserRole::User]); // Built-in enum check
Overuse in Controllers
Assert with Laravel’s built-in validation. Use Assert for low-level checks (e.g., method arguments) and Laravel’s Validator for user input.Assert in a UserRepository but Validator in a StoreUserRequest.Performance in Loops
Assert throws exceptions on failure. Use sparingly in performance-critical loops (e.g., bulk operations).try-catch blocks.Type Juggling
Assert::numeric() may behave unexpectedly with strings like "123" vs. 123. Explicitly cast if needed:
Assert::numeric((int) $value);
Laravel’s validate() vs. Assert
validate() returns a Validator object, while Assert throws exceptions. Combine them:
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new \InvalidArgumentException($validator->errors()->first());
}
Assert::minLength($data['name'], 3); // Extra check
New Regex Stricter in v1.5.0
$value = trim($value); // Ensure no trailing newlines
Assert::regex($value, '/pattern/');
Custom Error Messages
Pass custom messages to Assert:
Assert::minLength($name, 3, 'Name must be at least 3 characters.');
Assert::enum($role, [UserRole::Admin, UserRole::User], 'Invalid user role.');
Stack Traces Exceptions include stack traces. For cleaner logs, wrap assertions:
try {
Assert::enum($role, [UserRole::Admin, UserRole::User]);
} catch (\InvalidArgumentException $e) {
\Log::error("Validation failed: {$e->getMessage()}");
throw new \HttpException(422, $e->getMessage());
}
Testing
Mock Assert in PHPUnit:
$this->expectException(\InvalidArgumentException::class);
Assert::enum($invalidRole, [UserRole::Admin, UserRole::User]);
Custom Assertion Classes Create reusable validators, including enum checks:
use SimpleSAML\Assert\Assert;
class RoleAssert {
public static function isValid(UserRole $role): void
{
Assert::enum($role, [UserRole::Admin, UserRole::User]);
}
}
Laravel Service Provider Extensions Register global assertions, including enum validation:
public function boot()
{
Assert::extend('active', function ($attribute, $value, $fail) {
if (!$value) {
$fail('The '.$attribute.' must be active.');
}
});
// Add enum validation for a custom field
Assert::extend('valid_status', function ($attribute, $value, $fail) {
Assert::enum($value, [OrderStatus::Pending, OrderStatus::Completed]);
});
}
Integration with Laravel Packages
Use Assert in packages to enforce contracts, including enums:
// In a package's service class
public function __construct(array $config)
{
Assert::keyExists($config, 'api_key');
Assert::string($config['api_key']);
Assert::enum($config['timeout'], [Timeout::Short, Timeout::Long]); // New enum check
}
simplesamlphp/assert requires zero config.webmozart/assert is not duplicated in composer.json (use replace if needed).enum() method requires PHP 8.1+ for full functionality (backward compatibility is maintained for older PHP versions).How can I help you explore Laravel packages today?