Skip to content

Released version 1.4.0

Latest

Choose a tag to compare

@dg dg released this 22 Oct 02:22
· 28 commits to master since this release

Your schema can now be the documentation of your configuration: give elements a
description, export the schema as JSON Schema, and editors offer completion, explain
each option and underline mistakes right in the config file.

✨ New Features

  • JsonSchema::export() – turns a schema into a JSON Schema
    document, the format editors read to validate and complete JSON or YAML files. It exports
    types, ranges, patterns, required items, enum values and descriptions. A type that exists
    only in PHP, such as a class, throws NotSupportedException instead of exporting something
    the schema would not accept.
  • description() – attach a human-readable description to any element. It does not affect
    validation, and it travels into the JSON Schema export.
  • Expect::enum() – stop hand-rolling the conversion of backed enums:
    Expect::enum(Suit::class) accepts the backing value or the case itself and always gives
    you the case. An enum-typed property in Expect::from() maps to it automatically.
    Expect::type(Suit::class) keeps meaning plain instanceof, as for any other class.
  • Expect::tuple() – a fixed-size list where each position has its own schema:
    Expect::tuple([Expect::string(), Expect::int()]). otherItems() describes the rest
    elements, extend() appends positions, and when configurations are layered, a later tuple
    replaces the earlier one as a whole.
  • Expect::listOf() with wrap – Expect::listOf('string', wrap: true) also accepts a single
    value and wraps it into a one-item list, so both email: foo@example.com and
    email: [a@example.com, b@example.com] work.
  • mergeMode() and mergeWith() – say in the schema how layered configurations combine
    instead of relying on the deep merge: Expect::arrayOf('string')->mergeMode(MergeMode::Replace)
    takes the later array as a whole, while OverwriteKeys and AppendKeys merge by keys. Any
    element can get its own combining function, such as
    Expect::bool()->mergeWith(fn($value, $base) => $value || $base). A schema without them
    merges exactly as before, and the same calls keep working in 2.0.
  • castTo('?type') – castTo('?string') keeps a null and casts everything else. A class
    or an enum keeps the null even without the question mark, so
    Expect::string()->nullable()->castTo(DateTime::class) just works.
  • Kind-specific element classes – Expect::string() returns a StringType, Expect::int()
    a NumberType, Expect::listOf() an ArrayType, so your IDE and PHPStan know which options
    each element has. They are subclasses of Type, so existing code keeps working.

⚠️ Deprecations

  • before() called repeatedly – a second before() on the same element still replaces the
    first one, but raises a notice: in 2.0 the handlers will chain in declaration order. Merge
    them into one.
  • '?int[]' – in 1.x it means an array of nullable items, while 2.0 reads it as a nullable
    array, as phpDoc does. The notice names the replacement that keeps the meaning:
    Expect::arrayOf(Expect::type('int')->nullable()).
  • Options that do not fit the element – min(), max(), pattern(), items() and
    mergeDefaults() on a union such as Expect::type('int|string') belong to the variants:
    Expect::anyOf(Expect::int()->min(1), Expect::string()->min(1)). Likewise pattern() on a
    number or an array, items() on a string or a number and mergeDefaults() on anything but
    an array raise a notice.

🐛 Bug Fixes

  • A nullable anyOf() containing a structure or an array gives null for a null input,
    wherever the null sits among the variants.
  • Smaller fixes: anyOf()->deprecated() emits its warning, min() and max() keep the
    precision of large integer bounds, and the internal _prevent_merging marker no longer
    appears in the output of a structure.