A PHP port of Ruby's Liquid Templates

Overview

Liquid template engine for PHP Build Status Coverage Status Total Downloads

Liquid is a PHP port of the Liquid template engine for Ruby, which was written by Tobias Lutke. Although there are many other templating engines for PHP, including Smarty (from which Liquid was partially inspired), Liquid had some advantages that made porting worthwhile:

  • Readable and human friendly syntax, that is usable in any type of document, not just html, without need for escaping.
  • Quick and easy to use and maintain.
  • 100% secure, no possibility of embedding PHP code.
  • Clean OO design, rather than the mix of OO and procedural found in other templating engines.
  • Seperate compiling and rendering stages for improved performance.
  • Easy to extend with your own "tags and filters":https://github.com/harrydeluxe/php-liquid/wiki/Liquid-for-programmers.
  • 100% Markup compatibility with a Ruby templating engine, making templates usable for either.
  • Unit tested: Liquid is fully unit-tested. The library is stable and ready to be used in large projects.

Why Liquid?

Why another templating library?

Liquid was written to meet three templating library requirements: good performance, easy to extend, and simply to use.

Installing

You can install this lib via composer:

composer require liquid/liquid

Example template

{% if products %}
	<ul id="products">
	{% for product in products %}
	  <li>
		<h2>{{ product.name }}</h2>
		Only {{ product.price | price }}

		{{ product.description | prettyprint | paragraph }}

		{{ 'it rocks!' | paragraph }}

	  </li>
	{% endfor %}
	</ul>
{% endif %}

How to use Liquid

The main class is Liquid::Template class. There are two separate stages of working with Liquid templates: parsing and rendering. Here is a simple example:

use Liquid\Template;

$template = new Template();
$template->parse("Hello, {{ name }}!");
echo $template->render(array('name' => 'Alex'));

// Will echo
// Hello, Alex!

To find more examples have a look at the examples directory or at the original Ruby implementation repository's wiki page.

Advanced usage

You would probably want to add a caching layer (at very least a request-wide one), enable context-aware automatic escaping, and do load includes from disk with full file names.

use Liquid\Liquid;
use Liquid\Template;
use Liquid\Cache\Local;

Liquid::set('INCLUDE_SUFFIX', '');
Liquid::set('INCLUDE_PREFIX', '');
Liquid::set('INCLUDE_ALLOW_EXT', true);
Liquid::set('ESCAPE_BY_DEFAULT', true);

$template = new Template(__DIR__.'/protected/templates/');

$template->parse("Hello, {% include 'honorific.html' %}{{ plain-html | raw }} {{ comment-with-xss }}");
$template->setCache(new Local());

echo $template->render([
    'name' => 'Alex',
    'plain-html' => '<b>Your comment was:</b>',
    'comment-with-xss' => '<script>alert();</script>',
]);

Will output:

Hello, Mx. Alex
<b>Your comment was:</b> &lt;script&gt;alert();&lt;/script&gt;

Note that automatic escaping is not a standard Liquid feature: use with care.

Similarly, the following snippet will parse and render templates/home.liquid while storing parsing results in a class-local cache:

\Liquid\Liquid::set('INCLUDE_PREFIX', '');

$template = new \Liquid\Template(__DIR__ . '/protected/templates');
$template->setCache(new \Liquid\Cache\Local());
echo $template->parseFile('home')->render();

If you render the same template over and over for at least a dozen of times, the class-local cache will give you a slight speed up in range of some milliseconds per render depending on a complexity of your template.

You should probably extend Liquid\Template to initialize everything you do with Liquid::set in one place.

Custom filters

Adding filters has never been easier.

$template = new Template();
$template->registerFilter('absolute_url', function ($arg) {
    return "https://www.example.com$arg";
});
$template->parse("{{ my_url | absolute_url }}");
echo $template->render(array(
    'my_url' => '/test'
));
// expect: https://www.example.com/test

Requirements

  • PHP 7.0+

Some earlier versions could be used with PHP 5.3/5.4/5.5/5.6, though they're not supported anymore.

