Pug-php adds inline PHP scripting support to the Pug template compiler

Overview

Pug-php

Latest Stable Version Monthly Downloads License StyleCI Test Coverage Code Climate Dependencies

Pug-php adds inline PHP scripting support to the Pug template compiler. Since version 3, it uses Phug, a very customizable Pug template engine made by the tale-pug and pug-php developers as the new PHP Pug engine reference.

Official Phug documentation
See Pug-php demo
Get supported pug-php/pug with the Tidelift Subscription

Install

First you need composer if you haven't yet: https://getcomposer.org/download/

Then run:

composer require pug-php/pug

Pug in your favorite framework

Phalcon: https://github.com/pug-php/pug-phalcon

Symfony: https://github.com/pug-php/pug-symfony

Laravel: https://github.com/BKWLD/laravel-pug

CodeIgniter: https://github.com/pug-php/ci-pug-engine

Yii 2: https://github.com/rmrevin/yii2-pug

Slim 3: https://github.com/MarcelloDuarte/pug-slim

Zend Expressive: https://github.com/kpicaza/infw-pug

Use



include 'vendor/autoload.php';

$pug = new Pug([
    // here you can set options
]);

$pug->displayFile('my-pug-template.pug');

Since pug-php 3.1.2, you no longer need to import the class with use Pug\Pug; as we provide an alias.

Main methods are render, renderFile, compile, compileFile, display, displayFile and setOption, see the complete documentation here: phug-lang.com.

You can also use the facade to call methods statically:



use Pug\Facade as PugFacade;

include 'vendor/autoload.php';

$html = PugFacade::renderFile('my-pug-template.pug');

Pug options

Pug options should be passed to the constructor

$pug = new Pug([
    'pretty' => true,
    'cache' => 'pathto/writable/cachefolder/',
]);

Supports for local variables

$pug = new Pug();
$output = $pug->renderFile('file.pug', [
    'title' => 'Hello World',
]);

New in pug-php 3

pug-php 3 is now aligned on pugjs 2, it aims to be a perfect implementation of the JS project. That's why there are breaking changes in this new version.

See the changelog to know what's new

See the migration guide if you want to upgrade from pug-php 2 to 3

Support for custom filters

Filters must be callable: It can be a class that implements the __invoke() method or an anonymous function.

$pug->filter('escaped', 'My\Callable\Class');

// or

$pug->filter('escaped', function($node, $compiler) {
    foreach ($node->block->nodes as $line) {
        $output[] = $compiler->interpolate($line->value);
    }

    return htmlentities(implode("\n", $output));
});

Built-in filters

  • :css
  • :php
  • :javascript
  • :escaped
  • :cdata

Install other filters with composer

http://pug-filters.selfbuild.fr/

Publish your own filter

https://github.com/pug-php/pug-filter-base#readme

Support for custom keywords

You can add custom keywords, here are some examples:

Anonymous function:

$pug->addKeyword('range', function ($args) {
    list($from, $to) = explode(' ', trim($args));

    return [
        'beginPhp' => 'for ($i = ' . $from . '; $i <= ' . $to . '; $i++) {',
        'endPhp' => '}',
    ];
});

$pug->render('
range 1 3
    p= i
');

This will render:

<p>1p>
<p>2p>
<p>3p>

Note that the existing for..in operator will have the precedence on this custom for keyword.

Invokable class:

', 'end' => '
', ]; } } $pug->addKeyword('user', new UserKeyword()); $pug->render(' user Bob badge(color="blue") badge(color="red") em Registered yesterday ');">
class UserKeyword
{
    public function __invoke($arguments, $block, $keyWord)
    {
        $badges = array();
        foreach ($block->nodes as $index => $tag) {
            if ($tag->name === 'badge') {
                $href = $tag->getAttribute('color');
                $badges[] = $href['value'];
                unset($block->nodes[$index]);
            }
        }

        return [
            'begin' => '
  
$keyWord . '" data-name="' . $arguments . '" data-badges="[' . implode(',', $badges) . ']">', 'end' => '
'
, ]; } } $pug->addKeyword('user', new UserKeyword()); $pug->render(' user Bob badge(color="blue") badge(color="red") em Registered yesterday ');

This will render:

Registered yesterday
">
<div class="user" data-name="Bob" data-badges="['blue', 'red']">
    <em>Registered yesterdayem>
div>

A keyword must return an array (containing begin and/or end entries) or a string (used as a begin entry).

The begin and end are rendered as raw HTML, but you can also use beginPhp and endPhp like in the first example to render PHP code that will wrap the rendered block if there is one.

PHP Helper functions

If you want to make a php function available in a template or in all of them for convenience, use closures and pass them like any other variable:

$myClosure));">
$myClosure = function ($string) {
    return 'Hey you ' . $string . ', out there on your own, can you hear me?';
};

