jade-esque templates for PHP 5.3

Installs: 2 364

Dependents: 1

Stars: 15

Watchers: 8

Forks: 2

Open Issues: 14

Language: PHP


Lavender is a templating language for php loosely based on jade.


via composer:

first get yourself some composer. next you need to make yourself a composer.json, here's an example.

  "require": {
    "lavender/lavender": "*"

then run php composer.phar install or composer install depending on how you installed composer. once composer has finished it will generate an autoloader in vendor/autoload.php which you can require() from your application's bootstrap process.

via git:

add the lavender submodule with git submodule add <folder to clone to> and include it with require "<where you put lavender>/src/Lavender/lavender.php"


once you have installed and included lavender the only required configuration is to tell lavender where the views directory is.


   * required - path to views directory
  'view_dir'       => String,

   * optional - cache view files, if this is true
   *            the cache folder needs to be emptied
   *            every time view files are changed
  //'caching'        => FALSE,

   * optional - defaults to something random in your tmp directory
  //'cache_dir'      => NULL,

   * optional - defaults to "lavender"
  //'file_extension' => String,

   * optional - defaults to TRUE
   * renders a debugging error page instead of throwing an exception.
   * in production you should disable this and use a 500 page.
  //'handle_errors'  => Boolean, 

rendering a template is as easy as

$output = Lavender::view('some_template')->compile();

// or if you need to pass data into the template (probably the case)

$output = Lavender::view('some_template')->compile(array(
  'data'      => 'some data that the template will use',
  'more_data' => "moar data",

language reference



<section id="some_id" class="foo bar" attribute1="foo" attribute2="bar"></section>

child nodes are indented below their parent


<section class="foo">
  <div data-something="foo"></div>
  <div data-something="bar"></div>

there is a shortcut if you just want a div, you can skip right to class or id definitions

.foobar text text
#foobar text text


<div class="foobar">text text</div>
<div id="foobar">text text</div>

text can be added to nodes in two ways
  div(data-something='foo') this is some text 
    | this is also some text 

Lavender knows not to put a closing tag on certain nodes. (we got the list from here)



<input type="text" value="fooooobar">


do you always forget the syntax for doctype? Lavender exists to solve your first world problems. just throw a !!! at the top of your layout and you get yourself a nice html doctype. doctypes supported are html, transitional, strict, frameset, 1.1, basic and mobile. here are some pretty examples.


defaults to html but you can also specify

!!! html

the word doctype works too

doctype html
doctype strict


comments are a thing

// this is an html comment. it will be rendered to the page
//- this is a lavender comment. it will not be rendered

conditional comments

the IE shorthand can be used to generate conditional comments for Internet Explorer. check out this if you're not farmiliar with conditional comments.

  script(src="some script to make IE work")
IE lt 7
  script(src="some script to make super old IE work")


<!--[if IE]>
  <script src="some script to make IE work"></script>
<!--[if lt IE 7]>
  <script src="some script to make super old IE work"></script>


lavender views can be passed variables from the parent php application or they can be defined right in the template.

this just assigns a variable without outputting anything to the page

- some_variable = "this is stored in a variable"

note the - symbol, which evaluates an expression without outputting. the content of the variable can then later be output using the = symbol

- some_variable = "this is stored in a variable" some_variable
  = some_variable


<span class="foo">this is stored in a variable</div>
<span class="bar">this is stored in a variable</div>

arrays too

- some_array = ['foo', 'bar', 'baz']

and associative arrays

- some_array = {key1: 'foo', key2: 'bar', key3: 'baz'} 


markup can be conditionally rendered using the if keyword. Lavender supports all the conditional operations you know and love !, &&, ||, <. >, <=, >=, ==, != and does a truthy check against the result.

- some_variable = "this is stored in a variable"

if some_variable some_variable

if some_variable_that_doesnt_exist this won't be rendered

  div this isn't rendered
elseif FALSE
  div still not rendered
elseif TRUE
  div wooooooooo
  div sadface


<span class="foo">this is stored in a variable</div>

TRUE and FALSE (and true and false) are keywords in Lavender, they can be assinged to variables or used in conditionals directly (but if you actually do that then wat)

- my_variable = TRUE

if my_variable
  div this will show up

if false
  div this will not


Lavender supports iterating over arrays and associative arrays with the each keyword

  each value in some_random_array
    li= value 

  each value, key in some_random_array
    li(data-key=key)= value 

you may have noticed how i snuck in using variables for your html attributes there, yea you can do that too.


the else keyword can be used after loops or conditionals

  div will not show up
  div will show up

- empty_array = []

each value in empty_array
  div nothing to see here
  div empty array!


math. you can do it. supported operators are %, +, -, *, /, (, )

  | 1 + 1 = 
  = 1 + 1

  | 2 - 3 = 
  = 2 - 3

  | 2 * 3 = 
  = 2 * 3

  | 10 / 5 = 
  = 10 / 5

  each value, key in some_random_array
    if key % 2 == 0
      li(data-key=key)= value 


partials are a thing

span stuff and things

  include /path/relative/to/view/directory/somefile

  include /path/relative/to/view/directory/somefile with {stuff: "Asdfasdf"}


Lavender supports block style layout extension. this means that in the parent template you define blocks using the block keyword. then in the child template you only have blocks which override the blocks in the parent template

so if this was layout.lavender

h1 this is a pretty cool web page

block header
  | you can put some default content in here
  | it will be displayed if no child template
  | overrides it

  block content

and this is the child template

extends layout

block header
      | foo bar baz

block content
  | content
  | more content
  | moar content

and you were to render the child temlate, you would get

<h1>this is a pretty cool web page</h1>

  <span>foo bar baz</span>

<div class="content">
  more content
  moar content

a special case has been added to allow definining variables at the top of your child templates, these variables can be referenced from the layout to do cool things like specifying a list of javascript files necessary for a template to work or setting the page title.


newlines are added automatically after every html element, if you don't want the newline you can add a - to the node definition like so.

  a(href="/some/place")- click here for some cool stuff

automagic whitespace management is hard and subject to change.


programatic expressions in Lavender are a little limited, none of your favorite php functions are available for modifying the template data. this is by design, we don't think there should be a ton of logic in templates when that logic could be in controllers or models. however since you have to be able to do some templating logic we added filters. it's super easy to add your own filters to Lavender and there are (or will be) plenty in place out of the box. here's how they work.

- myvariable = "some really cool text"

    | i'm gonna filter some stuff. it's gonna be cool. 
    = myvariable | upper 


  <span>im gonna filter some stuff. it's gonna be cool. SOME REALLY COOL TEXT</span>

profound right? not that we invented this, we copied the idea from twig. the filters Lavender comes with are:

upper: ="string"|upper becomes STRING

trim: =" string "|trim becomes string

title: ="this is a title"|title becomes This Is A Title

split: "these are some words"|split(' ') becomes the array ['these', 'are', 'some', 'words']

sort: "these are some words"|split(' ')|sort becomes the array ['are', 'some', 'these', 'words']

capitalize: = "these are some words"|capitalize becomes These are some words

date: = "1396372567"|date becomes 4/1/2014 = "1396372567"|date('Y') becomes 2014

default: = []|default('foobar') becomes foobar

first: = ['asdf','qwer']|first becomes asdf

join: = ['asdf','qwer']|join becomes asdf qwer = ['asdf','qwer']|join('-') becomes asdf-qwer

last: = ['asdf','qwer']|last becomes qwer = ['asdf','qwer', 'zxcv']|last(2)|join('-') becomes qwer-zxcv

keys: {'asdf': 'foo','qwer': 'bar'}|keys becomes the array ['asdf', 'qwer']

length: = ['asdf','qwer']|length becomes 2

lower: = "FOO BAR"|lower becomes foo bar

nl2br: = "new\nline"|nl2br becomes new<br>line

round: = 12345.123|round(2) becomes 12345.12

merge: ['foo', 'bar']|merge(['baz']) becomes the array ['foo', 'bar', 'baz']

replace: = "foo bar"|replace(' ', '-') becomes foo-bar

reverse: ['asdf', 'qwer']|reverse becomes the array ['qwer', 'asdf']

slice: ['foo', 'bar', 'baz']|slice(1, 1) becomes the array ['bar']

number_format: = 10000000000.12345|number_format() becomes 10,000,000,000 = 10000000000.12345|number_format(2, ',', '.') becomes,12

ceil: = 10000000000.12345|ceil becomes 10000000001

floor: = 10000000000.12345|floor becomes 10000000000

relative: if you had a timestamp variable timestamp|relative would return things like 'just now' or 'in 3 days' or 6 minutes ago

contains: = ['asdf', 'qwer']|contains('asdf') returns TRUE


= ['asdf', 'qwer']|is('list') returns TRUE

= {asdf: 'qwer'}|is('object') returns TRUE

= 8|is('number') returns TRUE

= myVariable|is('My_Class_Name') also works

json: = ['asdf', 'qwer']|json returns "['asdf','qwer']"