beberlei/assert
A simple PHP library of assertions and guard methods for fast, low-level input validation in business models, libraries, and application code. Failed assertions throw exceptions, eliminating if-clauses. Install via Composer: composer require beberlei/assert.
Architecture fit. beberlei/assert is a deliberately framework-agnostic, dependency-free PHP library that lives at the domain/application boundary. It fits Laravel well as an invariant and precondition layer (DTO constructors, value objects, service-layer guards, shared packages, queued jobs, console commands) and fits poorly as a user-facing form validator. Laravel's Validator owns localization, error bags, request binding, and 422 semantics; Assert owns fast, low-level "this must never be true" checks that throw. Treating these as substitutes rather than complements is the primary architectural failure mode.
Integration feasibility. High. composer require beberlei/assert requires no service provider, config file, facade, or container binding. The library exposes a static API (Assertion::*), a fluent API (Assert::that()), nullOr / all modifiers, and a lazy API that accumulates failures and throws once via verifyNow(). The README explicitly recommends subclassing Assertion in a project namespace to shield against BC breaks and to override the thrown exception — that subclass is the natural seam for a Laravel integration. The one real integration gap is that LazyAssertionException is not a ValidationException; an adapter is required if lazy results should surface as standard 422 responses.
Technical risk.
NOASSERTION. Upstream is understood to be BSD-2-Clause, but this must be resolved by legal before adoption; it is a hard gate, not a nice-to-have.Dependents: 0 is implausible given ~2.4k stars and heavy Packagist download volume; treat dependency-graph signals in this evaluation as unreliable and re-derive from Packagist/GitHub directly.AssertionFailedException becomes a 500 in Laravel's handler unless mapped. This is the most likely production incident.composer.json.Key questions.
MessageBag, 400, or domain exception)?Stack fit. Composer-native, PSR-4, zero framework coupling. It installs cleanly into a Laravel app, into first-party packages, and into non-Laravel shared libraries consuming the same domain code — which is the strongest argument for it over Illuminate\Validation in package code. Its near-zero dependency footprint keeps the transitive supply chain thin, a genuine operational benefit in a Laravel app that already carries a large dependency tree.
Migration path. Incremental, boundary-first — never a big-bang replacement of Laravel validation.
App\Support\Assertion extends \Assert\Assertion to pin messages, override stringify(), and control the exception type.LazyAssertionException into Illuminate\Validation\ValidationException (using getErrorExceptions() to build the MessageBag keyed by property path).old() redisplay matter.Compatibility. Verify the PHP constraint against the target Laravel version before any code is written. Note that the functional constructors (Assert\that(), lazy()) do not honor an extended Assertion class — standardize on Assert::that() / Assert::lazy() on the subclass, or wrap them in project-local helper functions, otherwise the exception customization silently doesn't apply. Add PHPStan/Psalm rules (or a phpcs forbidden-call sniff) to prevent raw \Assert\Assertion:: usage from leaking outside the wrapper.
Sequencing.
ValidationException adapter.Maintenance. Low ongoing cost: no runtime configuration, no migrations, no service provider, small surface area. The maintenance burden is concentrated in two places — the wrapper (ours, fully controlled) and the upstream dependency (not ours). Because the wrapper is thin and the library is stateless, the eventual "package is abandoned" scenario is a bounded swap rather than a rewrite. That property should be recorded as the explicit mitigation for the bus-factor risk.
Support. No vendor support; community issues only. Internal support load is low but front-loaded: the API is small (~80 assertions, clear naming, decent README), yet the conceptual distinction between invariant assertions and user input validation is the thing engineers get wrong. Budget for a short internal doc and a code-review checklist rather than formal training.
Scaling. Effectively free. Checks are plain function calls with no object instantiation on the static path, making them cheaper than instantiating Laravel validator objects, translators, and rule sets. Negligible memory and CPU impact at any realistic request volume. The scaling caveat is not performance but state: the static exception configuration must be set once at bootstrap, never per-request, or Octane workers will leak configuration across requests.
Failure modes.
ValidationException adapter and a handler-level mapping.::assertion; callbacks that ignore it misreport which assertion failed, degrading debuggability.AssertionFailedException — a subtle cross-cutting change.Ramp-up. A mid-level Laravel developer is productive with the static and fluent APIs in well under a day. Establishing the wrapper, the exception adapter, the testing helpers, and the "when to use which validator" convention is a 1–2 day effort for one engineer, and that investment is what makes the rest of the rollout low-risk.
How can I help you explore Laravel packages today?