$pug->render('p=$myClosure("Pink")', array('myClosure' => $myClosure));

This will render:

<p>Hey you Pink, out there on your own, can you hear me?p>

You can make that closure available to all templates without passing it in render params by using the share method:

// ... $pug instantiation
$pug->share('myClosure', $myClosure);
$pug->render('p=$myClosure("Pink")');

This will render the same HTML than the previous example. Also note that share allow you to pass any type of value.

Cache

Important: to improve performance in production, enable the Pug cache by setting the cache option to a writable directory, you can first cache all your template at once (during deployment):

$pug = new Pug([
    'cache' => 'var/cache/pug',
]);
[$success, $errors] = $pug->cacheDirectory('path/to/pug/templates');
echo "$success files have been cached\n";
echo "$errors errors occurred\n";

Be sure any unexpected error occurred and that all your templates in your template directory have been cached.

Then use the same cache directory and template directory in production with the option upToDateCheck to false to bypass the cache check and automatically use the cache version:

$pug = new Pug([
    'cache' => 'var/cache/pug',
    'basedir' => 'path/to/pug/templates',
    'upToDateCheck' => false,
]);
$pug->render('path/to/pug/templates/my-page.pug');

Templates from pug-js

First remember pug-php is a PHP template engine. Pug-js and Pug-php provide both, a HAML-like syntax for markup and some abstraction of the language behind it (loops, conditions, etc.). But for expressions and raw code, pug-js uses JS, and pug-php uses PHP. By default, we do some magic tricks to transform simple JS syntax into PHP. This should help you to migrate smoother from pug-js if you already have some templates, but benefit from the PHP advantages.

If you start a new project, we highly recommend you to use the following option:

$pug = new Pug([
    'expressionLanguage' => 'php',
]);

It will disable all translations, so you always have to use explicit PHP syntax:

- $concat = $foo . $bar
p=$concat

If you want expressions very close to JS, you can use:

$pug = new Pug([
    'expressionLanguage' => 'js',
]);

It will allow both PHP and JS in a JS-style syntax. But you have to stick to it, you will not be able to mix PHP and JS in this mode.

Finally, you can use the native pug-js engine with:

$pug = new Pug([
    'pugjs' => true,
]);

This mode requires node and npm to be installed as it will install pug-cli and directly call it. This mode will flatten your local variables (it means complex object like DateTime, or classes with magic methods will be stringified in JSON to simple objects) and you will not benefit from some features like mixed indent, pre/post render hooks. But in this mode you will get exact same output as in pug-js.

Write locals object to JSON file with pug-js

If your locals object is large it may cause a RuntimeException. This is because locals object passed directly to pug-cli as argument. To fix this problem you can use the localsJsonFile option:

$pug = new Pug([
    'pugjs' => true,
    'localsJsonFile' => true
]);

Then your locals will be written to a JSON file and the path of the file will be passed to the compiler.

Pug CLI

Pug also provide a CLI tool:

./vendor/bin/pug render-file dir/my-template.pug --output-file

See the complete CLI documentation here

Check requirements

To check if your environment is ready to use Pug, use the requirements method:

