Introduction to JadePHP

Tweet

There are dozens of templating engines out there, with options such as Smarty, Twig (used in the upcoming version of Drupal) and Blade (the default for Laravel) among the best known – as well as vanilla PHP, of course. Stepping away from PHP specifically, eRuby / ERB and Haml for Ruby / Ruby on Rails, and Javascript has scores of popular choices including Mustache, Handlebars, Hogan and EJS. Some have subtly different syntax, some more markedly so.

One which differs quite significantly from most is Jade, an engine usually associated with Javascript applications – it’s supported out-of-the-box by Express for Node.js, for example. It’s Jade I’m going to look at in this article; or more specifically the PHP port JadePHP.

Haml and Jade

It would be remiss to talk about Jade without mentioning Haml, from which Jade takes its inspiration – and indeed there are several libraries for using Haml with PHP. Jade shares its overall philosophy, which is to make templating “beautiful” and use what the authors describe as templating “haiku”. Whatever that actually means, there’s no denying Haml and Jade do share some characteristics which make them fundamentally different to most templating languages.

What’s the Difference?

Most templating engines involve writing the target markup and “injecting” it with placeholders and / or basic logic – a superset, in a sense. Jade still has placeholders and logic, but also provides a shorthand for writing XML-like elements. Generally that means HTML, although you can also use it for things like RSS as well as XML itself.

In fact if you wanted to, you could just use Jade as a shorthand for HTML without taking advantage of its more “traditional” templating features.

How to use the Repository

Rather frustratingly, the code is not currently available via Composer – although it should be a simple enough task to package it up, if anyone has an hour or two. You can get it to to work, however, by cloning the repository and include‘ing or require‘ing the included autoload.php.dist (the Github repository includes Symfony’s UniversalClassLoader).

Here’s an example, adapted from the one in the project’s README, which assumes that the repository has been downloaded into a directory called jade:

require('./jade/autoload.php.dist');

use Everzet\Jade\Dumper\PHPDumper,
        Everzet\Jade\Visitor\AutotagsVisitor,
        Everzet\Jade\Filter\JavaScriptFilter,
        Everzet\Jade\Filter\CDATAFilter,
        Everzet\Jade\Filter\PHPFilter,
        Everzet\Jade\Filter\CSSFilter,
        Everzet\Jade\Parser,
        Everzet\Jade\Lexer\Lexer,
        Everzet\Jade\Jade;

$dumper = new PHPDumper();
$dumper->registerVisitor('tag', new AutotagsVisitor());
$dumper->registerFilter('javascript', new JavaScriptFilter());
$dumper->registerFilter('cdata', new CDATAFilter());
$dumper->registerFilter('php', new PHPFilter());
$dumper->registerFilter('style', new CSSFilter());

// Initialize parser & Jade
$parser = new Parser(new Lexer());
$jade   = new Jade($parser, $dumper);

$template = __DIR__ . '/template.jade';

// Parse a template (both string & file containers)
echo $jade->render($template);

This will compile the file template.jade and echo its contents.

Where you actually use this depends on your workflow, whether you’re using a framework, and so on. You could, perhaps, use a service such as Watchman, Guard or Resource Watcher to watch the filesystem for changes to your Jade templates, and compile them at the appropriate time during the development process.

A simple example

Let’s look at a simple example, which shows a complete – if basic – HTML page, with only two variables and no logic (yet!)

!!! 5
html(lang="en-us")

    meta(charset="utf-8")
    meta(http-equiv="X-UA-Compatible", content="IE=Edge;chrome=1")

    title(dir="ltr")= pageTitle

    meta(name="viewport", content="width=device-width, initial-scale=1.0")

    link(rel="stylesheet", media="screen", href="/css/styles.css")

    body
    header
    h1 My Jade Application
    div#content
    div.inner
        =$bodyContent

    script(data-main="js/main.js", src="js/libs/require.js")

Important: you’ll need to use two spaces to indent. This is the only method currently understood by Jade PHP, and using something different will cause errors or invalid markup.

