PHP started as code embedded in HTML, but today many developers want the reverse: HTML inside PHP, as a value you can return and pass around. This RFC from Liam Hammett adds markup expressions, inspired by JSX. You write HTML tags directly in your PHP code and get back an object that renders as safely escaped HTML.

class Greeting implements Markup\Html
{
    public function __construct(public string $name, public string $type) {}

    public function toHtml(): Markup\Html
    {
        return <>
            <h1 class="title">Hello, {$this->name ?: ucfirst($this->type)}!</h1>
            <p>Welcome to PHP, where markup is a first-class expression.</p>
        </>;
    }
}

echo <Greeting name="Rasmus" type="guest" />; // prints the rendered HTML, dynamic values escaped

It's just objects

The new syntax isn't a new template language. At compile time, PHP turns each tag into a plain new call:

// The new syntax...
$html = <button class="btn">Sign in</button>;

// ...compiles to exactly this - same AST, same opcodes:
$html = new \Markup\Element('button', ['class' => 'btn'], ['Sign in']);

Because of that, it adds no runtime cost and behaves like any other PHP expression.

How it works

  • Escaped by default. Values in {$expr} and in attributes go through htmlspecialchars(), and Markup\raw() marks HTML you trust. Escaping only covers HTML, so URL, JavaScript and CSS escaping are still up to you.
  • Lowercase tags are HTML. <div> creates a Markup\Element.
  • Capitalized tags are components. <Card title="Hi" /> becomes new Card(title: 'Hi'). Attributes become named arguments, so a typo throws a TypeError. Static methods work too, as in <Author::byline name="Rasmus" />.
  • Dynamic tags. <$tag> picks the tag name at runtime.
  • Framework hooks. Frameworks can register a factory that builds components through their container, or a decorator that wraps every component's output.

The RFC says this isn't meant to replace Blade, Twig or Latte. Instead, it gives them a shared foundation they could build on.

It has to live in core rather than in an extension, because PHP's parser has to tell a markup < apart from a less-than sign, and only core can change how PHP reads code.

What it means for existing code

The RFC says no working code changes meaning. Markup only starts where a < would be a syntax error today, such as right after return or =, so $a < $b still compares two values. There's a new Markup\ namespace with classes like Markup\Html, Markup\Element and Markup\Fragment, provided by a new ext/markup extension that's always enabled. Static analysis tools like PHPStan and Psalm, along with IDEs, will need updates to understand the new syntax.

Where it stands

The RFC is under discussion. It was first posted in July 2026 with a full implementation ready. It targets the next PHP 8.x minor version and needs a two-thirds vote to pass.