$pug = new Pug([
    'cache' => 'pathto/writable/cachefolder/',
]);
$missingRequirements = array_keys(array_filter($pug->requirements(), function ($valid) {
    return $valid === false;
}));
$missings = count($missingRequirements);
if ($missings) {
    echo $missings . ' requirements are missing.
'
; foreach ($missingRequirements as $requirement) { switch($requirement) { case 'streamWhiteListed': echo 'Suhosin is enabled and ' . $pug->getOption('stream') . ' is not in suhosin.executor.include.whitelist, please add it to your php.ini file.
'
; break; case 'cacheFolderExists': echo 'The cache folder does not exists, please enter in a command line : mkdir -p ' . $pug->getOption('cache') . '.
'; break; case 'cacheFolderIsWritable': echo 'The cache folder is not writable, please enter in a command line : chmod -R +w ' . $pug->getOption('cache') . '.
'; break; default: echo $requirement . ' is false.
'
; } } exit(1); }

Contributing

All contributions are welcome, for any bug, issue or merge request (except for security issues) please refer to CONTRIBUTING.md

Security

To report a security vulnerability, please use the Tidelift security contact. Tidelift will coordinate the fix and disclosure.

Contributors

This project exists thanks to all the people who contribute.

And all the people contributing to our dependencies, in particular: The Phug engine The JS syntax converter Js-Phpize

Backers

