Library / SDK
webmozarts/assert avatar
webmozarts/assert

webmozart/assert: the PHP assertion library whose real API is its error messages

Assertions to validate method input/output with nice error messages.

7,650 stars167 forksPHPMIT

At a glance

What is it?
One class, one exception type, and a placeholder convention that stays the same across every assertion, which is the whole reason this package exists separately from the one it was inspired by.
Who is it for?
webmozart/assert is a small package with a specific reason to exist, and the reason is in its FAQ rather than its feature list: consistent placeholder ordering that a message-compatible predecessor could not adopt without breaking its own users. If you are choosing between PHP assertion libraries, that single detail is the deciding factor, because every guard clause you write depends on what the message can express.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • webmozarts/assert7,650 ★ · +2
Star history
Repository2026-10-082026-10-23
webmozarts/assert7.6k7.7k

2 Stars in the last 15 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One class, one exception, 7,649 stars

The package description is one sentence: assertions to validate method input and output with nice error messages. That is the entire scope. There is a single `Assert` class of static methods, and the README states the rule that makes the library predictable: every assertion in the `Assert` class throws a `Webmozart\Assert\InvalidArgumentException` when it fails.

One exception type for all failures is a deliberate design choice with real consequences. A caller can wrap a block of validation in a single try/catch and know that nothing else in `Assert` will escape, and a caller who does not catch it gets a standard exception rather than a library-specific base class they had to learn. It also means the failure type carries no information about which assertion failed, which is exactly why the message text matters so much here.

The scale suggests how settled this problem is considered in PHP. The repository has 7,649 stars, 166 forks and 30 open issues, is MIT licensed, uses `master` as its default branch, and had its last push on 2026-06-15. The fork ratio is low, under three percent, which is what you would expect from a package people consume through Composer rather than vendor and edit. There are no repository topics set.

The three most recent releases are 2.4.1 on 2026-06-15, 2.4.0 on 2026-05-20 and 2.3.0 on 2026-04-11. All three release bodies say the same thing: see the change log in CHANGELOG.md for changes.

The placeholder contract is the actual API

The README has a section explaining the difference from beberlei/assert, and it is the most useful text in the documentation. The library is described as heavily inspired by Benjamin Eberlei's package, and the stated difference is that this one fixes a usability issue with error messages which could not be fixed there without breaking backwards compatibility.

The concrete problem is placeholder ordering. In beberlei/assert the ordering of the `%s` placeholders differs from one assertion to the next, so a reusable message string cannot be written. This package standardises it:

- `%s` is the tested value as a string, for example `"/foo/bar"`. - `%2$s`, `%3$s` and onward are additional assertion-specific values, such as the minimum or maximum length, or the allowed values.

The README directs readers to the source code of each assertion to find the additional placeholders available. That is worth internalising as a habit rather than a chore: because the extra slots are per-assertion, `%2$s` means the minimum in one method and the allowed set in another.

Writing a custom message looks like this:

php
Assert::string($path, 'The path is expected to be a string. Got: %s');

The library's claim is that usable error messages are the default rather than something you opt into, which inverts the usual arrangement where you have to read each assertion's source to learn which argument goes where.

What the assertion catalogue covers

The README organises the `Assert` methods into categories, and the type assertions are the largest block. They run from the plain checks (`string`, `stringNotEmpty`, `integer`, `float`, `numeric`, `boolean`, `scalar`, `object`, `resource`) through the numeric refinements (`integerish` for values that cast to an integer, `positiveInteger`, `negativeInteger`, `notNegativeInteger`, `natural`) into the structural checks: `isArray`, `isIterable` for an array or a `\Traversable`, `isCountable`, `isCallable`, and the property-level `isInitialized($value, $property)`.

Then there is a cluster of class relationship assertions, and the naming is worth reading closely because it draws distinctions that are easy to get wrong by hand: `isInstanceOf` versus `isAOf`, where the latter also accepts a class that has one of the named class's parents. `isInstanceOfAny` and `isAnyOf` take arrays for the same reason, `notInstanceOf` and `isNotA` are the negations, and `isNotInstanceOfAny` closes the group. `isArrayAccessible` and `uniqueValues` round out the structural set.

The comparison assertions follow the same convention. There are identity checks for `true`, `false`, `notFalse`, `null` and `notNull`, `isEmpty` and `notEmpty` for emptiness as PHP defines it, loose and strict equality separated into `eq` and `same` (with `notEq` and `notSame` as negations), and `greaterThan` for ordering. That `eq` and `same` are distinct methods rather than one method with a strict flag is a good example of the library's preference: the intent is visible in the call.

Every signature follows the shape `$message = ''` as the last parameter, which is what makes a message optional everywhere without exceptions to remember.

Reading a failure message in practice

The README's example is short enough to hold in your head, and it shows the default message format as well as the custom one. An `Employee` constructor validates its ID:

php
use Webmozart\Assert\Assert;

class Employee
{
    public function __construct($id)
    {
        Assert::integer($id, 'The employee ID must be an integer. Got: %s');
        Assert::greaterThan($id, 0, 'The employee ID must be a positive integer. Got: %s');
    }
}

Constructing one with a bad value produces an `InvalidArgumentException` carrying the message:

php
new Employee('foobar');
// => Webmozart\Assert\InvalidArgumentException:
//    The employee ID must be an integer. Got: string

new Employee(-10);
// => Webmozart\Assert\InvalidArgumentException:
//    The employee ID must be a positive integer. Got: -10

