shyim/mjml-php 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

shyim/mjml-php

最新稳定版本:0.1.1

Composer 安装命令:

composer require shyim/mjml-php

包简介

Native PHP port of MJML - the email template engine

README 文档

README

Native PHP port of MJML — the markup language for responsive HTML emails.

MJML-PHP converts MJML markup into responsive HTML that works across all major email clients, including Outlook. No Node.js dependency required.

Requirements

  • PHP 8.2+
  • ext-dom
  • ext-libxml

Installation

composer require shyim/mjml-php

Usage

Basic

use MjmlPHP\Mjml;

$result = Mjml::render('<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text>Hello World</mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>');

echo $result->html;

With Options

use MjmlPHP\Mjml;
use MjmlPHP\MjmlOptions;

$result = Mjml::render($mjml, new MjmlOptions(
    keepComments: true,
    minify: false,
    beautify: false,
    language: 'en',
    dir: 'ltr',
));

echo $result->html;

Custom Components

use MjmlPHP\Mjml;

$mjml = new Mjml();
$mjml->registerComponent(MyCustomComponent::class);

$result = $mjml->toHtml('<mjml>...</mjml>');

CLI

A small dependency-free CLI is exposed as Composer bin mjml-php:

vendor/bin/mjml-php email.mjml -o email.html
vendor/bin/mjml-php < email.mjml > email.html

Useful options:

vendor/bin/mjml-php email.mjml --validation-level=skip
vendor/bin/mjml-php email.mjml --process-includes --include-path=partials

Run vendor/bin/mjml-php --help for all options.

Supported Components

Structure

  • mj-body — Email body container
  • mj-section — Horizontal section with background image/color support and Outlook VML
  • mj-column — Responsive column with auto-width distribution
  • mj-group — Non-responsive column grouping
  • mj-wrapper — Section wrapper with gap support

Content

  • mj-text — Styled text block
  • mj-image — Responsive image with srcset/sizes and fluid-on-mobile
  • mj-button — Call-to-action button
  • mj-divider — Horizontal rule
  • mj-spacer — Vertical spacing
  • mj-table — HTML table passthrough
  • mj-raw — Raw HTML passthrough

Interactive

  • mj-accordion — Expandable/collapsible sections (CSS-only, no JavaScript)
  • mj-carousel — Image carousel (CSS radio-button technique)
  • mj-navbar — Navigation bar with responsive hamburger menu
  • mj-hero — Full-width hero section with VML background for Outlook
  • mj-social — Social media icons (17 built-in networks)

Head

  • mj-title — Email title
  • mj-preview — Preview text
  • mj-attributes — Default attribute values and mj-class definitions
  • mj-font — Web font imports
  • mj-style — Custom CSS (inline or in <style> tag)
  • mj-breakpoint — Mobile responsive breakpoint
  • mj-html-attributes — Custom HTML attributes via CSS selectors

Validation

MJML-PHP validates your markup and throws a ValidationException on errors:

use MjmlPHP\Validation\ValidationException;
use MjmlPHP\Validation\ValidationLevel;

try {
    $result = Mjml::render($mjml);
} catch (ValidationException $e) {
    echo $e->getMessage();

    foreach ($e->errors as $error) {
        echo $error; // "Line 5: Attribute 'colr' is not allowed on mj-text (mj-text)"
    }
}
  • Strict (default) — Validate and throw ValidationException on errors
  • Soft — Validate but do not throw; errors are exposed via $result->errors
  • Skip — No validation
$result = Mjml::render($mjml, new MjmlOptions(
    validationLevel: ValidationLevel::Soft,
));

foreach ($result->errors as $error) {
    error_log((string) $error);
}

Exception Model

All library exceptions extend MjmlPHP\MjmlException, so you can catch them with a single catch:

use MjmlPHP\MjmlException;
use MjmlPHP\Parser\ParseException;
use MjmlPHP\Validation\ValidationException;

try {
    $result = Mjml::render($mjml);
} catch (ValidationException $e) {
    // Markup-level validation failures
} catch (ParseException $e) {
    // Circular includes, broken mj-include references, etc.
} catch (MjmlException $e) {
    // Any other library failure
}

MjmlException extends \RuntimeException, so existing catch (\RuntimeException $e) blocks still work.

Security

Input is trusted. MJML markup is treated as a template authored by you, not as end-user input. Do not concatenate untrusted strings into MJML markup — attribute values flow into the rendered HTML without HTML-escaping, the same as the official JS MJML.

Defensive measures the renderer already applies:

  • URL attributes (href, src, background, action, formaction, poster) pass through a scheme allowlist. javascript:, vbscript:, file:, and similar are rewritten to #. Only http, https, mailto, tel, sms, ftp, cid, anchor fragments, protocol-relative URLs, and relative paths pass through. data:image/* is allowed for inline images.
  • mj-font URLs that are not http/https/protocol-relative are dropped instead of being emitted into <link> / @import.
  • mj-include is disabled by default (unlike the JS MJML CLI). When enabled (ignoreIncludes: false), included paths are jailed under the current file's directory plus any explicit includePath roots, with realpath resolution, null-byte / URL-encoded-traversal rejection, and circular-include detection.
  • libxml is invoked with LIBXML_NONET (no network access). Under PHP 8 / libxml ≥ 2.9, external entities are not resolved by default, so this parser is not vulnerable to XXE.

If you must interpolate user data into MJML, escape it yourself before passing it to the renderer (htmlspecialchars for text content; URL-encode parameters you put into href query strings).

MJML Compatibility

This is a native PHP port aligned with MJML 5.2.1. The HTML output is tested against the original JavaScript implementation using snapshot tests to ensure identical rendering. A CI job re-renders the snapshot fixtures with mjml@5.2.1 and fails on drift, so if the upstream JS package publishes a patch you may see CI failures — open an issue and regenerate the fixtures.

Limitations

  • CSS @import inlining: The CSS inliner does not resolve @import directives found in inline style blocks. This matches the behavior of the JS MJML reference implementation for email-safe output.
  • CSS shorthand parsing: Only padding, margin, and border shorthands are fully supported for width calculation. More exotic shorthand properties (e.g., border-radius with / syntax) are passed through as-is.

Development

# Install dependencies
composer install

# Run tests
vendor/bin/phpunit

# Run only snapshot tests (compares against JS MJML output)
vendor/bin/phpunit --testsuite Snapshot

Fixture Tests

Small render and validation cases can be added as .test files under tests/Fixtures/. Use labeled sections:

--OPTIONS--
validationLevel: skip
ignoreIncludes: false

--MJML--
<mjml>...</mjml>

--HTML--
Expected HTML fragment

--HTML-NOT--
Unexpected HTML fragment

--HTML-RAW--
Expected raw multi-line HTML fragment

--HTML-NOT-RAW--
Unexpected raw multi-line HTML fragment

--ERRORS--
Expected validation error fragment

--EXCEPTION--
MjmlPHP\Parser\ParseException
Expected exception message fragment

For simple render fixtures, the legacy shorthand is also supported:

<mjml>...</mjml>
----
Expected HTML fragment

Regenerating Snapshot References

The snapshot test fixtures compare PHP output against reference HTML generated by the JS MJML CLI. To regenerate:

npm install -g mjml

for f in tests/Snapshot/Fixtures/*.mjml; do
    npx mjml@5.2.1 "$f" --no-minify > "${f%.mjml}.html"
done

License

MIT

统计信息

  • 总下载量: 1
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 3
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 3
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-08

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固