php-dextrin

Application

Table of Contents

Interfaces

HasCategory
Implemented by any exception that knows its DXN.md §5 category -- `syntax`, `binary`, `encode/unsupported`, ... -- so {@see DXNError::wrap()} can carry it across without knowing every concrete exception class, the way {@see HasOffset} carries an offset.
HasOffset
Implemented by any exception that can report a byte/character offset into the source it failed on (lexer/parser errors) -- lets {@see DXNError::wrap()} preserve that offset without needing to know about every concrete exception class in the text/binary pipelines.
DXNSchemaMaterializingProvider
A {@see DXNSchemaProvider} that additionally supplies the schema's materializer -- called for every decoded struct of the schema, whatever its version, with the schema's name, the version the fields were read with and the checked fields; it answers the provider's own class at the latest version. Without this, a provider's schema decodes to the generic {@see \JOetjen\Dextrin\Value\DXNStruct}.
DXNSchemaProvider
Lets a class's *own* library ship a DXN schema for it -- field names, types, required/optional, refinements -- without that library ever taking a runtime dependency on this one.
DXNValueInterface
Implemented by every `DXN*` wrapper class -- the PHP analog of the `dxnType` string tag used for kind dispatch in the JS port, and of `Dextrin.Value`'s closed type union in the Elixir original. Native PHP values (`null`, `bool`, `int`, `float`, `string`, and `array` for `map`/`array`) never implement this; {@see Equality::kindOf()} handles those directly.

Classes