Straight away, it will be apparent that Jade looks rather different to the HTML you’re accustomed to. No angled brackets and no closing tags, for starters. Nor are there curly brackets, double curly brackets, or any of the common approaches to marking up variables to be injected into the template. (That said, you can use the double curly bracket syntax, but it’s not a part of Jade, per se.)

What it does show is a very concise method of generating markup. Let’s look at the resulting HTML:

<!DOCTYPE html>
<html lang="en-us">
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge;chrome=1" />
    <title dir="ltr"><?php echo pageTitle ?></title>
    <meta name="viewport" content="width=device-width" initial-scale="1.0" />
    <link rel="stylesheet" media="screen" href="/css/styles.css" />
    <body>
        <header>
            <h1>My Jade Application</h1>
        </header>
        <div id="content">
            <div class="inner">
                <?php echo $bodyContent ?>
            </div>
        </div>
        <script data-main="js/main.js" src="js/libs/require.js"></script>
    </body>
</html>

Let’s go through the key lines in the Jade template, to get an idea of how the shorthand for HTML works.

!!! 5 is shorthand for the HTML5 doctype. It’s the only place you’ll see that triple exclamation mark syntax. You can also use !!! xml to get <?xml version="1.0" encoding="utf-8" ?>, for transitional you can use !!! transitional to get <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> or the default !!! default gives you <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">.

NOTE; in the latest Javascript-flavoured version of Jade, the !!! 5 declaration has been replaced with doctype html; it’s possible – though perhaps unlikely, looking at the Github repo’s lack of activity – that JadePHP will follow suit.

An HTML tag is specified by its name alone, with no need to close it; for example:

body
  header

…would, if you stopped there, result in:

<body>
    <header></header>
</body>

Note how the structure of the document is represented using indentation.

You can put a tag’s content after its name, with a space between them:

h1 My Jade Application

…becomes:

<h1>My Jade Application</h1>

If you want to split large blocks of content into multiple lines, use the pipe “|” character:

p 
  | Curabitur blandit tempus porttitor. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. 
  | Aenean eu leo quam. 
  | Pellentesque ornare sem lacinia quam venenatis vestibulum.

This compiles to:

<p>Curabitur blandit tempus porttitor. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.</p>

You can use a format similar to CSS selectors to add IDs and classes to HTML elements:

div#content
  div.inner

…results in:

<div id="content">
    <div class="inner">

Other attributes such as src, href, lang, media etc can be specified using parenthesis:

html(lang="en-us") === <html lang="en-us">

link(rel="stylesheet", media="screen", href="/css/styles.css") === <link rel="stylesheet" media="screen" href="/css/styles.css" />

An equals sign is used for variable substitution. As you can see above, when you compile a Jade template it converts something like this:

= $pageTitle

…into the following:

<?php echo $pageTitle ?>

Adding some Logic

You can use a dash to inject basic conditional logic. Here’s a concrete example:

header
    h1= $pageTitle
    - if ($loggedIn):
    p.greeting Welcome back!
    - else:
    a(href="/login") Please login
    - endif;

When you compile the template, this will result in the following:

<header>
    <h1><?php echo $pageTitle ?></h1>
    <?php if ($loggedIn) ?>
    <p class="greeting">Welcome back!</p>
    <?php else ?>
    <a href="/login">Please login</a>
    <?php endif ?>
</header>

Iteration follows very similar lines:

ul
  - foreach ($items as $item):
  li= $item

Filters

You can use filters to take a block of text and process it in some way, for example:

:php
    | $value = 10;
    | $computed_value += 100;
    | print $computed_value;

…will evaluate to:

<?php
    $value = 10;
    $computed_value += 100;
    print $computed_value;
?>

Perhaps more useful are the javascript and CSS filters, for example:

:style
| body {
|   background: yellow;
| }

…will evaluate to:

<style type="text/css">
body {
  background: yellow;
}

You set up these filters as follows (see the example code above for the context of these declarations):

$dumper->registerFilter('javascript', new JavaScriptFilter()); 
$dumper->registerFilter('php', new PHPFilter());
$dumper->registerFilter('style', new CSSFilter());

