NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #2790 most downloaded on Packagist
Token-Oriented Object Notation - A compact data format for reducing token consumption when sending structured data to LLMs
Last release 3 months ago
08 Jul 2026
Ships unpredictably
gaps range from 9 days to 7 months
Nearly every release is documented
notes for 10 of 10 stable releases
Nothing withdrawn
no release was ever pulled
11 months old
10 releases · first in 2025
Patch release fixing a decoder round-trip regression introduced in v3.2.0.
Patch release fixing a decoder round-trip regression introduced in v3.2.0.
[ or { — for example k: "a[3]b", or log lines carrying ANSI escape sequences such as "�[31m…" — were misread as array headers and raised SyntaxException: Unterminated quoted string on decode, so the encoder's own output could not be decoded back. Header detection now locates the : separator and the [/{ markers outside quoted spans, so encoder output round-trips through Toon::decode() for every string value. The regression was introduced in v3.2.0; v3.1.0 was unaffected.tests/Spec/RoundTripSpecialStringsTest.php, tests/Spec/ComprehensiveRoundTripTest.php, tests/Spec/MalformedHeaderDecodeTest.php); line coverage raised from 90% to 96%.DelimiterParser helpers) with no change to the public decode path.Full Changelog: v3.2.0...v3.2.1
[ or { — for example k: "a[3]b", or log lines carrying ANSI escape sequences such as "[31m…" — were misread as array headers and raised SyntaxException: Unterminated quoted string on decode. Header detection now locates the : separator and the [/{ markers outside quoted spans, so encoder output round-trips through Toon::decode() for every string value. Regression introduced in v3.2.0 (v3.1.0 was unaffected). Added comprehensive bidirectional round-trip test coverage (tests/Spec/RoundTripSpecialStringsTest.php, tests/Spec/ComprehensiveRoundTripTest.php).One column per month.
This release brings the library into conformance with TOON Specification v3.3 (upstream advanced v3.0 → v3.3) and adds a validation API.
This release brings the library into conformance with TOON Specification v3.3 (upstream advanced v3.0 → v3.3) and adds a validation API.
docs/SPEC.md and docs/CHANGELOG.md.\n, \r, \t) as \uXXXX; the decoder accepts \uXXXX (case-insensitive hex) and rejects lone surrogates and escapes with fewer than four hex digits.[] and key: [] forms in addition to the legacy [0]: / key[0]: forms.Toon::validate(), toon_validate() and toon_validate_lenient() for checking TOON syntax without decoding.tests/Spec/Version31To33ComplianceTest.php.n = 0 or 1e-6 ≤ |n| < 1e21; values outside that range use exponent notation (lowercase e, explicit sign, e.g. 1e+21, 1e-7). Large in-domain floats are no longer quoted as strings, and very small numbers no longer underflow to 0.[03] (§6, §14.2), header delimiter mismatches where the bracket delimiter differs from the field-list delimiter, e.g. rows[2|]{a,b}: (§6, §14.2), and duplicate sibling keys at the same depth (§8, §14.4). Non-strict mode treats malformed bracket tokens as literal keys and applies last-write-wins for duplicate keys."App\Controller::method") now decode correctly (#4, #5).key: [] previously decoded to null and a bare [] crashed with an uninitialized-offset warning; both now decode to an empty array.- [M]: … → inner array, - key: … → object, bare - → empty object. This fixes round-trips for arrays of arrays and lists of single-field objects.null (lenient).key: (§8): decodes to an empty object, not null."my-key"[3]: is unescaped.[3]: a,,b → ['a','','b'])."a\\": c) parses.EncodeOptions/DecodeOptions reject indent < 1; EncodeOptions::compact() now uses indent: 1 so nested output round-trips.Toon::validate() now runs the full decoder internally and returns false only when decode() throws, so validation and decoding can never disagree. The separate validator implementation was removed.Full Changelog: v3.1.0...v3.2.0
docs/SPEC.md and docs/CHANGELOG.md.\n, \r, \t) as \uXXXX; decoder accepts \uXXXX (case-insensitive hex) and rejects lone surrogates and escapes with fewer than four hex digits.[] and key: [] forms in addition to the legacy [0]: / key[0]: forms.Toon::validate(), toon_validate() and toon_validate_lenient() for checking TOON syntax without decoding.tests/Spec/Version31To33ComplianceTest.php.n = 0 or 1e-6 ≤ |n| < 1e21; values outside that range use exponent notation (lowercase e, explicit sign, e.g. 1e+21, 1e-7). Large in-domain floats are no longer quoted as strings, and very small numbers no longer underflow to 0.[03] (§6, §14.2), header delimiter mismatches where the bracket delimiter differs from the field-list delimiter, e.g. rows[2|]{a,b}: (§6, §14.2), and duplicate sibling keys at the same depth (§8, §14.4). Non-strict mode treats malformed bracket tokens as literal keys and applies last-write-wins for duplicate keys."App\\Controller::method") now decode correctly (#4, #5).key: [] previously decoded to null and a bare [] crashed with an uninitialized-offset warning; both now decode to an empty array.- [M]: … → inner array, - key: … → object, bare - → empty object. This fixes round-trips for arrays of arrays and lists of single-field objects. Nested arrays of objects/arrays as list items also round-trip now.[]) in both modes, instead of throwing (strict) or returning null (lenient).key: (§8): decodes to an empty object ([]), not null."my-key"[3]: is unescaped.[3]: a,,b → ['a','','b'])."a\\": c) parses.EncodeOptions/DecodeOptions reject indent < 1 (indent 0 cannot represent nesting); EncodeOptions::compact() now uses indent: 1 so nested output round-trips.Toon::validate() now runs the full decoder internally and returns false only when decode() throws, so validation and decoding can never disagree. The separate validator implementation (which had drifted from the decoder on duplicate keys, malformed brackets, and over-indented list fields) was removed.toJSON() support : Objects with a toJSON() method can now provide custom serialization, similar to JSON.stringify in JavaScript
toJSON() method can now provide custom serialization, similar to JSON.stringify in JavaScript-05, -0001) as strings per spec §2.4toJSON() method can now provide custom serialization, similar to JSON.stringify in JavaScript. The method takes priority over JsonSerializable interface and includes recursion protectiontests/Spec/Version3BreakingChangesTest.php with 6 tests verifying v3.0 compliancetest_tabular_first_field_in_list_item_round_trip to verify v3.0 format survives encode/decode cycles-05, -0001) as strings per TOON Specification §2.4This major release aligns with TOON Specification v3.0 , which standardizes the encoding for list-item objects whose first field is a tabular array.
This major release aligns with TOON Specification v3.0, which standardizes the encoding for list-item objects whose first field is a tabular array.
When a list-item object has a tabular array as its first field:
Before (v2.x):
items[1]:
- users[2]{id,name}:
1,Ada
2,Bob
status: active
After (v3.0):
items[1]:
- users[2]{id,name}:
1,Ada
2,Bob
status: active
tests/Spec/Version3BreakingChangesTest.php with 6 tests verifying v3.0 complianceAdded 10 tests for v2.0 breaking change verification
This major release aligns with TOON Specification v2.0, which removes the optional # length marker prefix from array headers.
EncodeOptions::$lengthMarker parameter removed from constructorEncodeOptions::withLengthMarkers() preset method removedEncodeOptions::withLengthMarker() fluent setter removed[N] format (e.g., [3]: a,b,c)[#N] format with SyntaxExceptionBefore (v1.x):
$options = EncodeOptions::withLengthMarkers();
$toon = Toon::encode($data, $options);
// Output: [#3]: a,b,cAfter (v2.0):
$options = EncodeOptions::default();
$toon = Toon::encode($data, $options);
// Output: [3]: a,b,csprintf() calls that caused decimal points to become commas in locales like da_DK, de_DE. Now uses number_format() with explicit decimal separator for locale-independent formatting per TOON Spec §2.InvalidArgumentException. Only \n, \r, and \t have defined escape sequences per TOON Spec §7.1.[N#] now produces correct v2.0 error messagedocs/SPEC-COMPLIANCE.md with control character policy and v2.0 compliance detailsThis major release aligns with TOON Specification v2.0, which removes the optional # length marker prefix from array headers. The library version now matches the spec version for clarity.
EncodeOptions::$lengthMarker parameter - The optional length marker parameter has been removed from the constructorEncodeOptions::withLengthMarkers() preset - This preset method has been removedEncodeOptions::withLengthMarker() method - This fluent setter has been removed[N] format (e.g., [3]: a,b,c). The deprecated [#N] format is no longer supported.[#N] format with SyntaxException. Previously accepted both [N] and [#N] formats.Before (v1.x):
use HelgeSverre\Toon\EncodeOptions;
use HelgeSverre\Toon\Toon;
// Using preset with length markers
$options = EncodeOptions::withLengthMarkers();
$toon = Toon::encode($data, $options);
// Output: [#3]: a,b,c
// Using constructor
$options = new EncodeOptions(lengthMarker: '#');
$toon = Toon::encode($data, $options);
After (v2.0):
use HelgeSverre\Toon\EncodeOptions;
use HelgeSverre\Toon\Toon;
// Use default options (no length marker parameter)
$options = EncodeOptions::default();
$toon = Toon::encode($data, $options);
// Output: [3]: a,b,c
// Constructor no longer accepts lengthMarker
$options = new EncodeOptions();
$toon = Toon::encode($data, $options);
[#N] format[N] format[#N] format with clear error messageslengthMarker parameter and related methodsEncodeOptions::$lengthMarker propertyEncodeOptions::withLengthMarkers() static methodEncodeOptions::withLengthMarker() instance methodsprintf() calls that were locale-dependent, causing decimal points to become commas in locales like da_DK, de_DE, etc. Now uses number_format() with explicit decimal point separator for guaranteed locale-independent formatting per TOON Spec §2. This ensures output is always valid regardless of system locale settings.InvalidArgumentException. Per TOON Spec §7.1, only \n, \r, and \t have defined escape sequences. This prevents potential security issues from raw control characters in output.[N#] (hash after digits) now produces the correct v2.0 error message instead of a generic errortests/Spec/Version2BreakingChangesTest.php)docs/SPEC-COMPLIANCE.md with control character policy and v2.0 verification detailsFull TOON decoder implementation : Complete decoding functionality with strict mode support
just benchmark-performance - Run PHPBench with default reportjust benchmark-perf-summary - Run with summary reportjust benchmark-all - Run both token efficiency and performance benchmarksjust benchmark-baseline - Store current performance as baselinejust benchmark-compare - Compare against stored baselineNative PHP enum support for both BackedEnum and UnitEnum types:
Native PHP enum support for both BackedEnum and UnitEnum types:
Thanks to @AmolKumarGupta for contributing this feature!
BackedEnum and UnitEnum types
Status::ACTIVE → "active")Counting::TWO → "TWO")HttpCode::cases() → "[2]: 201,400")Empty array encoding now correctly outputs [0] length marker
[0] length marker
items:items[0]:Full changelog: https://github.com/HelgeSverre/toon-php/blob/main/CHANGELOG.md#120---2025-10-28
[0] length marker (e.g., items[0]:)Justfile for task automation with commands for setup, testing, analysis, formatting, benchmarks, quality checks, and CI workflows
Token savings compared to other formats:
Full changelog: https://github.com/HelgeSverre/toon-php/blob/main/CHANGELOG.md#110---2025-10-28
just commands for:
Toon::encode() methodTOON (Token-Oriented Object Notation) is a compact data format for reducing token consumption when sending structured data to Large Language Models.
TOON (Token-Oriented Object Notation) is a compact data format for reducing token consumption when sending structured data to Large Language Models.
composer require helgesverre/toonuse HelgeSverre\Toon\Toon;
echo Toon::encode([
'items' => [
['sku' => 'A1', 'qty' => 2, 'price' => 9.99],
['sku' => 'B2', 'qty' => 1, 'price' => 14.5]
]
]);Output:
items[2]{sku,qty,price}:
A1,2,9.99
B2,1,14.5
Full changelog: https://github.com/HelgeSverre/toon-php/commits/v1.0.0
Toon::encode() static methodEncodeOptions:
EncodeOptions with fluent APIYour coding agent can read these notes before it upgrades. Set up the MCP server →