Search by

phil / ujo

phil

UJO (Universal JSON Output), a block document format, as immutable values: every official and community tool and the page and game block types validated, sanitized inline HTML, strict JSON, purified HTML rendering and plain text

0.1.0-alpha 2026-11-11 04:05 UTC

This package is auto-updated.

Last update: 2026-11-11 06:17:26 UTC


README

UJO (Universal JSON Output) documents as values you can trust: read strictly, checked block by block, rendered as safe HTML.

phil/ujo is the model of UJO (Universal JSON Output), a JSON format for documents made of blocks — a time, a version and an ordered list of blocks, each with an identifier, a type, its data and its tunes. It reads that JSON strictly into immutable values, checks every block against the shape of its type, upgrades the older and third-party forms of the data to one canonical form, sanitizes the inline HTML each text may hold, and renders the document as HTML or as plain text. It knows every official Editor.js tool, the community tools worth reading — alerts, toggles, columns, formulas, diagrams, charts, buttons, galleries —, and the types of printed pages and game books: page and column breaks, footnotes, tables of contents, indexes, boxed notes, stat blocks. It does not edit documents, convert other markup to UJO, or draw formulas, diagrams and QR codes on the server.

It is for any PHP application that stores what its writers compose in a block editor and shows it to readers: a CMS, a wiki, a forum, a tool that lays out books. A document is checked once, when it is received, and stored in canonical form; rendering it again checks it again, so a tightened rule reaches older documents. In a Symfony application, phil/ujo-bundle adds the ujo_html and ujo_text Twig filters, a form field holding UJO JSON and a validation constraint.

It is a plain PHP library with no dependency. The data written by the tools of Editor.js editors is read as it is shaped. Its HTML sanitizer is its own: it keeps, field by field, the inline elements a profile allows, written back in one canonical form, and nothing else.

The design is small: Document and Block are the values; BlockTypeRegistry holds a BlockType per type, whose Shape of fields checks the data; Normalizer applies them; HtmlRenderer renders with one BlockRenderer per type; PlainText converts; Ujo puts them together.

Status

Part State
Document, Block: values, structure checked, nested blocks found, JSON form Implemented and tested
JsonDecoder, JsonEncoder: strict reading with size and depth limits, writing in canonical form Implemented and tested
Block types: 13 official, 8 community, 21 page and game types; raw on request Implemented and tested
Older and third-party forms: lists 1.x, checklists, nested checklists, simple images, flat toggles and collapsible blocks, calumk columns, LaTeX, buttons, carousels, alignment and anchors saved in the data Implemented and tested
Two validation levels: the shape for every save, required values filled in and bounds for publication Implemented and tested
Tunes: alignment, anchor, text variant, notice, indent, layout, mask, table of contents, variant Implemented and tested
InlineSanitizer: rich, plain and raw profiles of inline tools Implemented and tested
HtmlRenderer: one renderer per type, replaceable; tunes as classes, identifiers and styles Implemented and tested
PlainText: one conversion per type, replaceable Implemented and tested
The layout community tool (free CSS layouts) Not read, by design

Documentation

Document What it covers
Installation Requirements and installing.
Documents Document and Block, the JSON form, strict reading and writing, nested blocks.
Block types Every type, its data, the forms it also reads, and the tunes.
Normalization What Normalizer checks and changes, registries, and adding a type.
Inline HTML The sanitizer, its profiles and inline tools, and why it is the library's own.
HTML rendering The HTML of each type, the tunes, replacing a renderer, and purification.
Plain text The text of each type, and replacing a conversion.
Extension points What an application may replace or add, and when it is stable.
Exceptions UjoException and its four exceptions, with their messages.
Testing The library's tests and mutation bar, and how to test code that stores documents.
Reference Every type of the library, with its role.

Quick start

composer require phil/ujo
use Phil\Ujo\Ujo;

$ujo = new Ujo();

$document = $ujo->decode($json);          // strict, checked, canonical — or an exception naming the path
$stored = $ujo->encode($document);        // the canonical JSON to keep

echo $ujo->toHtml($stored);               // <h2 id="…" class="ujo-header">…</h2>…
echo $ujo->toText($stored);               // for a search index or an excerpt

Requirements

PHP 8.5 or later, with ext-json and ext-mbstring. Nothing else.

Contributing

Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.

License

MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.