PHPackages                             unzeroun/isocontent - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. unzeroun/isocontent

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

unzeroun/isocontent
===================

A library to transform web contents (say HTML) to an abstract AST that can be rendered everywhere

1.1.0(5mo ago)16212[2 PRs](https://github.com/un-zero-un/Isocontent/pulls)MITPHPPHP &gt;=7.4CI failing

Since Mar 24Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/un-zero-un/Isocontent)[ Packagist](https://packagist.org/packages/unzeroun/isocontent)[ RSS](/packages/unzeroun-isocontent/feed)WikiDiscussions main Synced yesterday

READMEChangelog (1)Dependencies (9)Versions (8)Used By (0)

Isocontent
==========

[](#isocontent)

[![CI Status](https://github.com/un-zero-un/Isocontent/actions/workflows/ci.yml/badge.svg)](https://github.com/un-zero-un/Isocontent/actions/workflows/ci.yml)[![Coverage Status](https://camo.githubusercontent.com/0d98fcbffd3157605a64ff797546edad6a078aae579613989c40db655585f22b/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f756e2d7a65726f2d756e2f49736f636f6e74656e742f62616467652e737667)](https://coveralls.io/github/un-zero-un/Isocontent)[![Mutation testing badge](https://camo.githubusercontent.com/feb67b543c8c2db8c78b7f633dfb2c6e14328723fe0d3dcc6d79c31b6550ce2d/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f7374796c653d666c61742675726c3d687474707325334125324625324662616467652d6170692e737472796b65722d6d757461746f722e696f2532466769746875622e636f6d253246756e2d7a65726f2d756e25324649736f636f6e74656e742532466d61696e)](https://dashboard.stryker-mutator.io/reports/github.com/un-zero-un/Isocontent/main)[![License: GPL v3](https://camo.githubusercontent.com/48bf9b56d44f38db53ce21294cf0b9487d0a3734ab3ba1fe4c69858ae20db2c1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c76332d626c75652e737667)](https://www.gnu.org/licenses/gpl-3.0)

**Isocontent** is a PHP library that transforms rich‑text content (such as HTML) into a platform‑agnostic Abstract Syntax Tree (AST). The AST can then be rendered back to any target format — HTML, JSON, React Native, mobile views, or anything else you need.

Typical use‑case: accept content from a WYSIWYG editor, store it as a portable AST, and render it on any platform.

Installation
------------

[](#installation)

Requires PHP **&gt;= 8.2**. The `DOMParser` needs `ext-dom` and `ext-libxml`; the `JSONRenderer` needs `ext-json`.

```
composer require unzeroun/isocontent
```

End‑to‑End Demo
---------------

[](#endtoend-demo)

The `Isocontent` service is the main entry point. Register parsers and renderers, then parse and render content in any supported format:

```
use Isocontent\Isocontent;
use Isocontent\Parser\DOMParser;
use Isocontent\Renderer\HTMLRenderer;
use Isocontent\Renderer\JSONRenderer;

$isocontent = new Isocontent(
    parsers:   [new DOMParser()],
    renderers: [new HTMLRenderer(), new JSONRenderer()],
);

// 1. Parse HTML into an AST
$ast = $isocontent->buildAST(
    'HelloThis is rich content with a link.',
    'html',
);

// 2. Inspect the AST as a portable array (suitable for JSON storage / API responses)
$ast->toArray();
// [
//     [
//         'type'       => 'block',
//         'block_type' => 'title',
//         'arguments'  => ['level' => 1],
//         'children'   => [
//             ['type' => 'text', 'value' => 'Hello'],
//         ],
//     ],
//     [
//         'type'       => 'block',
//         'block_type' => 'paragraph',
//         'arguments'  => [],
//         'children'   => [
//             ['type' => 'text', 'value' => 'This is '],
//             [
//                 'type'       => 'block',
//                 'block_type' => 'strong',
//                 'arguments'  => [],
//                 'children'   => [
//                     ['type' => 'text', 'value' => 'rich'],
//                 ],
//             ],
//             ['type' => 'text', 'value' => ' content with a '],
//             [
//                 'type'       => 'block',
//                 'block_type' => 'link',
//                 'arguments'  => ['href' => 'https://example.com'],
//                 'children'   => [
//                     ['type' => 'text', 'value' => 'link'],
//                 ],
//             ],
//             ['type' => 'text', 'value' => '.'],
//         ],
//     ],
// ]

// 3. Render back to HTML
$isocontent->render($ast, 'html');
// 'HelloThis is rich content with a link.'

// 4. Render to JSON
$isocontent->render($ast, 'json');
// The same array structure above, encoded as a JSON string
```

Core Concepts
-------------

[](#core-concepts)

Isocontent models content as a tree of **nodes**:

ClassDescription`TextNode`Leaf node holding a plain text value.`BlockNode`Structural element (paragraph, heading, link…) with optional children and typed arguments.`NodeList`Ordered collection of nodes — this is what parsers produce and renderers consume.### Block Types

[](#block-types)

The `DOMParser` maps HTML elements to the following block types:

Block TypeHTML Tag(s)Arguments`paragraph```—`title```–```level` (int 1–6)`strong```—`emphasis```—`inline_text```—`link````href` (string)`list```, ```ordered` (bool)`list_item```—`quote```—`new_line```—`stripped```—`separator```—`subscript```—`superscript```—`code```—`generic`*any other element*—Parsers
-------

[](#parsers)

A parser reads input in a given format and builds an AST through the `Builder`.

- **`DOMParser`** — parses HTML strings via PHP's `DOMDocument` (format: `html`)
- **`ArrayParser`** — re‑hydrates an AST from a PHP array or decoded JSON (format: `array`)

### Custom Parser

[](#custom-parser)

Implement the `Parser` interface:

```
use Isocontent\AST\Builder;
use Isocontent\Parser\Parser;

final class MarkdownParser implements Parser
{
    public function supportsFormat(string $format): bool
    {
        return 'markdown' === $format;
    }

    public function parse(Builder $builder, mixed $input): void
    {
        // Build the AST using $builder->addTextNode() / $builder->addBlockNode()
    }
}
```

Renderers
---------

[](#renderers)

A renderer converts a `NodeList` into an output format.

- **`HTMLRenderer`** — renders to HTML (format: `html`). Tag mapping is [customizable](#custom-html-tag-mapping).
- **`JSONRenderer`** — renders to a JSON string (format: `json`).
- **`TextDebugRenderer`** — renders an indented tree for debugging (format: `text_debug`).

### Custom Renderer

[](#custom-renderer)

Implement the `Renderer` interface:

```
use Isocontent\AST\NodeList;
use Isocontent\Renderer\Renderer;

final class ReactNativeRenderer implements Renderer
{
    public function supportsFormat(string $format): bool
    {
        return 'react_native' === $format;
    }

    public function render(NodeList $ast): mixed
    {
        // Walk the AST and produce your output
    }
}
```

### Custom HTML Tag Mapping

[](#custom-html-tag-mapping)

The `HTMLRenderer` uses the [Specification pattern](https://en.wikipedia.org/wiki/Specification_pattern) to map block types to HTML tags. You can pass your own mapping to override the defaults:

```
use Isocontent\Renderer\HTMLRenderer;
use Isocontent\Specs\BlockTypeMatch;
use Isocontent\Specs\BlockArgumentMatch;

$renderer = new HTMLRenderer([
    [(new BlockTypeMatch('title'))->and(new BlockArgumentMatch('level', 1)), 'h1'],
    [(new BlockTypeMatch('title'))->and(new BlockArgumentMatch('level', 2)), 'h2'],
    [new BlockTypeMatch('paragraph'), 'p'],
    [new BlockTypeMatch('strong'), 'b'],        //  instead of
    [new BlockTypeMatch('emphasis'), 'i'],       //  instead of
    [new BlockTypeMatch('inline_text'), 'font'], //  instead of
]);
```

Available specifications: `BlockTypeMatch` (match by type), `BlockArgumentMatch` (match by argument key/value), and `AllMatch` (compose with `->and()`).

Symfony Integration
-------------------

[](#symfony-integration)

Register the bundle:

```
// config/bundles.php
return [
    // ...
    Isocontent\Bridge\Symfony\Bundle\IsocontentBundle::class => ['all' => true],
];
```

The bundle auto‑discovers all `Parser` and `Renderer` implementations (tagged `isocontent.parser` / `isocontent.renderer`) and registers a public `Isocontent` service. All built‑in parsers and renderers are autowired out of the box.

```
use Isocontent\Isocontent;

final class ContentController
{
    public function __construct(private readonly Isocontent $isocontent) {}

    public function show(): Response
    {
        $ast  = $this->isocontent->buildAST($html, 'html');
        $json = $this->isocontent->render($ast, 'json');
        // ...
    }
}
```

Two Symfony Form **data transformers** are also provided: `ASTToStringTransformer` (`Node|NodeList` ↔ rendered string) and `ASTToArrayTransformer` (`Node|NodeList` ↔ PHP array).

Twig Integration
----------------

[](#twig-integration)

A Twig filter is available to render AST directly in templates:

```
{{ content|render_isocontent_ast }}
{{ content|render_isocontent_ast('json') }}
```

The filter accepts both `NodeList` objects and raw arrays.

Testing
-------

[](#testing)

```
vendor/bin/phpunit          # Unit & E2E tests
vendor/bin/psalm            # Static analysis
vendor/bin/infection        # Mutation testing
vendor/bin/php-cs-fixer fix # Code style
```

CI runs on PHP 8.2, 8.3, 8.4, and 8.5 with both lowest and highest dependency versions.

Contributing
------------

[](#contributing)

Contributions are welcome! Fork the repository, create a feature branch, ensure all checks pass, and open a pull request.

License
-------

[](#license)

Isocontent is released under the [MIT License](https://opensource.org/licenses/MIT).

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance82

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 76.9% 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 ~449 days

Total

4

Last Release

161d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cebd97d261d234cc9e88fb337c8b54a36f0ef20862894be210ecc4ee43e917ac?d=identicon)[yohang](/maintainers/yohang)

---

Top Contributors

[![yohang](https://avatars.githubusercontent.com/u/608984?v=4)](https://github.com/yohang "yohang (10 commits)")[![oxodao](https://avatars.githubusercontent.com/u/10746319?v=4)](https://github.com/oxodao "oxodao (2 commits)")[![jpottier](https://avatars.githubusercontent.com/u/1446926?v=4)](https://github.com/jpottier "jpottier (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/unzeroun-isocontent/health.svg)

```
[![Health](https://phpackages.com/badges/unzeroun-isocontent/health.svg)](https://phpackages.com/packages/unzeroun-isocontent)
```

PHPackages © 2026

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