Two details are worth noticing. First, the message names the rule that failed in plain language and then reports what arrived, which is the format that survives being read in a log file at three in the morning. Second, the reported value is compact: a string input is described by its type in the first case while a numeric input is rendered as its value in the second, so the default messages are not simply a raw dump of the argument.

Because the assertions run in order and throw on the first failure, the second call only reports on an input that already passed the first. That ordering is what lets two short lines cover a whole precondition, and it is also why the message of the first failing assertion is the one you will actually see.

The repository shape and the empty release notes

The tree is conventional for a Composer package with real tooling: `src/` and `tests/` at the root, a `composer.json`, a `bin/` directory, a `tools/` directory, and the quality configuration files `.php-cs-fixer.php`, `phpunit.xml.dist`, `psalm.xml`, `.editorconfig`, `.gitattributes` and `.gitignore`, plus `CHANGELOG.md` and `LICENSE`. The presence of both a code style fixer config and static analysis config tells you the project is held to a standard beyond passing its own tests, and the tests directory means the assertion catalogue is exercised rather than trusted.

The release history is the weak spot, and it is worth naming plainly. Three releases in 2026, roughly one a month: 2.3.0 on 2026-04-11, 2.4.0 on 2026-05-20, 2.4.1 on 2026-06-15. Every one of those bodies is a single sentence pointing at CHANGELOG.md. So there is no way to learn what changed in a version from the release page itself; if a new assertion appeared or a placeholder meaning shifted, the changelog file in the repository is the only place that records it. Plan your reading accordingly when you upgrade.

On the numbers again: 7,649 stars with 166 forks and 30 open issues. That ratio of users to open threads is what a widely depended-on low-level library looks like, and it is also an argument for pinning rather than tracking the major version, since a behavioural change in an assertion message is exactly the kind of thing that breaks a downstream test suite.

Choosing between assertion libraries, and where the tradeoff sits

The README's own framing is the honest way to compare. This package was inspired by beberlei/assert and fixes an error-message problem in it that could not be fixed without breaking backwards compatibility. Both statements are true at once, and together they mean the two are similar in call shape but not in message compatibility: the argument lists match closely enough that migrating is mechanical, but any custom message you wrote against the old placeholder ordering has to be rewritten.

That is the actionable part of the comparison, and it applies to switching in either direction. If you adopt this package, you can write one message template and reuse it, and you will want to read the source of an assertion before assuming which assertion-specific value lands in `%2$s`. If you move away, expect your test fixtures that assert on message text to fail even where the validation logic is unchanged.

The scope question matters too. Everything here validates a value against a rule and throws on failure. Nothing in the documented surface composes rules, produces a list of violations instead of throwing on the first one, or integrates with a validation layer that already owns error collection. For method-level preconditions in a domain where `InvalidArgumentException` is the right thing to throw, that is exactly the right size of tool. For collecting all the problems with a submitted request before answering it, you would be building that on top yourself.

One last practical note from the metadata: the newest tag is 2.4.1, published minutes after the last push on the same day, and the repository is not archived. Read CHANGELOG.md before upgrading, because the release page will not tell you what you are picking up.

Editorial conclusion

webmozart/assert is a small package with a specific reason to exist, and the reason is in its FAQ rather than its feature list: consistent placeholder ordering that a message-compatible predecessor could not adopt without breaking its own users. If you are choosing between PHP assertion libraries, that single detail is the deciding factor, because every guard clause you write depends on what the message can express. Install it with `composer require webmozart/assert`, put the calls at your public method boundaries where they can throw, and read the method signature table in the README before you write custom messages, since the extra `%2$s`, `%3$s` placeholders are assertion specific and the source of each assertion is where they are documented. Two operational notes: the current tag is 2.4.1 from 2026-06-15 and the release notes carry no content of their own, so CHANGELOG.md is the only place change details live, and with 30 open issues against 7,649 stars the maintainer surface is small enough that pinning a version is worth doing.

Frequently asked questions

How do I install webmozart/assert?

Through Composer. The README gives a single command, `composer require webmozart/assert`, and links badges for the latest stable version and total downloads on Packagist. The package is MIT licensed and the current release is 2.4.1, published 2026-06-15.

What does webmozart/assert throw when an assertion fails?

Every assertion in the `Assert` class throws a `Webmozart\Assert\InvalidArgumentException`. One exception type covers all failures, which means a caller can wrap a block of validation in one try/catch, at the cost of the exception not carrying the assertion name, so the message text carries the information instead.

How does webmozart/assert differ from beberlei/assert?

The README says it was heavily inspired by beberlei/assert and fixes a usability issue with error messages that could not be fixed there without breaking backwards compatibility. The fix is consistent placeholder ordering: `%s` is always the tested value, and `%2$s`, `%3$s` and onward are assertion-specific values such as a minimum length or the allowed values.

Which assertions does webmozart/assert provide?

The README groups them into type assertions and comparison assertions. Types cover strings, integers, floats, booleans, scalars, objects, resources, arrays, iterables, callables and class relationships through `isInstanceOf`, `isAOf` and their array and negated variants. Comparisons cover booleans, null, emptiness, `eq` and `same` for loose and strict equality, and ordering.

Can I write my own error message for an assertion?

Yes, and the README's stated design goal is that usable messages are the default rather than an extra. Every assertion signature takes `$message = ''` as its last parameter, so passing a custom string overrides the default, and `%s` inserts the tested value while numbered placeholders such as `%2$s` fill assertion-specific values.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. webmozarts/assert on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/webmozarts-assert.svg)](https://hysenlabs.com/projects/webmozarts-assert)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/webmozarts-assert.svg)](https://hysenlabs.com/projects/webmozarts-assert)

Was this useful?