Thank you to all our backers! 🙏 [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. Become a sponsor

And a big thank-you to Jet Brains who provides such a great IDE:

Comments
  • Question regarding slow rendering

    Question regarding slow rendering

    Hello,

    I recently updated to the newest version available from Composer (from 2.x) and I've notice an incredible drop in rendering time. It can take up to 4-5 seconds to render. It renders around 1 second when I use the cached files, but when I'm in dev mode I like to always renders my files.

    Here's how I'm creating the Pug instance which is the same as the previous version:

    $options = [
        'basedir' => ROOT_DIR . '/public',
        'expressionLanguage' => 'js',
        'upToDateCheck' => true,
        'cache' => null
    ];
    $this->pug = new Pug($options);
    

    After that I'm using the renderFile method. So I was wondering what I can do to enhance the performances. I've seen that you have a pugjsoption which uses the pug-cli, but do I really need to install node.js to have better performances?

    Thanks a lot!

    performance 
    opened by dadajuice 46
  • Unexpected override occerd in block scopes

    Unexpected override occerd in block scopes

    Hi, I encountered an issue with the following code:

    data

    array(
      'text' => '2',
    )
    

    pug

    mixin paragraph(text)
      p= text
      block
      
    doctype html
    html
      body
        +paragraph('1')
          +paragraph(text)
        +paragraph(text)
    

    I expected to get:

    <!DOCTYPE html>
    <html>
      <body>
        <p>1</p>
        <p>2</p>
        <p>2</p>
      </body>
    </html>
    

    But I actually get:

    <!DOCTYPE html>
    <html>
      <body>
        <p>1</p>
        <p>1</p>
        <p>2</p>
      </body>
    </html>
    

    php-pug(3.2.0) will fail, but pugjs will success.

    Thanks!

    bug 
    opened by narirou 29
  • I could not call method of object as template local variable

    I could not call method of object as template local variable

    Hello,

    I encountered an issue with the following code: //php file

       $pug = new Pug(array(
            'cache' => $cachePath,
            'pugjs' => true,
            'expressionLanguage' => 'js',
            'basedir' => CURRENT_WORKING_DIR . '/client/templates/pug',
        ));
    
       $macroses_create = new Macroses();
    
       // class Macroses has getPagesById method
    
        $customVariables = array(
            'page_info' => $page_info_create,
            'macroses' => $macroses_create,
            'custom_macroses' => $custom_macroses_create,
            'req_params' => $page_info->getParams(),
            'req_method' => $page_info->getRequestMethod()
        );
        echo $pug->renderFileWithPhp($pathToIndexPugTemplate,  customVariables);
    

    //pug file

    - var getGlobalPageParams = {pageIds:[2],noActive:true,groups:["menu_group"]};
    - var getGlobalPage = macroses.getPagesById(getGlobalPageParams);
    

    I expected to get: the php method will be called

    But I actually get: it was error

    I try different settings, but it does not help. In previous pug-php(2) version it worked

    Thanks!

    opened by Freest10 25
  • read-only cache directory

    read-only cache directory

    Hello, im trying to trim down a docker production build which is using pug-php/pug 3.2.0

    What i would like to do:

    • pre cache templates
    • use read-only pre cached template
    • don't include original pug templates

    Current state:

    • pre caching works, but is kinda slow
    • can't use read-only cache directory, so i ship it writable
    • i have to include original pug templates

    Thanks!

    bug 
    opened by s4msung 20
  • localisation of strings

    localisation of strings

    I need to run all strings in plain language in a php localisation function.

    e.g. __( 'some string', 'mytheme' ) or _e( 'some string', 'mytheme' ) (shortcuts to php gettext)

    What is the best way to achieve this?

    I could build a data array and pre populate it with strings and pass it to pug:

    $data = [
      'string1' => __( 'some string', 'mytheme' )
    ];
    

    and than interpolate string1 inside pug.

    But surely there is a better way :)

    question added to the FAQ 
    opened by sandrodz 18
  • Can't use array member in jade / pug inline attributes directly?

    Can't use array member in jade / pug inline attributes directly?

    I want to show array member in foreach loop. It works fine except inline code combination like this:

    each i in ModuleAtMenu
       li: a.indent(href='?act=module&module=' + i.module_key)=i.module_title
    

    But this one is ok

    each i in ModuleAtMenu
       li: a.indent(href=i.module_key)=i.module_title
    
    enhancement 
    opened by dzpt 18
  •  Pug/Jade old interpolation syntax #{url} deprecated

    Pug/Jade old interpolation syntax #{url} deprecated

    We are using pug as a bridge between front-end and back-end repos. Our aim is to have same pug files both while building front-end with nodejs or when I compile it with php.

    Obviously we need to pass some variables to pug files. From php I make a big array and pass it as $data. In nodejs we have same $data exported as json and we feed pug that.

    Problem is that in php-pug only #{this_is_var} works and in nodejs version its deprecated.

    https://pugjs.org/language/attributes.html#attribute-interpolation

    What do you think, how can we deal with this?

    documentation 
    opened by sandrodz 17
  • Support for magic methods in JS syntax

    Support for magic methods in JS syntax

    (original, misleading description below)

    When using JS syntax it is not currently possible to access methods and properties that are handled by magic methods such as __get() or __call(). For example, this code:

    = errors.first('email_address')
    

    leads to an error when errors has a __call method but not a first method.


    Pretty self-explaining. This code snippet:

    if errors.has('email')
      = errors.first('email')
    

    leads to the error Function name must be a string (View: [redacted]/resources/views/signin.pug). Here's the context:

    error

    When using PHP syntax there's no such error

    if $errors->has('email')
      = $errors->first('email')
    

    And for the record, this hybrid syntax works as well:

    if errors->has('email')
      = errors->first('email')
    

    So this is probably a bug with the JS-PHP conversion.

    enhancement documentation 
    opened by J5lx 17
  • Error in the render() method for large template files (more than 8192 bytes after compilation)

    Error in the render() method for large template files (more than 8192 bytes after compilation)

    Call method:

    Jade->render('.../template.jage')

    return Exeption:

    Warning: include(): Jade\Stream\Template::stream_read - read 250 bytes more data than requested (8442 read, 8192 max) - excess data will be lost in .../Jade.php on line 319

    help wanted added to the FAQ 
    opened by temoffey 17
  • Use javascript library in template

    Use javascript library in template

    Hello. Is it possible to use a javascript library (moment, for example) in a template? I know I can pass php closures but I don't know how to pass javascript functions. Maybe we could extend $pug->share in some way?

    Thanks!

    enhancement question added to the FAQ 
    opened by MarcelRobitaille 16
  • pug file compiles fine with pug-cli, but fails with pug-php

    pug file compiles fine with pug-cli, but fails with pug-php

    My index.pug compiles just fine to html when using pug-cli, but gives

    Uncaught ErrorException: /var/www/html/sheba_new/jade/dump/_index_dump.pug (2) : /var/www/html/sheba_new/jade/dump/_index_dump.pug (2) : Unexpected token "indent"

    when trying to compile it 'render' it with pug-php.

    Also, why is that I'm declaring an extension attribute in the Pug builder, but when I;m rendering the file I have to specify its full filename?

    ex: echo $pug->render("jade/pages/index"); will render a blank page echo $pug->render("jade/pages/index.pug"); renders the needed file

    opened by iAlex97 16
  • Bug when using combinaison of mixins with block in iteration

    Bug when using combinaison of mixins with block in iteration

    Hello,

    I recently switched a project from Pug 2 to Pug 3 and I encountered a very specific kind of bug. It needs couple blocks of code to properly explain. I excuse myself in advance for my explanation which is hard to summarize 😅 There seem to be a problem when I call a mixin (in my code the +delete-button()) from within a mixin's block part in a loop (defined in my list) which has beed programmed using at least one mixing block (+list-headers(headers)).

    //-
      GENERIC MIXIN EXAMPLE
    mixin delete-button()
      button.btn.btn-sm.btn-danger delete
    
    //-
      MIXIN EXAMPLES FOR LIST RENDERING
    mixin list-headers(headers)
      thead
        tr
          each header, i in headers
            th=header
    
    mixin list(rows, headers)
      .table-wrapper
        table.table.table-sm
          +list-headers(headers)
          tbody
              each row in rows
                block
    
    //-
      VARIABLES DEFINITION (normally given as the pug file's arguments)
    -rows = [{id: 1, name: "Martin Sandwich"}, {id: 2, name: "Jumbo Moutarde"}, {id: 3, name: "Sir. Bob Lewis"}]
    -headers = ['number', 'name']
    
    //-
      LIST MIXIN USAGE [WORKING CASE WITHOUT OTHER MIXIN CALLED]
    +list(rows, headers)
      tr
        td=row.id
        td=row.name
    
    //-
      LIST MIXIN USAGE [NOT WORKING CASE WITH OTHER MIXIN CALLED]
    +list(rows, headers)
      tr
        td=row.id
        td=row.name
        td
          +delete-button()
    

    What happens is that the variable row, doesn't change. It stays at the first element of my rows variable. But, if I remove the +delete-button() call, it will work as expected (having the correct 3 names). Also, if I comment the +list-headers(headers) in the list mixin straight and I keep the +delete-button it will also work as expected. So the result of the above Pug file will be :

    <div class="table-wrapper">
        <table class="table table-sm">
            <thead>
                <tr>
                    <th>number</th>
                    <th>name</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>1</td>
                    <td>Martin Sandwich</td>
                </tr>
                <tr>
                    <td>2</td>
                    <td>Jumbo Moutarde</td>
                </tr>
                <tr>
                    <td>3</td>
                    <td>Sir. Bob Lewis</td>
                </tr>
            </tbody>
        </table>
    </div>
    <div class="table-wrapper">
        <table class="table table-sm">
            <thead>
                <tr>
                    <th>number</th>
                    <th>name</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>1</td>
                    <td>Martin Sandwich</td>
                    <td>
                        <button class="btn btn-sm btn-danger">delete</button>
                    </td>
                </tr>
                <tr>
                    <td>1</td>
                    <td>Martin Sandwich</td>
                    <td>
                        <button class="btn btn-sm btn-danger">delete</button>
                    </td>
                </tr>
                <tr>
                    <td>1</td>
                    <td>Martin Sandwich</td>
                    <td>
                        <button class="btn btn-sm btn-danger">delete</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
    

    It is a very weird bug to kinda explain as it needs a specific combinaison of mixins within mixins within a block inside a loop to properly replicate. As I stated at the beginning, I switched from Pug 2 and this use case was working (if it can be of any help). I tried my best to simplify the use case to the bare minimum from my part to isolate the problem. I hope it can help.

    Thank you very much for your time and this amazing project 😊

    bug 
    opened by dadajuice 0
Releases(2.7.7)
PeachPie - the PHP compiler and runtime for .NET and .NET Core

PeachPie Compiler The open-source PHP compiler to .NET If you run into any inconsistencies, bugs or incompatibilities, kindly let us know and we'll do

PeachPie Compiler Platform 2.1k Dec 22, 2022
High performance view templating API for PHP applications using tags & expressions inspired by Java JSTL and C compiler

View Language API Table of contents: About Expressions Tags Configuration Compilation Installation Unit Tests Examples Reference Guide About This API

Lucian Gabriel Popescu 0 Jan 9, 2022
MassPlugCompiler - Textpattern CMS plugin compiler

mtxpc mtxpc compiles Textpattern CMS plugin sources into installer packages. Supports multi-file structure and a JSON manifest file. Install Using Com

Jukka Svahn 5 Apr 15, 2022
Task for GrumPHP that adds CSS linting support with stylelint. An easy way to enforce convention and avoid errors in your styles

grumphp-stylelint-task Installation Stylelint is a static analysis tool for styles. A mighty, modern linter that helps you avoid errors and enforce co

null 3 Apr 29, 2021
Silverstripe-fulltextsearch - Adds external full text search engine support to SilverStripe

FullTextSearch module Adds support for fulltext search engines like Sphinx and Solr to SilverStripe CMS. Compatible with PHP 7.2 Important notes when

Silverstripe CMS 42 Dec 30, 2022
Adds support for quickly adding a "snow day" banner at the top of a website.

❄️ Snow Day Plugin ?? Requires OctoberCMS 2.0 ✨ What does this plugin do? Provides the ability to quickly add a cross-site banner using a component. ❓

Albright Labs 4 Nov 7, 2022
PaaS template based on production template using platform.sh

Shopware for Platform.sh This template builds Shopware on Platform.sh using Composer. To get started on Platform.sh, please visit https://docs.platfor

Shopware 9 Oct 12, 2022
PDF API. JSON to PDF. PDF Template Management, Visual HTML Template Editor and API to render PDFS by json data

PDF Template Management, Visual HTML Template Editor and API to render PDFS by json data PDF ENGINE VERSION: development: This is a prerelease version

Ajous Solutions 2 Dec 30, 2022
Michael Pratt 307 Dec 23, 2022
The Assure Alliance support website. This website is based on Questions2Answers and is a forum for support using Biblical Tools

The Assure Alliance support website. This website is based on Questions2Answers and is a forum for support using Biblical Tools

United Bible Societies Institute for Computer Assisted Publishing 3 Jul 29, 2022
Adds a header to every response to try and twart Google's usage of your site in it's FLoC tracking method.

Laravel No FLoC This package will add the Permissions-Policy: interest-cohort=() to try and twart Google's usage of your site in it's FLoC tracking me

Jean-Philippe Murray 11 Jul 14, 2022
A plugin that adds worker entities to minecraft.

WorkersDemo A plugin that adds worker entities to minecraft. Workers does things that players does such as mining wood/stone etc. How to use? You can

Oğuzhan 6 Dec 17, 2021
This plugin adds custom pets to game for PocketMine-MP!

ComplexPets A plugin that adds pets to game made by OguzhanUmutlu for PocketMine-MP. Command Simply type /pets and summon your favorite animal! Featur

Oğuzhan 10 Aug 12, 2021
A plugin that adds minions to your Minecraft Server

BetterMinion A plugin that adds minions to your Minecraft Server ⚠️ This project is currently unfinished and needs some help! If you'd like to help us

Khang Nguyen 1 Jul 30, 2022
Adds a compact "easy-sort" mode to Repeater and Repeater Matrix, making those fields easier to sort when there are a large number of items.

Repeater Easy Sort Adds a compact "easy-sort" mode to Repeater and Repeater Matrix, making those fields easier to sort when there are a large number o

Robin Sallis 3 Oct 10, 2021
Adds factory functions for WooCommerce to be used with wp-browser integration tests.

wp-browser-woocommerce This library simplifies testing of WooCommerce themes and plugins with wp-browser. Several Unit Test Factories are added that a

Level Level 12 Dec 29, 2022
A plugin that adds minions to your Minecraft Server

BetterMinion A plugin that adds minions to your Minecraft Server ⚠️ This project is currently unfinished and needs some help! If you'd like to help us

null 24 Apr 25, 2022
⚗️ Adds code analysis to Laravel improving developer productivity and code quality.

⚗️ About Larastan Larastan was created by Can Vural and Nuno Maduro, got artwork designed by @Caneco, is maintained by Can Vural, Nuno Maduro, and Vik

Nuno Maduro 4.4k Jan 4, 2023
This plugin adds 95% of 1.16 blocks & items and their functionality

INether This plugin adds 95% of 1.16 blocks & items and their functionality Implemented blocks Ancient Debris All types of Basalt Crimson & Warped Fun

null 34 Dec 7, 2022