Skip to content

Support LIVR 2.5 - #40

Merged
antonfin merged 4 commits into
masterfrom
livr-2.5
Nov 8, 2026
Merged

antonfin merged 4 commits into
masterfrom
livr-2.5

Conversation

@koorchik

@koorchik koorchik commented Nov 7, 2026

Copy link
Copy Markdown
Member

Hi! First of all, thank you for creating and maintaining php-validator-livr, and for reviewing and merging the earlier pull requests so quickly.

What LIVR 2.5 is

LIVR 2.5 was released on 2026-11-07. It only adds rules to LIVR 2.0: every 2.0 schema keeps working, except for a few small fixes of 2.0 behavior (section 1 of the spec) that only change schemas that never worked as written.

The reference implementation is JS livr 2.13. Perl, Dart, Rust and Raku were released with LIVR 2.5 too.

What this PR does

New rules (in lib/Validator/LIVR/Rules/, new files only):

  • types and numbers: boolean, not_one_of, max_decimals
  • strings: digits, starts_with, ends_with, contains
  • dates and times: iso_date with min and max, iso_datetime, iso_time
  • presence and metarules: required_if, when, values, keys, any
  • lists and modifiers: list_length, unique, split
  • formats: uuid, ip, cidr, hostname, base64, hex, digest, mongo_id, hex_color, mac, e164, semver, jwt
  • checksums: luhn, credit_card, iban

A rule the user registers under one of these names still replaces the built-in one. A schema with wrong arguments of the new rules throws when the validator is built.

Fixes of LIVR 2.0 behavior (with the 2.0 forms kept working):

  • Aliases and or are transparent: their rules see the siblings of the field, so equal_to_field works there. In an item of list_of, a reference to another field is a missing value (before, it compared the item with itself).
  • eq and one_of compare booleans as strings: {"eq": true} accepts true and "true". Everything 2.0 accepted still passes: {"eq": true} accepts "1" and 1, {"one_of": ["1"]} accepts true.
  • iso_date: an argument that is not an options object ({"iso_date": "today"}) is a schema error; before, it was silently ignored. "iso_date", {"iso_date": []} and {"iso_date": null} are unchanged. A number or a boolean gives WRONG_DATE instead of FORMAT_ERROR.
  • Meta rules and like ignore extra arguments instead of losing the validator's custom rules.

Test suites: the 2.0 suite copy is updated, keeping the PHP adjustments of negative/22-not_empty_list and negative/27-any_object as before. test_suite_2.5 is added with its own runner (tests/06-livr_test_suite_2_5.php).

Docs: a "LIVR 2.5" section in the README.

Things worth knowing

  • Zero new runtime dependencies. php-mock/php-mock-phpunit is added as a dev dependency only: the tests use it to fix the clock for today / yesterday / tomorrow. The README shows how users can do the same.
  • API addition: the constructor also accepts an options array, new Validator\LIVR($rules, ['autoTrim' => false]), which turns auto trim off even when defaultAutoTrim is on. The boolean second argument works as before.
  • Skipped cases: positive/35-object_or_list and negative/35-object_or_list of the 2.5 suite. PHP decodes {} and [] to the same array, so they can't be told apart.
  • Performance: I compared the 2.0 rules with master on PHP 8.5 (23 interleaved old/new pairs on a pinned core, medians). Validation is as fast or faster (a list of objects about 10% faster); building a validator is within 0–4% of master.
  • The commits are split into: the 2.0 suite with the 2.0 fixes, the 2.5 suite, the new rules, and the README.

Tests

On PHP 7.2 and 8.5, after a clean composer update (the CI steps, including phpcs):

  • 355 tests, 683 assertions, 0 failures, 2 skipped (the two cases above)
  • LIVR 2.0 suite: all cases pass
  • LIVR 2.5 suite: positive 34/34, negative 34/34 (plus 1 skipped in each), schema_errors 179/179

About how this was made

To be transparent: this work was done with an LLM assistant (Claude). It was checked carefully against the spec and both test suites on the lowest and the highest PHP versions in CI. I would still be very grateful for your review.

What I'd ask

A review and, if it looks good, a merge and a release whenever it's convenient for you. I'm happy to change anything: naming, structure, docs, or to split the PR differently.

Thank you!

The test suite is copied from the LIVR repository again. As before, the
copy keeps negative/22-not_empty_list and negative/27-any_object adjusted,
as PHP decodes {} and [] to the same array. These are the fixes of LIVR 2.0
behavior from section 1 of the LIVR 2.5 specification.

- Aliases and "or" are transparent: their rules see the siblings of the
  field, so equal_to_field compares with a sibling there. In an item of
  list_of a reference to another field is a missing value (it compared the
  item with itself). The data a field rule gets is not trimmed again.
- eq and one_of compare booleans as strings: {"eq": true} accepts true and
  "true", {"eq": "false"} accepts false. What LIVR 2.0 accepted still
  passes: {"eq": true} accepts "1" and 1, {"one_of": ["1"]} accepts true.
- Meta rules and like ignore extra arguments instead of losing the custom
  rules of the validator.
- The test runners read only the directories of the test suite.
A copy of test_suite_2.5 from the LIVR repository: the cases of the rules
added in LIVR 2.5, aliases, and schema_errors with the schemas that must
fail to build.
LIVR 2.5 only adds rules to LIVR 2.0:

- types and numbers: boolean, not_one_of, max_decimals
- strings: digits, starts_with, ends_with, contains
- dates and times: iso_date with min and max, iso_datetime, iso_time
- presence and metarules: required_if, when, values, keys, any
- lists and modifiers: list_length, unique, split
- formats: uuid, ip, cidr, hostname, base64, hex, digest, mongo_id,
  hex_color, mac, e164, semver, jwt
- checksums: luhn, credit_card, iban

The new rules are looked up after the rules of a validator, so a registered
rule with the same name replaces them. A schema with wrong arguments of
these rules throws when the validator is built, {"rule": null} included.

iso_date takes an options object with min and max now. Its LIVR 2.0 forms
are unchanged, {"iso_date": null} included; another argument, such as
{"iso_date": "today"}, throws. A number or a boolean gives WRONG_DATE.

The constructor also takes an array of options: [ 'autoTrim' => false ]
turns auto trim off even when defaultAutoTrim is on.

No new runtime dependencies. php-mock-phpunit is a dev dependency: the
tests fix the clock for "today" with it.
The rules added in LIVR 2.5, the changes of the LIVR 2.0 rules, the options
array of the constructor and how tests fix the current date.
@antonfin
antonfin merged commit 04ae1ba into master Nov 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants