THP templating

React changed the way HTML is rendered with the introduction of JSX. Having access and being able to create and manipulate HTML elements from code led to the pattern of components.

On the other hand, PHP is characterized for its ability to easily render HTML, to the point that PHP code is conceptually written inside an HTML tag. However, this model doesn’t play well with the components model pioneered by React and that is so common nowadays on front-end development.

Creating small, reusable templates is hard with pure PHP. Instead you need to rely on external templating libraries, and even still, all your HTML is written outside the PHP file, and communicates in one way with weak, fragile data passing.

For example, using the Plates PHP library you may create a Button component like so:

// button.php
function render_button(string $name) {
    $templates = new League\Plates\Engine("./");
    $button_html = $templates->render("button.template", ["name" => $name]);
    return $button_html;
}
// button.template.php
<button class="some tailwind classes">
    Hello <?= $this->e($name) ?>!
</button>

This approach has many problems:

Maybe for these (and other) reasons components are not used with templating libraries. Instead people use sections, layouts, etc.

We believe that by inverting the PHP model we can improve the experience of writing HTML. By having HTML inside of code, not code inside of HTML, we can easily create many small components and compose them.

The following would be the equivalent in THP:

fun Button(String name) -> HTML {
    <button class="some tailwind classes">
        Hello {name}!
    </button>
}thp
    
Syntax error: Expected an identifier after the `fun` keyword. at line 1:4

It is very similar to React. The HTML is inside the THP code, not the other way around, so you can have arbitrary logic in the component.

fun User(String name) -> HTML {
    // Get info from the database
    val user = try Model::get_user(name)
    else {
        return <div>User not found!</div>
    }

    // Run other logic

    <div class="some tailwind classes">
        Hello {user.name}!
        <br>
        Here are your transactions:
        @for t in user.transactions {
            <TransactionItem t={t} />
        }
    </div>
}

fun TransactionItem(Transaction t) -> HTML {
    <li>
        {t.date} - {t.name} ({t.price})
    </li>
}thp
    
Syntax error: Expected an identifier after the `fun` keyword. at line 1:4

Is this a JavaScript Front-End Framework?

No

This is an HTML templating solution. All the code is run in the backend, generates static HTML and sends it to the browser. There is no reactivity, hooks, signals, etc.

If you need reactivity on the front-end and want to use this templating take a look at htmx, Alpine.js and hyperscript.

Styling

We don’t provide any syntax or facility for styling. However, this component model is good to use with TailwindCSS.

Conversion

Warning

TBD: This is a draft, subject to change.

HTML expressions will be compiled to plain strings, and those will be then composed.

fun Sample(String name) -> HTML {
    val user = Model::get_user(name)

    <button>
        @if user != null
        {
            Hello {name} (id {user.name})!
        }
        @else
        {
            Not logged in
        }
    </button>
}thp
    
Syntax error: Expected an identifier after the `fun` keyword. at line 1:4
function Sample(name) {
    $user = Model::get_user(name);

    $__expr_1 = "";
    if ($user !== null) {
        $__expr_2 = user.name;
        $__expr_1 = "Hello {$name} (id {$__expr_2})";
    } else {
        $__expr_1 = "Not logged in";
    }

    return "<button>{$__expr_1}</button>"
}