Issues

Have a bug? Please create an issue here on GitHub!

https://github.com/kalimatas/php-liquid/issues

Fork notes

This fork is based on php-liquid by Harald Hanek.

It contains several improvements:

  • namespaces
  • installing via composer
  • new standard filters
  • raw tag added

Any help is appreciated!

Comments
  • Additional tests

    Additional tests

    • [x] deal with LocalFileSystem, extending tests coverage to the remaining lines
    • [x] integration test for include tag with an actual file
    • [x] basic test for extends tag
    • [x] full test case for extends tag
    • [x] test case for Template::getCache when used in extends and include tags
    opened by sanmai 25
  • Allow using `FILTER_SEPARATOR` as a split filter parameter in filters for variable assignment

    Allow using `FILTER_SEPARATOR` as a split filter parameter in filters for variable assignment

    Refactor TagAssign to use a Variable instance in the value assignment instead of a string. Remove QUOTED_STRING config from the TestCase as it was different from the default config for no reason that I could see.

    opened by jfoucher 15
  • Return

    Return "deep variable" working incorrect

    I have issue

    {{ settings.my_key }}
    

    My mean is echo property my_key in settings object. If my_key is exists, it return well, but when my_key no exists, it return all property in settings object. I think it have to return null

    Please check. Sorry my bad english

    bug 
    opened by phambinh217 9
  • Throw different types of exception depending on the case instead of always throwing a LiquidException

    Throw different types of exception depending on the case instead of always throwing a LiquidException

    I have 6 different types of exceptions depending on the case :

    • CacheException for cache configuration errors. (extends Liquid\LiquiException)
    • NotFoundException in case of file not found exception (extends FilesystemException)
    • FilesystemException in case of errors with the filesystem. (extends Liquid\LiquiException)
    • ParseException in case of parsing errors (extends Liquid\LiquiException)
    • RenderException in case of render errors (extends Liquid\LiquiException)
    • WrongArgumentException for wrong arguments passed to filterBank::addFilter (extends Liquid\LiquiException)

    Let me know what you think.

    Resolves https://github.com/kalimatas/php-liquid/issues/74

    • [x] I've run the tests with vendor/bin/phpunit
    • [x] None of the tests were found failing
    • [x] I've seen the coverage report at build/coverage/index.html
    • [x] Not a single line left uncovered by tests
    • [x] Any coding standards issues were fixed with vendor/bin/php-cs-fixer fix
    • [x] Hotfix-type commits were squashed with git rebase -i or git commit --amend
    • [x] All my work wasn't smushed into one large commit without necessity
    opened by jfoucher 9
  • TagPaginate: Configurable 'page' keys #94

    TagPaginate: Configurable 'page' keys #94

    • [x] I've run the tests with vendor/bin/phpunit
    • [x] None of the tests were found failing
    • [x] I've seen the coverage report at build/coverage/index.html
    • [x] Not a single line left uncovered by tests
    • [x] Any coding standards issues were fixed with vendor/bin/php-cs-fixer fix
    • [x] Hotfix-type commits were squashed with git rebase -i or git commit --amend
    • [x] All my work wasn't smushed into one large commit without necessity
    opened by edwardoka 8
  • LocalFileSystem bad exception message

    LocalFileSystem bad exception message

    Let's say you get a template name wrong. This can happen for any number of reasons. In my case, I didn't realize there was a default _ prefixing includes.

    You initialize Liquid with

    $renderer = new \Liquid\Template(__DIR__ . '/templates/');
    $renderer->parse(file_get_contents(__DIR__ . '/templates/'));
    echo $renderer->render([]);
    

    The template looks like this:

    {% include 'header' %}
    Hello
    {% include 'footer' %}
    

    Inside LocalFileSystem $fullPath is set to /src/project/templates/_header.liquid, which does not exist.

    The following exception is then thrown:

    Details
    Type: Liquid\LiquidException
    Message: Illegal template path ''
    File: C:\src\project\vendor\liquid\liquid\src\Liquid\LocalFileSystem.php
    Line: 84
    

    This isn't really that helpful. PHP's docs on realpath state that for a missing file, realpath will return false. So as far as I can tell, there is no case when the current thrown exception will give a useful error message.

    Is there a reason that realpath is being called here?

    question 
    opened by JoshWillik 8
  • Add supporting filter of the PSR-2

    Add supporting filter of the PSR-2

    Add supporting PSR-2 method namign Replace satooshi/php-coveralls to php-coveralls/php-coveralls

    • [X] I've run the tests with vendor/bin/phpunit
    • [X] None of the tests were found failing
    • [X] I've seen the coverage report at build/coverage/index.html
    • [X] Not a single line left uncovered by tests
    • [X] Any coding standards issues were fixed with vendor/bin/php-cs-fixer fix
    opened by almost-online 7
  • White space control

    White space control

    Hello! We're a big fan of this package -- thank you very much for creating and maintaining!

    One of our clients tried to implement white space control. Example:

    {%- assign my_variable = "tomato" -%}
    

    However, when creating tags with a dash "-", we're experiencing a "Tag not properly terminated error".

    Liquid \ Exception \ ParseException
    Tag {%- assign my_variable = "tomato" -%} was not properly terminated
    

    We're using version 1.4.8.

    Is this something you're intending to support in future builds?

    Thank you very much in advance! -Rich

    help wanted feature request 
    opened by rjhankison 7
  • Add support for array first/last dot notation

    Add support for array first/last dot notation

    • [x] I've run the tests with vendor/bin/phpunit
    • [x] None of the tests were found failing
    • [x] I've seen the coverage report at build/coverage/index.html
    • [x] Not a single line left uncovered by tests
    • [x] Any coding standards issues were fixed with vendor/bin/php-cs-fixer fix

    Array first and last. If you have an expression whose value is an array, you can follow it with .first or .last to resolve to its first or last element. https://github.com/Shopify/liquid/wiki/Liquid-for-Designers

    opened by funkjedi 5
  • If you use filter without required argument then php throw `Type Error`.

    If you use filter without required argument then php throw `Type Error`.

    Try:

    {{"now"|date }} or {{ 4 | plus }}
    

    and you get:

    Type error: Too few arguments to function Liquid\StandardFilters::date(), 1 passed and exactly 2 expected 
    

    An LiquidException should be thrown.

    bug 
    opened by PATROMO 5
  • endpaginate does not end pagination

    endpaginate does not end pagination

    Ending pagination still paginates outside of the paginate block.

    For example in a blog where you want to paginate posts but also have a sidebar that reuses the same data.

    `{% paginate blog.articles by 4 %} {% for article in blog.articles %} {{ article.title }} {% endfor %} {% endpaginate %}

    {% for article in blog.articles %} {{ article.title }} {% endfor %}`

    opened by asacarter 5
  • Deprecated Functionality: Function strftime() is deprecated

    Deprecated Functionality: Function strftime() is deprecated

    We have a magento 2 function that calls your package. We are upgrading to PHP8.1 and the function strftime is deprecated

    Full error message Deprecated Functionality: Function strftime() is deprecated in /app/vendor/liquid/liquid/src/Liquid/StandardFilters.php on line 81

    Do you know of a way to use something different than strftime.

    Thanks!

    bug 
    opened by birdman002 4
  • tag names with dots is removed

    tag names with dots is removed

    If i the following scenario, which is used often in the project i work with. $template = new Template; $template->parse("Hello, {{ name.1 }}!") $template->render(array('name.1'=> 'Kennet')) it will output "Hello, !" so if the array key has a . in it it does not replace the tag correctly

    If i remove the . from both the parse and render function call, it works as expected, but as 'name.1' is a valid name for a php array key, i think it should work the same. Or have i missed something? :)

    opened by 99kennetn 1
  • Can I use the % operator?

    Can I use the % operator?

    Hi!

    I have an issue what I cant seem to solve. I have a for loop and I want to echo something every 4th iteration of the for loop.

    Normaly I would something like index % 4 === 1 that echo that thing but this is not working.

    Anyone know how I can solve this?

    opened by brogier 0
  • TagBlock elements missing in nodeList

    TagBlock elements missing in nodeList

    I'm working on a Liquid-to-Twig converter, and am very appreciative of this library, thanks for all the work you've done on it.

    If the source is

    {% comment %} This is the child template. {% endcomment %}
    {% extends "blocks/child.tpl" %}
    
    {% block content %}
        	<h2>Entry one</h2>
        <p>This is my first entry.</p>
    {% endblock %}
    

    and I run

                    $liquidTemplate = $liquid->parse($liquidSource);
                   dump($liquidTemplate->getRoot()->getNodelist());
    

    I don't get the "TagBlock" element.

    image

    Is there a way to access it?

    opened by tacman 2
  • "where" filter

    I saw in Jekyll (I'm writing a small PHP SSG inspired in it, thanks to this project) and other .liquid implementations that there's a filter called where useful to filter out an array of data. Apparently in a recent version, Jekyll also added it as a filter in a for iteration (but I didn't see it in Shopify's version).

    I didn't see it in php-liquid, so here's my initial take on this filter. Tried it on a single level array, need to check it out for multidimensional cases.

    Note: if available under another name in the project, please disregard. Thanks!

    {% assign developers = team | where: "job","Developer" %}

      /**
       * Where filter
       */
       public function where($array,$field,$value) {
    
            $r = array_filter($array, function($v, $k) use ($field,$value) {
                    if (is_array($v)) {
                        return (isset($v[$field]) && $v[$field] == $value);
                    } else {
                        return true;
                    }
                }, ARRAY_FILTER_USE_BOTH);
    
            return $r;
       }
    
    feature request 
    opened by cdsaenz 0
Releases(1.4.32)
  • 1.4.32(Aug 8, 2022)

    What's Changed

    • Align split filter behaviour with Shopify/Ruby Liquid by @funkjedi in https://github.com/kalimatas/php-liquid/pull/167

    New Contributors

    • @live627 made their first contribution in https://github.com/kalimatas/php-liquid/pull/166

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.31...1.4.32

    Source code(tar.gz)
    Source code(zip)
  • 1.4.31(May 14, 2022)

    What's Changed

    • Fix PHP 8.1 error in preg_split by replacing null with default value by @PNixx in https://github.com/kalimatas/php-liquid/pull/165

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.30...1.4.31

    Source code(tar.gz)
    Source code(zip)
  • 1.4.30(Apr 15, 2022)

    What's Changed

    • Fix PHP 8.1 error in preg_split by replacing null with default value by @peterjaap in https://github.com/kalimatas/php-liquid/pull/163
    • Start testing on PHP 8.1 by @sanmai in https://github.com/kalimatas/php-liquid/pull/164

    New Contributors

    • @peterjaap made their first contribution in https://github.com/kalimatas/php-liquid/pull/163

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.29...1.4.30

    Source code(tar.gz)
    Source code(zip)
  • 1.4.29(Mar 22, 2022)

    What's Changed

    • fix parse exception "Variable was not properly terminated" with line … by @PNixx in https://github.com/kalimatas/php-liquid/pull/162

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.28...1.4.29

    Source code(tar.gz)
    Source code(zip)
  • 1.4.28(Mar 22, 2022)

    What's Changed

    • fix parse exception "Unknown tag endfor" with line break by @PNixx in https://github.com/kalimatas/php-liquid/pull/161

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.27...1.4.28

    Source code(tar.gz)
    Source code(zip)
  • 1.4.27(Mar 1, 2022)

    What's Changed

    • fix parse exception "tag was never closed" with line break by @PNixx in https://github.com/kalimatas/php-liquid/pull/159

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.26...1.4.27

    Source code(tar.gz)
    Source code(zip)
  • 1.4.26(Feb 17, 2022)

    What's Changed

    • Fix truncate, capitalize, upcase, downcase methods for UTF-8 by @PNixx in https://github.com/kalimatas/php-liquid/pull/158

    Full Changelog: https://github.com/kalimatas/php-liquid/compare/1.4.25...1.4.26

    Source code(tar.gz)
    Source code(zip)
  • 1.4.25(Nov 4, 2021)

  • 1.4.24(Sep 17, 2021)

  • 1.4.23(Jun 25, 2021)

  • 1.4.22(Jan 22, 2021)

  • 1.4.21(Jan 7, 2021)

  • 1.4.20(Jan 1, 2021)

  • 1.4.19(Sep 21, 2020)

  • 1.4.18(Mar 18, 2020)

  • 1.4.17(Sep 14, 2019)

  • 1.4.16(Jul 31, 2019)

  • 1.4.15(Jul 11, 2019)

  • 1.4.14(Jun 17, 2019)

    Not passing a date format to the date filter now results in a common and expected LiquidException instead of surprising TypeError under PHP 7.1+. Other filters may also be affected.

    Kudos to @PATROMO for raising the issue and providing a fix.

    This release also the first one to be thoroughly tested under PHP 7.3, although earlier releases should work just as fine.

    Source code(tar.gz)
    Source code(zip)
  • 1.4.13(Mar 22, 2019)

  • 1.4.12(Feb 11, 2019)

    Fixes #114. This issue is particularly important because $_SERVER may contain sensitive data, like AWS access keys, which then can be accessed from user-provided templates.

    To re-enable old unsafe behavior, use:

    Liquid::set('EXPOSE_SERVER', true);
    
    Source code(tar.gz)
    Source code(zip)
  • 1.4.11(Jan 30, 2019)

    #113: Content ticks allow us to track the rendering cost at runtime, abort it or do something else if a template takes too much time to render. (Kudos to @timglabisch!)

    Source code(tar.gz)
    Source code(zip)
  • 1.4.10(Jan 29, 2019)

  • 1.4.9(Dec 29, 2018)

  • 1.4.8(Mar 22, 2018)

  • 1.4.7(Feb 9, 2018)

    • Paginate tag shall now respect request parameters.
    • It is now possible to set a custom query param for the paginate tag.
    • Page number will now never go overboard.

    Major thanks to @edwardoka and @asacarter.

    Source code(tar.gz)
    Source code(zip)
  • 1.4.6(Feb 7, 2018)

    • TagPaginate shall not pollute the global scope, but work in own scope. (Thanks @edwardoka and @asacarter)
    • TagPaginate errors if no collection present instead of vague warning.
    Source code(tar.gz)
    Source code(zip)
  • 1.4.5(Dec 12, 2017)

  • 1.4.4(Nov 3, 2017)

  • 1.4.3(Oct 10, 2017)

    • escape and escape_once filters now escape everything, but arrays
    • New standard filter for explicit string conversion, mainly useful for object with __toString method in conjunction with filters
    Source code(tar.gz)
    Source code(zip)
A redacted PHP port of Underscore.js with additional functions and goodies – Available for Composer and Laravel

Underscore.php The PHP manipulation toolbelt First off : Underscore.php is not a PHP port of Underscore.js (well ok I mean it was at first). It's does

Emma Fabre 1.1k Dec 11, 2022
Port of the Java Content Repository (JCR) to PHP.

PHP Content Repository PHPCR This repository contains interfaces for the PHPCR standard. The JSR-283 specification defines an API for a Content Reposi

PHPCR 436 Dec 30, 2022
A simple, type-safe, zero dependency port of the javascript fetch WebApi for PHP.

A simple, type-safe, zero dependency port of the javascript fetch WebApi for PHP.

Matias Navarro Carter 105 Jan 4, 2023
Back the fun of reading - PHP Port for Arc90′s Readability

PHP Readability Library If you want to use an up-to-date version of this algorithm,check this newer project: https://github.com/andreskrey/readability

明城 517 Nov 18, 2022
An improved version of the PHP port of KuzuhaScript

KuzuhaScriptPHP+ (くずはすくりぷとPHP+) An improved version of the PHP port of KuzuhaScript (くずはすくりぷと). To my knowledge, it works with PHP version 4.1.0 and a

Heyuri 4 Nov 16, 2022
This is a port of the original WireGuard UI bits as implemented by Netgate in pfSense 2.5.0 to a package suitable for rapid iteration and more frequent updating on future releases of pfSense.

This is a port of the original WireGuard*** UI bits as implemented by Netgate in pfSense 2.5.0 to a package suitable for sideloading and more frequent updating on future releases of pfSense. This also includes some improvments such as a proper status page (found under Status / WireGuard Status) and improved assigned interface handling.

R. Christian McDonald 195 Dec 23, 2022
A class for easy connection to the zarinpal port

Payment class with ZarinPal A class to simplify payment operations and confirm payment of ZarrinPal payment gateway service ( به فارسی بخوانید ) Insta

Mohammad Qasemi 7 Jul 15, 2022
uaDetect – A multi-language port of Browserscope's user agent parser

uaDetect is a lightweight for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.

Fadjrir Herlambang 1 Jan 7, 2022
Redaxo 5-Addon zum gruppieren beliebiger Inhaltsmodule (Blöcke) innerhalb eines Spaltenrasters mit selbst definierten Templates.

Gridblock Redaxo 5-Addon zum gruppieren beliebiger Inhaltsmodule (Blöcke) innerhalb eines Spaltenrasters. Die Inhaltsmodule entsprechen dabei den übli

Falko Müller 13 Jun 27, 2022
A simple plugin to override all woocommerce templates

A simple plugin to override all woocommerce templates

null 10 Nov 16, 2021
Rah memcached - Store parts of Textpattern CMS templates in Memcached

rah_memcached Packagist | Issues | Donate A plugin for Textpattern CMS that stores parts of your templates in Memcached, a distributed in-memory key-v

Jukka Svahn 2 Aug 12, 2022
Magento 2 Module to add simple image resizing capabilities in all blocks and .phtml templates

Magento 2 Image Resizer Magento 2 Module to add simple image resizing capabilities in all blocks and .phtml templates Installation $ composer require

Stämpfli AG 88 Apr 18, 2022
PHPStorm Magento 2 File Templates

phpstorm-m2-filetemplates PHPStorm Magento 2 File Templates Useful file templates for working with Magento 2. To install, place the files directly in

Laura Folco 39 Dec 21, 2022
Benchmark of Elefant's template engine against Twig templates

Template Benchmark This is a simple benchmark to test the memory usage and speed of rendering templates using Elefant's Template engine, Twig, and Sma

John de Plume 9 Jun 28, 2016
TYPO3 CMS extension which extends TYPO3 page cache, by tags based on entities used in fluid templates.

Fluid Page Cache for TYPO3 CMS This TYPO3 CMS extension allows you to clear frontend page caches, automatically when a displayed record has been updat

Armin Vieweg 1 Apr 8, 2022
The Current US Version of PHP-Nuke Evolution Xtreme v3.0.1b-beta often known as Nuke-Evolution Xtreme. This is a hardened version of PHP-Nuke and is secure and safe. We are currently porting Xtreme over to PHP 8.0.3

2021 Nightly Builds Repository PHP-Nuke Evolution Xtreme Developers TheGhost - Ernest Allen Buffington (Lead Developer) SeaBeast08 - Sebastian Scott B

Ernest Buffington 7 Aug 28, 2022
A sampling profiler for PHP written in PHP, which reads information about running PHP VM from outside of the process.

Reli Reli is a sampling profiler (or a VM state inspector) written in PHP. It can read information about running PHP script from outside of the proces

null 272 Dec 22, 2022
PHP Meminfo is a PHP extension that gives you insights on the PHP memory content

MEMINFO PHP Meminfo is a PHP extension that gives you insights on the PHP memory content. Its main goal is to help you understand memory leaks: by loo

Benoit Jacquemont 994 Dec 29, 2022
A sampling profiler for PHP written in PHP, which reads information about running PHP VM from outside of the process.

Reli Reli is a sampling profiler (or a VM state inspector) written in PHP. It can read information about running PHP script from outside of the proces

null 258 Sep 15, 2022