The first argument corresponds to the text you use to “mark up” the text in your templates, which is prefixed with a colon; so in the example above you could use :javascript, :php and :style respectively.

By implementing Everzet\Jade\Filter\FilterInterface, you can even define your own filters.

Why use Jade?

Arguments about which templating language to use are ultimately pretty futile. Whilst you can come up with certain benchmarks about which perform better than others in certain circumstances, which ones are specifically designed for certain environments – like client vs server-side, at the end of the day the choice is often a very personal one which comes down to what you feel comfortable scripting in. I have no intention of trying to fan those flames, any more than I wish to get involved in arguments over which is “the best” programming language.

Jade’s shorthand approach won’t be for everyone. There are some who will argue that it’s easier to read, and some who will vehemently disagree. If, however, it’s an approach that you see the merit in then that’s a compelling reason to choose it.

Another reason you might decide to use Jade – apropos of a number of templating options – is if you alternate between technologies. If you often switch between, say, Node.js and PHP development then there’s some logic in keeping things consistent. Why master one engine then use something else entirely, if it’s available for multiple languages?

Summary

In this article I’ve looked at JadePHP, a port of the primarily Javascript-focussed templating engine, Jade. I’ve given you a few pointers on how to use it and some ideas about why you might want to use it. Are you going to give it a try, or does it seem unnecessarily terse to you? Do let me know what you think of it in the comments.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Fab G

    Great library, but unmaintained. So completely useless :(

    • everzet

      Great thing about OpenSource is that whenever something you’re interested in stops being maintained by the author – you always can help everyone by taking over ;)

      You can always fork and continue the project or I even can give you full R/W access to the original repo if you’re willing to move it forward.

      • Fab G

        The question is when I fork it and send you PRs, will you merge them? Or are you uninterested in maintaining it anymore?

        • everzet

          I don’t have time to maintain it at all (including management) – too busy with other OpenSource projects.

  • HotDang

    So are you supposed to write $jade->render() to a file, then include it, or is there another way to get dynamic variables into the template?

    • Oliver Gašpar

      That’s how I do it. I doubt there’s another way, and this works, so why not. Also, I only render() if the rendered file is older than the .jade template. I’m not sure if this approach is acceptable in production, but that’s not a concern at this moment.

      • HotDang

        Seems a little fucky.

  • Taylor Ren

    I personally don’t like this kind of template syntax. I prefer more working in a stricter HTML + “injection” mode.

  • Grant Palin

    I’ve been tinkering with Jade for a while, contemplating using it for a new relatively static website. I like the up-front simplicity yet offered functionality when needed. A bummer that the linked library hasn’t been updated in a long time. An idea for (yet another) side project…

  • M S

    df h vzgfhxfhm&nthrs!

    *parsing*

    Hooray, moar syntax!

  • Tom Higginson

    This strikes me as being similar to Emmet with a different enough syntax (why not be even more CSS like and use element[key=value] type syntax?) to require a little extra effort. I’d recommend learning Emmet and you get all the nice shortcuts to writing full-blown HTML, which most template engines I’ve encountered can use anyway.

    • http://memeLab.com.au/ Tim Osborn

      Jade does `element(key=”value”)`… for me it’s not about shortcuts (love emmet in sublime) but maintainable code.. jade is a pleasure to read!

  • Matt M.

    I’ve read a lot of PHP articles like this over the years, and it amazes me that almost no one has written about PHPTAL. I discovered this library several years ago and have never looked back. I love its clean and simple syntax. It requires you to only memorize a handful of attributes that you then attach to almost any HTML tag. Add a few lines of code to your PHP script and you’re done. I’ve used it on almost every custom PHP site I’ve developed.

  • Diego German Navarro Tesillo

    I prefer mustachePHP…

  • Antonio Brandao

    I’m using that one (sisoftrg) for a large production site in a Laravel 4 project. It’s working nicely. It would be great if it didn’t have a few differences from standard Node-ish Jade (without $’s in conditionals for example) but as we’re working with PHP it’s OK to keep the pattern I guess. It’s still nicer than Blade imo.

    • Russ

      Hey how did you get it to work in Laravel? I’m using Laravel 5 if that makes a difference.