PHPackages                             mbolli/php-ron - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. mbolli/php-ron

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

mbolli/php-ron
==============

Performance-focused PHP implementation of RON (Readable Object Notation)

v0.4.1(1mo ago)07↓50%[3 issues](https://github.com/mbolli/php-ron/issues)1MITPHPPHP &gt;=8.3CI passing

Since Jun 15Pushed 1mo agoCompare

[ Source](https://github.com/mbolli/php-ron)[ Packagist](https://packagist.org/packages/mbolli/php-ron)[ RSS](/packages/mbolli-php-ron/feed)WikiDiscussions master Synced 2w ago

READMEChangelogDependencies (16)Versions (7)Used By (1)

php-ron
=======

[](#php-ron)

[![PHP](https://camo.githubusercontent.com/4b1fbf4c64e526278a701c6a94a5fe5fbc7dc9e7e6aac2d10444860a268dc273/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d254532253839254135382e332d3737374242343f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://www.php.net/)[![CI](https://github.com/mbolli/php-ron/actions/workflows/ci.yml/badge.svg)](https://github.com/mbolli/php-ron/actions/workflows/ci.yml)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)[![Downloads](https://camo.githubusercontent.com/acedf74648fbcf8cd3cb956b12bb9d958b198fece2bebb7f615ea5d8f32da6f9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d626f6c6c692f7068702d726f6e)](https://packagist.org/packages/mbolli/php-ron)[![Tests](https://camo.githubusercontent.com/9611223d4d0bbb63c8c0b47912a0475805d263edc004a1035c2f69a6f4546f6d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d32373025323070617373696e672d627269676874677265656e)](tests/)[![PHPStan](https://camo.githubusercontent.com/b72adb1f27170ecf486459c4b07e920bb3db2b464444bce8277e018270665646/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e)](.phpstan.neon)[![Code style](https://camo.githubusercontent.com/bc641a0a57fb69441d51e7a073593e1b5cc4d6c3ca4323fff1bd9d6f8bb3c027/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64652532307374796c652d7068702d2d63732d2d66697865722d627269676874677265656e)](.php-cs-fixer.php)

A performance-focused PHP implementation of [RON (Readable Object Notation)](https://github.com/starfederation/ron).

RON keeps JSON's value model but drops avoidable syntax: top-level object braces can be elided, strings can be bare, commas are optional separators, and quoted strings use repeated `'`/`"` delimiters with no backslash escapes. It converts losslessly to and from JSON and is cheaper for humans and LLMs to read and write.

Reach for it wherever you'd use JSON but a person or an LLM authors or reads the data — config files, fixtures, logs, and prompt/context payloads where the saved quotes and braces add up to real tokens. Because every RON document maps 1:1 to a JSON value, you can adopt it only at those edges (author or display RON, keep storing and transmitting JSON) without changing your data model.

This library is a port of the reference Go implementation ([ron-go](https://github.com/starfederation/ron-go)) and passes the upstream conformance corpus (`testdata/conformance`) and the RFC 8785 corpus (`testdata/rfc8785`).

Requirements
------------

[](#requirements)

- PHP &gt;= 8.3 (`ext-hash` for `sha256`, `ext-mbstring`)

Install
-------

[](#install)

```
composer require mbolli/php-ron
```

Usage
-----

[](#usage)

```
use Mbolli\Ron\Ron;

// Encode/decode arbitrary PHP values, like json_encode/json_decode
Ron::encode(['name' => 'Ada', 'active' => true]); // active true\nname Ada\n
Ron::encode($data, pretty: false);                // compact RON
Ron::decode("name Ada\nactive true");             // ['active' => true, 'name' => 'Ada']

// RON -> JSON (compact by default, canonical key order)
Ron::toJson("name Ada\nactive true");          // {"active":true,"name":"Ada"}
Ron::toJson($ron, pretty: true);                // multiline JSON

// JSON -> RON (pretty by default, canonical key order)
Ron::fromJson('{"name":"Ada","active":true}'); // active true\nname Ada\n
Ron::fromJson($json, pretty: false);            // compact RON

// Canonical RON and its SHA-256 hash (64 lowercase hex)
Ron::canonicalRon($json);
Ron::canonicalHash($json);

// RFC 8785 (JCS) canonical JSON
Ron::canonicalJson($json);
```

Invalid input throws `Mbolli\Ron\RonException`.

### Typed-value render hooks

[](#typed-value-render-hooks)

`fromJson` accepts an optional hook that rewrites JSON values before rendering, e.g. to emit typed RON forms. The hook receives the path (object keys as strings, array indices as ints; root is `[]`) and the value, and returns `[replacement, replaced]`:

```
Ron::fromJson($json, mapper: function (array $path, mixed $value): array {
    if ($path === ['committed']) {
        return [['#utc' => $value], true]; // -> committed {#utc ...}
    }
    return [$value, false];
});
```

### Typed vocabularies

[](#typed-vocabularies)

A typed value is a single-key object whose key starts with `#`, e.g. `{"#utc": "..."}`, which RON renders compactly as `{#utc ...}`. This rendering is always on. Optionally, `fromJson` can validate typed payloads against the [official vocabularies](https://github.com/starfederation/ron/blob/main/docs/vocabularies.md)(core, time, network, math, spatial, color, geo). The **core** vocabulary is enabled by default; the rest are opt-in. Pass `vocabularies: []` to disable validation entirely.

```
use Mbolli\Ron\Vocabulary\VocabularyRegistry;

// Core is validated by default: a malformed payload throws RonException.
Ron::fromJson('{"id":{"#uid":"not-a-uuid"}}');                       // throws

// Enable additional vocabularies explicitly.
Ron::fromJson($json, vocabularies: [
    VocabularyRegistry::CORE_V1,
    VocabularyRegistry::TIME_V1,
]);

// Validate without rendering.
Ron::validate($json, [VocabularyRegistry::SPATIAL_V1]);

// Register a custom, namespaced vocabulary. A validator returns true to accept the
// payload, false to reject it, or any other value to replace it (a transform); to
// replace it with a literal boolean, return VocabularyRegistry::replace($bool).
$registry = VocabularyRegistry::official();
$registry->register('https://example.com/vocab/invoice/v1', [
    '#com.example/money' => static fn (mixed $payload): bool => is_array($payload) && count($payload) === 2,
]);
Ron::fromJson($json, vocabularies: ['https://example.com/vocab/invoice/v1'], registry: $registry);
```

Unknown typed values are left as ordinary objects; enabling a vocabulary the registry does not support throws.

Scope
-----

[](#scope)

This implementation matches ron-go: RON&lt;-&gt;JSON conversion (compact/pretty/canonical), RFC 8785 canonical JSON, the typed-value render hook, and typed-vocabulary validation (validation runs on the JSON-&gt;RON path; `toJson` is not validated). Validation preserves the value model, so vocabulary-tagged objects always round-trip losslessly.

Performance
-----------

[](#performance)

The hot paths scan bytes with native C functions (`strcspn`/`strpos`) instead of per-character PHP loops, sort object keys with `array_multisort` (no per-comparison PHP callback), and stream RON-&gt;JSON directly without an intermediate tree. The canonical hash uses native `hash('sha256')`.

Measured throughput (OPcache + JIT), flat and linear from ~1.5 KB to ~320 KB, on the same input against the Go reference ([ron-go](https://github.com/starfederation/ron-go), compiled):

Conversionphp-ronron-go (Go)vs ron-goJSON -&gt; RON (compact)~18 MB/s~45 MB/s~2.5x slowerJSON -&gt; RON (pretty)~15 MB/s~18 MB/s~on parRON -&gt; JSON (compact)~14 MB/s~183 MB/s~13x slowercanonical hash~18 MB/s—On pretty JSON-&gt;RON the two are ~on par. The compiled reference is faster on the other paths: ron-go's compact JSON-&gt;RON takes an unordered-decode fast path (~2.5x), and its RON-&gt;JSON streams through a dedicated hot path with pooled buffers (~13x) — the realistic interpreter tax for byte-level streaming against optimized Go. A 1-10 KB payload still converts in well under a millisecond.

Numbers are from one ~31 KB document on one machine (php-ron on PHP 8.4 + JIT; ron-go `main`, compiled with Go 1.26); run `composer benchmark` (or `php bin/benchmark.php`) to reproduce locally.

Testing
-------

[](#testing)

The suite runs against three pinned upstream corpora (each a git submodule): the official RON [conformance corpus](https://github.com/starfederation/ron) (exact RON &lt;-&gt; JSON byte matches plus canonical SHA-256 hashes, plus the typed-vocabulary fixtures for rendering and validation), its RFC 8785 corpus, and [nst/JSONTestSuite](https://github.com/nst/JSONTestSuite), whose every valid document is round-tripped through both `fromJson`/`toJson` and `encode`/`decode` to prove conversion is lossless. Static analysis runs at PHPStan level 9 with php-cs-fixer.

Development
-----------

[](#development)

`composer install` initializes and updates the corpus submodules automatically (via a post-install hook), so the following is enough after cloning:

```
composer install
composer test     # or: composer check  (lint + phpstan + test)
```

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance92

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

6

Last Release

38d ago

PHP version history (2 changes)v0.1.0PHP &gt;=8.1

v0.4.1PHP &gt;=8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/87558ba85a089fa7793e2b315085e9b17c355f867c34b782b7da81fd6d84946d?d=identicon)[mbolli](/maintainers/mbolli)

---

Top Contributors

[![mbolli](https://avatars.githubusercontent.com/u/722725?v=4)](https://github.com/mbolli "mbolli (34 commits)")

---

Tags

canonicalizationdata-formatjsonllm-toolsphpreadable-object-notationrfc8785ronserialization

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/mbolli-php-ron/health.svg)

```
[![Health](https://phpackages.com/badges/mbolli-php-ron/health.svg)](https://phpackages.com/packages/mbolli-php-ron)
```

###  Alternatives

[sauladam/shipment-tracker

Parses tracking information for several carriers, like UPS, USPS, DHL and GLS by simply scraping the data. No need for any kind of API access.

9843.5k](/packages/sauladam-shipment-tracker)[jstewmc/rtf

Read and write Rich Text Format (RTF) documents with PHP

45153.1k6](/packages/jstewmc-rtf)[tcds-io/php-jackson

A lightweight, flexible object serializer for PHP, inspired by FasterXML/jackson

113.2k10](/packages/tcds-io-php-jackson)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
