PHPackages                             zergius-eggstream/amp-converter - 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. zergius-eggstream/amp-converter

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

zergius-eggstream/amp-converter
===============================

Convert rendered HTML pages into AMP-valid HTML. Framework-agnostic with an optional Symfony bridge.

v0.1.0(1mo ago)017↓53.6%MITPHPPHP &gt;=8.4CI passing

Since Jun 5Pushed 1mo agoCompare

[ Source](https://github.com/zergius-eggstream/php-amp-converter)[ Packagist](https://packagist.org/packages/zergius-eggstream/amp-converter)[ RSS](/packages/zergius-eggstream-amp-converter/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (2)Used By (0)

php-amp-converter
=================

[](#php-amp-converter)

[![Latest Stable Version](https://camo.githubusercontent.com/b185ebf383d36b213cacf81a2d44e67eaa342a159b272826ed08ceef9a1b6c6c/68747470733a2f2f706f7365722e707567782e6f72672f7a6572676975732d65676773747265616d2f616d702d636f6e7665727465722f76)](https://packagist.org/packages/zergius-eggstream/amp-converter)[![License](https://camo.githubusercontent.com/364b0ba9d28d5499da9fc666e84de55f320b499d2c3a7fff0ce8141814407b7f/68747470733a2f2f706f7365722e707567782e6f72672f7a6572676975732d65676773747265616d2f616d702d636f6e7665727465722f6c6963656e7365)](https://packagist.org/packages/zergius-eggstream/amp-converter)[![PHP Version Require](https://camo.githubusercontent.com/483320da4794970b9c5d3eb5c3de3b81d867e4350abeb479c0d5612cd4f54f64/68747470733a2f2f706f7365722e707567782e6f72672f7a6572676975732d65676773747265616d2f616d702d636f6e7665727465722f726571756972652f706870)](https://packagist.org/packages/zergius-eggstream/amp-converter)

Convert rendered HTML pages into AMP-valid HTML — pure PHP, no Node runtime.

PHP port of the Node-based `convert-rendered-to-amp.js` shipped with the [amp-seo-sites toolset](https://github.com/zergius-eggstream/amp-seo-sites). Designed for build-time conversion of pre-rendered HTML into AMP HTML that passes the official `amphtml-validator`, with no Node runtime in the conversion path.

Status
------

[](#status)

**Port complete, ready for review.** All 13 algorithm stages implemented and exercised end-to-end on a real-world ~119 KB rendered page (covering amp-img, amp-youtube, amp-iframe, amp-bind burger, amp-accordion FAQ, inlined external CSS, JSON-LD); PHP output sits within **0.1 % of the Node reference** (113.6 KB vs 113.7 KB, structurally identical AMP). 258 phpunit tests / 513 assertions; PHPStan level 8 clean. CI runs on PHP 8.4. Stage map: [`doc/port-status.md`](doc/port-status.md).

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

[](#requirements)

- PHP `>=8.4` (will be lowered if no 8.x features end up being required)
- `ext-libxml`, `ext-mbstring`, `ext-simplexml`
- `ext-gd` is **NOT** required at runtime. Raster image dimensions come from `getimagesize()`, which is part of PHP core; SVG parsing uses `ext-simplexml`. GD is only used by the unit tests to generate PNG fixtures, and the affected tests auto-skip when GD is absent. The package works fine on slim production servers that don't have GD loaded — that path is exercised in CI under the `no-gd` matrix entry.
- **No Node.js** — pure PHP

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

[](#installation)

```
composer require zergius-eggstream/amp-converter
```

Once the maintainer of the consuming projects forks/republishes the package under their preferred vendor, that `require` line points at the new name (the namespace stays `AmpConverter\` so call sites don't change).

Usage
-----

[](#usage)

```
use AmpConverter\AmpConverter;

$result = AmpConverter::createDefault()->convert(
    $renderedHtml,
    $siteRoot,
    canonicalUrl: 'https://example.com/the-page',
);
file_put_contents($outputPath, $result->html);
foreach ($result->warnings as $w) {
    error_log("amp-converter: $w");
}
```

Arguments:

NameRequiredDefaultMeaning`$renderedHtml`yes—the rendered HTML to convert`$siteRoot`yes—absolute path to the site directory (root of on-disk assets)`$canonicalUrl`no`null`absolute URL of the non-AMP (canonical) version of the page. When provided, AMP page emits `` and replaces any existing canonical link. When null, falls back to a relative self-reference `href="./"` so the package stays drop-in for hosts that don't compute absolute URLs at build time.`$assetsBaseDir`no`'public'`subdirectory under `$siteRoot` where assets live. The default matches the common `public/`-as-document-root convention; pass an empty string for a flat layout, or a custom folder for non-standard layouts.The package is intentionally agnostic about **where** the host gets `canonicalUrl` from (TSV, per-site config, request data, hard-coded mapping, …) — that's a host concern. The package just emits what it was given.

`ConversionResult` exposes:

- `html: string` — the converted AMP HTML.
- `usedComponents: list` — AMP custom components actually emitted (`amp-img`, `amp-youtube`, `amp-iframe`, `amp-bind`, `amp-accordion`, …). The pipeline emits a `` for each one (except `amp-img`, which ships with v0.js).
- `warnings: list` — non-fatal issues encountered (unresolvable image, dropped CSS block, malformed tag, …). The host project decides whether to log, surface or ignore them.

### Symfony integration

[](#symfony-integration)

The package is framework-agnostic. A typical Symfony host wires it through a thin one-line autowired service:

```
namespace App\Renderer;

use AmpConverter\AmpConverter as Lib;

readonly class AmpConverter
{
    public function convert(string $renderedHtml, string $siteRoot): string
    {
        return Lib::createDefault()->convert($renderedHtml, $siteRoot)->html;
    }
}
```

If a future iteration ships a Symfony Bundle for zero-config auto-wiring (`Bridge/Symfony/AmpConverterBundle`), it will be additive — the framework-agnostic core won't change.

Architecture
------------

[](#architecture)

The converter is an ordered **pipeline of transformers**. Each transformer implements `Transformer::apply(string $html, Context $ctx): string` and owns one feature area; `Context` carries cross-transformer state (siteRoot, used components, warnings, detected FAQ classes, font @imports collected from CSS, …).

```
input HTML
    │
    ▼
┌──────────────────────────┐
│ MaskSnippets             │  preserve Twig/PHP dynamics behind opaque placeholders
├──────────────────────────┤
│ CssAggregation           │  inline local , merge  blocks
├──────────────────────────┤
│ CssProcessing            │  HTML-entity decode, font @import extract, strip
│                          │   !important / @import / @charset, vendor-media,
│                          │   broken --vars
├──────────────────────────┤
│ ImgToAmpImg              │   →  with layout pick (fixed/intrinsic/
│                          │   responsive/fill) + logo/avatar heuristics
├──────────────────────────┤
│ IframeConversion         │  YouTube → ; other →
│                          │   (responsive / fixed-height / fill);  dropped
├──────────────────────────┤
│ FormConversion           │   → , submit → amp-bind tap
├──────────────────────────┤
│ DefensiveSourceFixes     │  script strip (preserves JSON-LD), on*= strip,
│                          │   URL typos, duplicate doctype / meta / head / body,
│                          │   table border, rel/class dedupe, alt/loading guards,
│                          │   preload strip, oversized inline style
├──────────────────────────┤
│ BurgerToAmpBind          │  3-tier detection (aria-controls / class+nav /
│                          │   nav-driven) + CSS-pair guard (5 hidden / 5 shown)
├──────────────────────────┤
│ FaqToAccordion           │  4 variants (container + dl + sibling Question +
│                          │   hN+p) + CSS post-process (accordion patch,
│                          │   specificity bump, question-class defaults)
├──────────────────────────┤
│ AutoContrastVars         │  resolve --X:auto via YIQ luma; fallback strip
├──────────────────────────┤
│ FontImportInjection      │  emit  for collected font CDNs
├──────────────────────────┤
│ AmpRuntimeInjection      │  , v0.js, custom-element scripts (sorted),
│                          │   boilerplate, canonical, http-equiv→charset,
│                          │   noscript guard
├──────────────────────────┤
│ PurgeCss                 │  shrink  (60 KB threshold);
│                          │   recursive @media, @font-face/@keyframes preserved
├──────────────────────────┤
│ UnmaskSnippets           │  restore the dynamics from step 1
└──────────────────────────┘
    │
    ▼
ConversionResult { html, usedComponents, warnings }

```

Replace the default pipeline by constructing `AmpConverter` directly:

```
use AmpConverter\AmpConverter;
use AmpConverter\Transformer\ImgToAmpImg;
use AmpConverter\PhpSnippets\MaskSnippets;
use AmpConverter\PhpSnippets\UnmaskSnippets;

$converter = new AmpConverter([
    new MaskSnippets(),
    new ImgToAmpImg(),
    new MyCustomTransformer(),
    new UnmaskSnippets(),
]);
```

Error handling
--------------

[](#error-handling)

Strict but graceful: when the converter cannot transform a fragment (unparseable `` tag, malformed CSS block, missing image dimensions), it **removes the fragment and records a warning** rather than throwing. The whole page still converts. Build pipelines decide what to do with warnings (log / fail / ignore). Exceptions are reserved for unrecoverable programmer errors (e.g. invalid pipeline configuration).

Testing
-------

[](#testing)

```
composer test       # phpunit
composer phpstan    # static analysis
```

Layout:

- `tests/Unit//Test.php` — per-rule unit tests for every transformer; each spec rule has its own positive + negative test.
- `tests/Regression/EndToEndSmokeTest.php` — end-to-end smoke test on a real rendered page. Reads the fixture directory from the `AMP_CONVERTER_SMOKE_FIXTURE_DIR` environment variable; auto-skips when the variable is unset or the path is missing, so CI just runs the unit tests.

CI is GitHub Actions over PHP 8.4 with the `libxml`, `mbstring`, `simplexml`, `gd` extensions; no Node required.

License
-------

[](#license)

MIT.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance90

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

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

Unknown

Total

1

Last Release

49d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a203334ff3077d8d395360088a8e5b7721941b45f6f77f4e89ece16b92153a8c?d=identicon)[zergius-eggstream](/maintainers/zergius-eggstream)

---

Tags

symfonyhtmlconverterampstatic-site

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/zergius-eggstream-amp-converter/health.svg)

```
[![Health](https://phpackages.com/badges/zergius-eggstream-amp-converter/health.svg)](https://phpackages.com/packages/zergius-eggstream-amp-converter)
```

###  Alternatives

[rumenx/php-sitemap

Framework-agnostic Sitemap generator for PHP, Laravel, and Symfony.

1.3k26.9k1](/packages/rumenx-php-sitemap)[pixel418/markdownify

The HTML to Markdown converter for PHP

198876.3k8](/packages/pixel418-markdownify)[3f/converter

A set of classes to translate a text from HTML to BBcode and from BBCode to Markdown.

29153.6k](/packages/3f-converter)[magyarandras/amp-converter

A library to convert HTML articles, blog posts or similar content to AMP (Accelerated Mobile Pages).

65155.7k](/packages/magyarandras-amp-converter)

PHPackages © 2026

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