Skip to content
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
Cannot retrieve contributors at this time

Migrate from pug-php 2 to 3

Here are the migration steps you should follow get an app running with pug-php 2 working with pug-php 3.

Update the dependency

First if you use a PHP version lower than 5.5, you will need to upgrade it. We recommend you PHP 7.1.

Supposing you manage your project dependencies with composer (this really is the recommended way), you should run the following command:

composer require pug-php/pug:3.*

Else, go the releases, download the release and replace your own copy of pug with the archive content.

Stay with pug-php 2

If you need to keep PHP 5.3/5.4 support or want to stay with pug-php 2, you can require it explicitly to be sure it won't be upgraded:

composer require pug-php/pug:2.*

API changes

In Pug-php 2 ->render could be used for both input string or input file. This means we first detect if the argument passed is a file. This was not good for performance and could create unexpected behaviour.

Now ->renderFile exists and we highly recommend you to use it when you render files. This also has the advantage to match the pug-js API:

$pug = new Pug([
  'strict' => true, // disable the file detection

$pug->render('div.pug'); // render <div class="pug"></div>
$pug->renderFile('div.pug'); // render the file ./div.pug

Consider to use strict mode when possible. When strict is true, it will follow best practices and throw exception when something is deprecated. By default strict is false and keep as much as possible backward compatibility with pug-php 2.

Syntax changes

Variables assignations

Variables assignations like myVar = 'myValue is no longer supported, now use simply raw code instead: - myVar = 'myValue

Mixin calls

In pug-php 2 mixin foo() was valid code for both declarations and calls, now the only way to call them is: +foo() and mixin keyword is reserved for declaration.

Attribute string interpolation

The attribute interpolation has been dropped (to match pugjs 2 specifications), but you still can use simple concatenation instead:



a(href="?id=" +

Important This only concerns string interpolations in attributes, it's still valid in texts:

p #{}

Attributes order

Attribute order and merge you get in pug-php 2 could not be the same in pug-php 3. Example:

div.d(a="b" class="c")

Will output:

in pug-php 2 in pug-php 3
<div a="b" class="d c"></div> <div class="d c" a="b"></div>

Dropped syntax

if condition: no longer exists, just use if condition

New options


Now, by default, including a file that does not exist throw a exception. We recommend you always include files that cannot be missing and also avoid any trick that would include a dynamic path.

However you can still set a default template that will replace missing includes:

$pug = new Pug(['not_found_template' => 'p Coming soon']);
    "h1 Video page\n".
    "include video.pug\n"

If video.pug does not exists, you will get:

<h1>Video page</h1>
<p>Coming soon</p>

Pretty output

The prettyprint should no longer be used and indentChar and indentSize has been removed. Now you should just use pretty option to prettify the HTML output. true indent with 4 spaces, false does not indent, else you can pass a string to indent with.

Format options

The phpSingleLine option no longer exists and by default, there is no new lines added, but you can patterns option:

$pug = new Pug([
 'patterns' => [
   'php_handle_code' => "<?php %s ?>\n",
   'php_nested_html' => "<?= %s ?>\n",

The singleQuote option no longer exists but you can patterns option:

$pug = new Pug([
  'patterns' => [
    'attribute_pattern'         => " %s='%s'",
    'boolean_attribute_pattern' => " %s='%s'",
    'html_expression_escape'    => 'htmlspecialchars(%s, ENT_QUOTES)',

Note: Adding ENT_QUOTES is needed to escape ' inside attributes values.

The restrictedScope option no longer exists. Instead, you can scope variables with the let keyword:

mixin test
  p=let foo = 1
  p=let foo = 2
    p=let foo = 3

The keepNullAttributes option no longer exists, now null and false attributes are just always removed like in pugjs.

The terse no longer exists, now attribute format can be set via patterns or using the matching doctype.

Mixin override

The allowMixinOverride option no longer exists, but you can get the same behavior with mixin_merge_mode, see below the option values:

mixin_merge_mode (pug-php 3) allowMixinOverride (pug-php 2) New declaration of a existing mixin
'replace' (default) true (default) replace the first one
'ignore' false is ignored
'fail' no equivalent throws an exception

Caution: this behavior is now only valid for mixins declared in the same scope (file and block).

Dropped features

The cache option no longer works for string rendering but only for file rendering.


If you used try {} catch {} on pug exceptions, be aware that most exceptions code and messages will be different because theyr are now handled by our new engine phug.


You can now longer insert raw texts or interpolations in filter contents, and your custom filters may no longer work since now filters receive 2 params: contents as a string matching the raw input contents given to your filter and options. Example:

$pug = new Pug([
  'filters' => [
    'foo' => function ($contents, array $options) {
      return json_encode($options)."\n".$contents;

  ":foo(a=1 b="2")\n".
  "  bar"

This will output:


Custom keywords

The second argument received by your custom keyword functions is now a KeywordElement instance. We added some backward-compatible API to make it close the the node instance you get before but some complex keywords functions may need to be refactored.