BinaryError
Error type for malformed `.dxnb` input -- the binary-side analog of `Text\ParseError`.
Decoder
`.dxnb` decoder -- the mirror image of {@see Encoder}. Envelope (`magic` `version` `cbor_item`) checked once at the top; everything below that is a single recursive {@see self::decodeItem()}, keyed on CBOR major type first, then on tag -- matching `dxn/DXN.md` §2.2's table row order.
Encoder
`.dxnb` encoder -- a recursive walk over CBOR's major types plus a fixed tag dispatch table, matching `dxn/DXN.md` §2.2's table row order.
FloatWidth
CBOR's half- and single-precision floats (RFC 8949 §3.3, Appendix D), which `.dxnb` writes when they hold a value exactly -- the smallest representation (DXN.md §2.2, Jan, 2026-10-20). Named for what it chooses, as `Float` is a name PHP keeps for itself.
Tags
Pure constants -- CBOR tag numbers and bit-layout tables, mirroring `dxn/DXN.md` §2.3 verbatim. No behavior beyond the flag<->byte conversions, which exist here (not in the encoder/decoder) as one shared source of truth for the bit layout, used by both directions.
Writer
Low-level CBOR item builders shared by {@see Encoder} -- raw major-type/head construction, kept separate from the DXN-type -dispatch logic there.
Canonical
The canonical form of a value (DXN.md §1.5): one `.dxn` rendering, fixed down to the byte, so values from different implementations can be compared as text.
Conformance
This implementation's answers to the Dextrin Conformance Suite.
Dextrin
Public API entry point: `.dxn` text and `.dxnb` binary decode/ encode, plus `.dxns` schema-backed struct decoding and encode-time validation via an optional {@see Registry}.
DocumentError
Why a {@see \JOetjen\Dextrin\Document} edit was refused: the path led nowhere (`document/not-found`), to no collection (`document/not-a-collection`), to a key already there (`document/exists`), or an update could not tell which edit was meant without risking a comment (`document/ambiguous`).
Formatter
A document laid out the way the pretty printer lays out a value -- every non-empty collection over lines, one entry per line, indented -- with every comment kept beside the entry it belongs to, mirroring `Dextrin.Document.Formatter`.
Document
A `.dxn` document with everything it holds: its value, and every comment, blank line and comma around it (`design/comment-retention.md` in `dextrin`), mirroring `Dextrin.Document`.
DXNError
One exception type for every `dextrin` failure mode -- text-parse, printing, and (in later phases) binary/schema failures alike -- so a caller never has to special-case which pipeline produced it.
Format
The three forms' names on the wire and on disk, and a cheap test of whether a body is worth decoding as one -- DXN.md §6, mirroring `Dextrin.Format`, so every implementation answers the same.
KeyNotAllowed
A map key that DXN.md §1.6 does not allow, with its `key/not-allowed` category.
MapBuilder
Builds a decoded DXN `map` -- shared by the text {@see \JOetjen\Dextrin\Text\Parser} and the binary {@see \JOetjen\Dextrin\Binary\Decoder}, which need identical logic.
MapKey
Which values may be map keys (DXN.md §1.6): a string, keyword, integer, uuid, objectid, date, bytes, symbol or char. Anything else is refused as `key/not-allowed` -- in text, in binary and when encoding.
Registry
Extension point for both `struct` and `custom-tag` decoding/encoding.
Absent
What {@see Validator} answers for an optional field that was not given and has no default: absent, which DXN tells apart from a field written `nil`. One instance, compared by class.
Compat
The version discipline check (DXNS.md §8), mirroring `Dextrin.Schema.Compat`: compares the schemas of a previous release with the current ones.
Compiled
The result of compiling one `%schema(N){}` entry from a `.dxns` document ({@see Compiler}) -- enough to both validate a decoded value and convert it both directions between `.dxnb`'s always-positional wire shape and a named field map.
Compiler
Compiles a parsed `.dxns` document (an ordinary decoded DXN value -- a `.dxns` file is valid `.dxn`, no new grammar; what makes it a *schema* document is purely the shape of the value it parses to: a `map` from name to type expression -- a bare PHP array in this port) into {@see Compiled} entries.
Context
What a check knows besides the type and the value: the registry (`null` when there is none -- strict is then off), and the fields of the nearest enclosing struct or shape by name, which a `{:case}` reads its sibling from (`null` outside any).
Docs
HTML documentation for a directory of `.dxns` schema documents -- the port of `Dextrin.Schema.Docs`, writing the same site byte for byte (Jan, 2026-10-20): a sidebar tree of every namespace and definition, the index and every namespace's page in one design, the pages a directory tree with an `index.html` per namespace, every reference a link, every version shown. It reads the documents' source, not their compiled schemas.
FetchedSchema
The result of a successful {@see Registry::fetchStructSchema()} -- the compiled schema itself, plus the (possibly-updated) registry a lazy resolver hit should be threaded forward through, so a resolved-on-demand schema is only ever resolved once per registry lineage.
Field
One compiled field spec inside a {@see Compiled} schema. `required` comes from the `?`-suffixed key convention (a field key ending in `?` is optional; no separate flag exists in `.dxns` itself); `default`/`description` only ever come from the `%field{...}` escape hatch, since optionality is already fully covered by the key suffix.
FileResolver
A resolver ({@see Registry::putResolver()}) finding a struct's schema in `.dxns` files on disk, by the convention DXNS.md §6 states for every implementation -- so one set of schema files serves them all.
Layout
The names a `.dxns` file's entries have, from where the file lies under its root (DXNS.md §6) -- one rule for every reader of a tree: the docs generator, {@see Tree} and the file resolver; the port of `Dextrin.Schema.Layout`.
Particle
One of a `{:list-items}`'s items (DXNS.md §1a): a type for exactly one item, or a repeat of a group of them, `$max` `null` when there is no upper bound.
Provider
Compiles and registers one {@see DXNSchemaProvider} into a {@see Registry}, in order:
Reader
Which schema a decoded struct is read with, given the version it was written with and the versions loaded (DXNS.md §8). Shared by the text parser and the binary decoder, so both answer every case the same way -- mirrors `Dextrin.Schema.Reader`:
SchemaCompileError
Thrown by {@see Compiler} for a malformed `.dxns` document, with the `schema/compile` category.
SchemaValidationError
Thrown by {@see Validator}/{@see \JOetjen\Dextrin\Schema} for a value that violates its schema, with the violation's DXN.md §5 category (`schema/missing-field`, ...), which every channel passes on unchanged. Null only where a caller asked for something that is not a DXN error at all, such as a schema that was never registered.
Std
This library's standard library of named types (`priv/schema/std.dxns`, ported verbatim from `dextrin`'s own) -- common refinements like `PositiveInteger`/`NonEmptyString`, so a schema author doesn't redefine them by hand. Built the same way any consumer's own named types would be -- nothing about them is special-cased in {@see Compiler}. Deliberately excludes anything domain-specific (email, phone number, URL-shaped string): what counts as a valid one is an application decision this library shouldn't guess at.
Tree
A tree of `.dxns` files read as one (Jan, 2026-10-20), the port of `Dextrin.Schema.Tree`: every entry named from where its file lies (DXNS.md §6, {@see Layout}), the files compiled in the order their references need -- so a reference to another file's named type resolves whatever order the files sort in -- and every problem reported with its file and line:
TypeExpr
Internal, compiled representation of a `.dxns` type expression -- the vocabulary of DXNS.md §1 (`any`, `never`, `primitive`, `reference`, `list-of`, `list-items`, `set-of`, `tuple-of`, `tuple-of-all`, `map-of`, `array-of`, `tagged`, `doc`, `if`, `ordered-map-of`, `sorted-set-of`, `enum`, `one-of`, `any-of`, `all-of`, `not`, `nilable`, `refine`) {@see Compiler} turns a parsed `.dxns` value into, and what {@see self::matches()} checks a decoded/to-be-encoded value against. (The 13th `dxn/DXN.md` §4 form, `struct`, isn't a distinct compiled shape here either -- a `%schema{}` entry compiles straight to {@see Compiled}, never to a `TypeExpr` a *field* could hold; `reference` is what a field uses to point at one.)
Validated
Internal only -- never appears in a value handed back to `Dextrin::decode()`/`decodeBinary()`'s caller.
Validator
Checks a value's fields against a {@see Compiled} schema (required fields and their types) -- shared by decode ({@see self::materialize()}, which also produces a materialized result) and encode-time validation ({@see self::validateForEncode()}/{@see self::validateTreeForEncode()}). Both directions reuse the exact same `resolveFields()`/`TypeExpr::matches()` field-checking; encode's own values are wrapped in the same {@see Validated} marker decode already uses, via `wrapAndCheck()`, so there's one type-checking implementation, not two that could drift.
Schema
Public entry point for `.dxns` schema compilation and validation.
Escapes
Shared escape decoding/encoding for DXN's `string`/`char`/quoted -`keyword` bodies (`dxn/DXN.md` §1.1's `escape` production) -- one implementation so no caller duplicates it.
Formatter
Multi-line, indented `.dxn` rendering -- the `pretty: true` option on `Dextrin::encode()`, built on top of {@see Printer}. Every empty collection and every scalar falls through to the single-line printer unchanged (there's nothing multi-line about `[]` or `42`).
Lexer
Hand-written `.dxn` tokenizer, directly off `dxn/DXN.md` §1.1's lexical grammar. No parser-generator dependency -- the grammar is small and fully normative.
LexError
ParseError
Parser
Hand-written recursive-descent `.dxn` parser -- the reverse of {@see Printer}, built directly on {@see Lexer}'s token stream.
Printer
`.dxn` printer -- the reverse of {@see Parser}. Single-line, minimal-whitespace output: a printer, not a formatter (no line-wrapping/indentation policy is specified anywhere in `dxn/DXN.md`) -- {@see Formatter} owns multi-line, human-readable rendering, built on top of this class.
SyntaxNode
One node of a document's syntax tree, as {@see Parser::parseTree()} records it: its kind, its first and last token (indices into the tokens), the nodes inside it, and which of its tokens open it, key it, or name it. The trivia between two tokens is the text between them.
Temporal
DXN's dates and times as text (DXN.md §1.2, §1.5; Jan, 2026-10-20): the ISO 8601 strings under `@date`, `@time`, `@datetime`, `@local-datetime` and `@duration`, read and written in one place so the parser, the printer and the canonical form never drift apart.
Token
One lexed token. `start`/`end` are **codepoint indices**, not byte offsets -- {@see Lexer} operates on the source split into an array of single-codepoint strings (`preg_split('//u', ...)`) rather than raw byte-indexed PHP strings, which sidesteps multi-byte UTF-8 sequences entirely (a JS port indexes UTF-16 code units natively; PHP strings are raw bytes, so this port picks codepoints as its own natural, consistently-defined unit instead).
UnsupportedValue
A value with no DXN meaning -- a closure, a resource -- refused by the printer and the binary encoder alike, with DXN.md §5's `encode/unsupported` category.
DXNBits
DXN `bits` (DXN.md §1.4): a run of bits that is not whole bytes -- `@bits {"oA==" 3}` in text, `T_BITS` around `[bytes, count]` in `.dxnb`. PHP has no bitstring, so this holds the bytes, the unused low bits of the last one zero, and how many bits are the value.
DXNBytes
DXN `bytes` (`@bytes "..."`, base64 in text) -- raw binary data.
DXNChar
DXN `char` (`?a`) -- a single Unicode codepoint, distinct from a 1-codepoint `string`.
DXNCustomTag
DXN `custom-tag` (`@tag value`) -- the open extension point, no decoder registered for `name`.
DXNDate
DXN `date` (`@date "YYYY-MM-DD"`) -- a bare calendar date, ISO 8601.
DXNDateTime
DXN `datetime` (`@datetime "2026-10-20T14:30:00Z"`) -- an instant with its offset, `Z` for UTC (DXN.md §1.3). DXN 0.2's `timestamp`, a UTC instant, is one of these at offset zero (Jan, 2026-10-20). Mirrors `.dxnb`'s wire shape directly: the instant as nanoseconds since the epoch, and the offset it was written with, in minutes.
DXNDecimal
DXN `decimal` (`19.99M`) -- an exact fixed-point number, `sign * unscaledValue * 10^exponent`. `unscaledValue` is always a numeric string (bcmath-formatted), independent of the top-level integer hybrid rule ({@see DXNInteger}) -- exact decimal arithmetic needs bcmath regardless of magnitude, so there's no native-`int` fast path to preserve here the way there is for a bare `integer`.
DXNDuration
DXN `duration` (`@duration "P1Y2M"`) -- 7 independent, individually optional fields, not reducible to one scalar. Seconds and their fraction are one field, `nanoseconds` (Jan, 2026-10-20); a field's sign is its own, `P-1DT2H`. No conversion helper: PHP's `DateInterval` exists, but normalizes `P1W` into `days = 7` internally, losing the weeks/days distinction DXN keeps independent -- not a clean conversion target.
DXNInteger
An arbitrary-precision integer, backed by `bcmath` (no `ext-gmp` assumed available) -- only ever constructed for a magnitude outside `PHP_INT_MIN..PHP_INT_MAX`. {@see DXNInteger::represent()} is the canonical entry point embodying the project's hybrid integer rule: a value that fits stays a native `int` everywhere (ordinary `+`, `-`, comparisons just work); only genuine overflow reaches this class, whose own arithmetic is method calls, not operators -- PHP has no operator overloading for arithmetic.
DXNIp
DXN `ip` (`@ip "..."`, Jan, 2026-10-20): an IPv4 or IPv6 address, or a prefix -- an address and a length. `$address` holds the raw bytes, 4 or 16; `$prefix` is the length, or null for an address. A prefix's host bits are zero (RFC 9164). In `.dxnb` it is CBOR's tag 52 or 54, raw bytes.
DXNKeyword
DXN `keyword` (`:foo` / `foo:` in key position) -- always wraps in this port, with no `trusted:`-driven alternate representation the way the Elixir (real atom) and JS (`Symbol.for`) ports have. PHP has nothing analogous to a BEAM atom table or `Symbol.for`'s global registry: every `DXNKeyword` is an ordinary heap-allocated, GC'd object, so there's no exhaustible interning table decoding untrusted input could ever threaten in the first place -- this is a deliberate omission, not a missing feature.
DXNList
DXN `list` (`[1 2 3]`) -- wrapped so it stays distinct from `array` (`@array[...]`), which claims the bare sequential-PHP-array shape instead (see the project's collections decision: PHP's `array` only offers one structural fork, sequential-vs-associative, so it can't safely host two DXN types).
DXNLocalDateTime
DXN `local-datetime` (`@local-datetime "2026-10-20T14:30:00"`) -- a date and a time on the wall clock, with no offset, so no instant (DXN.md §1.3; Jan, 2026-10-20). Held as `.dxnb` holds it: nanoseconds since 1970-01-01T00:00:00 on the wall clock.
DXNMap
A DXN map that a native PHP array cannot hold.
DXNObjectId
DXN `objectid` (`@objectid "..."`, Jan, 2026-10-20): BSON's 12-byte ObjectId, built in like `uuid` -- readable in text, its raw bytes in `.dxnb` (tag 221). The value holds the bytes; the 24 hex digits exist only at the text boundary.
DXNOrderedMap
DXN `ordered-map` (`@ordered %{...}`) -- order is part of value identity. Wrapped rather than a bare associative PHP array (which already claims `map`, the no-ordering-guarantee sibling) so the two stay distinguishable on encode; also because a DXN map/ordered-map key can be any DXN value, not just an `int|string` the way a native PHP array key must be.
DXNRational
DXN `rational` (`22/7`) -- an exact ratio, never auto-reduced (`4/2` and `2/1` are structurally distinct values, same as the Elixir and JS ports). `numerator`/`denominator` are numeric strings, same "always bcmath, regardless of magnitude" rule as {@see DXNDecimal}.
DXNRegex
DXN `regex` (`@regex "pattern"`, or `@regex(flags) "pattern"`) -- its flags in a header, PCRE's inline modifiers part of the pattern, so `@regex "(?i)a"` is a different value from `@regex(i) "a"` (DXN.md §1.2). PCRE-style semantics (matching Erlang `:re`/Elixir `Regex`, per `dxn/DXN.md` §2.3), which happens to make PHP's own PCRE-backed `preg_*` a *better* native fit than JS's `RegExp` had: DXN's `x` (extended) and `r` (ungreedy) flags both have real PHP modifier equivalents (`r` maps to PHP's uppercase `U`, distinct from lowercase `u`/unicode) where JS had neither. Only `f` (firstline) has no PHP preg modifier to reach for, so {@see self::toPcrePattern()} is guarded on that alone, not on `x`/`f`/`r` wholesale.
DXNSet
DXN `set` (`@{...}`) -- deduplicated at construction, structural equality, order-independent.
DXNSortedSet
DXN `sorted-set` (`@sorted-set @{...}`) -- sorted and deduplicated at every construction site, same invariant enforced in the Elixir and JS ports. Cross-type ordering is implementation-defined per DXN.md; this port's fixed rank order is {@see Equality}'s own.
DXNStruct
DXN `struct` (`%Name{...}` / `%Name[...]`), opaque without a schema. Keeps the keyed/positional shapes distinct rather than normalizing to one, same as the Elixir and JS ports -- `.dxnb` is always positional and can't recover field names without a schema.
DXNSymbol
DXN `symbol` (a bare identifier, e.g. `foo`) -- an unevaluated reference, e.g. a type name. Always wraps: PHP has no "evaluated later" bare-identifier native type, and no interning table whose exhaustion decoding untrusted input would need to guard against either way.
DXNTime
DXN `time` (`@time "HH:MM:SS(.f{1,9})?"`) -- a bare time of day, ISO 8601, to the nanosecond (DXN.md §1.3; Jan, 2026-10-20). No conversion helper: PHP has no native "time of day" type to convert to/from without a synthetic, misleading date component.
DXNTuple
DXN `tuple` (`{1 2 3}`) -- list-backed, arbitrary length.
DXNUri
DXN `uri` (`@uri "..."`) -- a raw string, not parsed, same round-trip-safety rationale as the Elixir and JS ports.
DXNUuid
DXN `uuid` (`@uuid "..."`) -- 16 raw bytes, RFC 4122. PHP strings are binary-safe natively, so the raw-bytes field itself needs no `Uint8Array`-style wrapper the way JS needed one.
Equality
Shared structural equality/ordering kernel backing every DXN value, native and wrapped alike -- the PHP analog of the JS port's `src/values/equality.cjs`. `kindOf()` dispatches on {@see DXNValueInterface::dxnType()} for wrapped classes, PHP's own native types for scalars, `array_is_list()` to split `array`/`map`, and "any other object" (a plain object with public properties, `stdClass` or otherwise) as `struct` -- the encode-side convenience described in the project's own value-model mapping.

Enums

Form
One of DXN's three forms: text (`.dxn`), binary (`.dxnb`) and schemas (`.dxns`). See {@see Format}.
NoDefault
A payload-free enum used purely as a unique sentinel value ({@see Field::NO_DEFAULT}), comparable via `!==` -- PHP has no `undefined`/ `Symbol()` to reach for the way JS's own `Field.NO_DEFAULT` does, and a plain `null` would be ambiguous with a field whose declared default genuinely *is* `null` (DXN `nil`).
NoMatch
What {@see TypeExpr::conform()} answers when a value does not match: a value of its own, since `null` and `false` are values a type takes.
On this page

Search results