Value
Table of Contents
Interfaces
- 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
- 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.