https://github.com/keepsuit/php-liquid
PHP implementation of Liquid markup language
https://github.com/keepsuit/php-liquid
Last synced: 11 months ago
JSON representation
PHP implementation of Liquid markup language
- Host: GitHub
- URL: https://github.com/keepsuit/php-liquid
- Owner: keepsuit
- License: mit
- Created: 2023-07-26T09:13:24.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2024-04-04T13:05:36.000Z (about 2 years ago)
- Last Synced: 2024-04-05T14:22:03.305Z (about 2 years ago)
- Language: PHP
- Size: 428 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.md
Awesome Lists containing this project
README
# PHP implementation of Liquid markup language
[](https://packagist.org/packages/keepsuit/liquid)
[](https://github.com/keepsuit/liquid/actions/workflows/run-tests.yml)
[](https://packagist.org/packages/keepsuit/liquid)
This is a PHP porting of the [Shopify Liquid template engine](https://github.com/Shopify/liquid).
If you are using laravel, you can use the [laravel-liquid](https://github.com/keepsuit/laravel-liquid) package.
Liquid is a template engine with interesting advantages:
- It is easy to learn and has a simple syntax.
- It is safe since it does not allow users to run insecure code on your server.
- It is extensible, allowing you to add your own filters and tags.
## Shopify Liquid version compatibility
| PHP Liquid | Shopify Liquid |
|------------:|---------------:|
| v0.9 | v5.8 |
| v0.8 | v5.7 |
| v0.7 | v5.6 |
| v0.1 - v0.6 | v5.5 |
#### Differences from Shopify Liquid
- **Error Modes** are not implemented, the parsing is always strict.
- `include` tag is not implemented because it is deprecated and can be replaced with `render`.
## Installation
You can install the package via composer:
```bash
composer require keepsuit/liquid
```
## Usage
Create a new environment factory instance:
```php
$environment = \Keepsuit\Liquid\EnvironmentFactory::new()
// enable strict variables mode (disabled by default)
->setStrictVariables(true)
// enable strict filters mode (disabled by default)
->setStrictFilters(true)
// rethrow exceptions instead of rendering them (disabled by default)
->setRethrowErrors(true)
// disable lazy parsing (enabled by default)
->setLazyParsing(false)
// replace the default error handler
->setErrorHandler(new \Keepsuit\Liquid\ErrorHandlers\DefaultErrorHandler())
// set filesystem used to load templates
->setFilesystem(new \Keepsuit\Liquid\FileSystems\LocalFileSystem(__DIR__ . '/views'))
// set the resource limits
->setResourceLimits(new \Keepsuit\Liquid\ResourceLimits())
// register a custom extension
->addExtension(new CustomExtension())
// register a custom tag
->registerTag(CustomTag::class)
// register a custom filters provider
->registerFilters(CustomFilters::class)
// build the environment
->build();
```
Then create a new template instance parsing a liquid template:
```php
/** @var \Keepsuit\Liquid\Environment $environment */
// Parse from string
$template = $environment->parseString('Hello {{ name }}!');
// Parse from template (loaded from filesystem)
$template = $environment->parseTemplate('index');
```
And finally render the template:
```php
/** @var \Keepsuit\Liquid\Environment $environment */
/** @var \Keepsuit\Liquid\Template $template */
// Create the render context
$context = $environment->newRenderContext(
// Data available only in the current context
data: [
'name' => 'John',
],
// Data shared with all sub-contexts
staticData: []
)
$view = $template->render($context);
// $view = 'Hello John!';
```
For advanced use cases, you can also stream the rendering output (still experimental):
```php
/** @var \Keepsuit\Liquid\Template $template */
/** @var \Keepsuit\Liquid\Render\RenderContext $context */
$stream = $template->stream($context);
// $stream is a Generator
```
## Drops
Liquid support almost any kind of object but in order to have a better control over the accessible data in the templates,
you can pass your data as `Drop` objects and have a better control over the accessible data.
Drops are standard php objects that extend the `Keepsuit\Liquid\Drop` class.
Public properties and public methods of the class will be accessible in the template as a property.
You can also override the `liquidMethodMissing` method to handle undefined properties.
Liquid provides some attributes to control the behavior of the drops:
- `Hidden`: Hide the method or the property from the template, it cannot be accessed from liquid.
- `Cache`: Cache the result of the method, it will be called only once and the result will be stored in the drop.
```php
use Keepsuit\Liquid\Drop;
class ProductDrop extends Drop {
public function __construct(private Product $product) {}
public function title(): string {
return $this->product->title;
}
public function price(): float {
return round($this->product->price, 2);
}
#[\Keepsuit\Liquid\Attributes\Cache]
public function expensiveOperation(){
// complex operation
}
#[\Keepsuit\Liquid\Attributes\Hidden]
public function buy(){
// Do something
}
}
```
If you implement the `MapsToLiquid` interface in your domain classes,
the liquid renderer will automatically convert your objects to drops.
```php
use Keepsuit\Liquid\Contracts\MapsToLiquid;
class Product implements MapsToLiquid {
public function __construct(public string $title, public float $price) {}
public function toLiquid(): ProductDrop {
return new ProductDrop($this);
}
}
```
## Advanced usage
### Custom tags
To create a custom tag, you need to create a class that extends the `Keepsuit\Liquid\Tag` abstract class (or `Keepsuit\Liquid\TagBlock` if tag has a body).
```php
use Keepsuit\Liquid\Parse\TagParseContext;
use Keepsuit\Liquid\Render\RenderContext;
use Keepsuit\Liquid\Tag;
class CustomTag extends Tag
{
public static function tagName(): string
{
return 'custom';
}
public function render(RenderContext $context): string
{
return '';
}
public function parse(TagParseContext $context): static
{
return $this;
}
}
```
> [!NOTE]
> Take a look at the implementation of default tags to see how to implement `parse` and `render` methods.
Then you need to register the tag in the environment:
```php
// register when building the environment
$environment = \Keepsuit\Liquid\EnvironmentFactory::new()
->registerTag(CustomTag::class)
->build();
// or directly in the environment
$environment->tagRegistry->register(CustomTag::class);
```
### Custom filters
To create a custom filter, you need to create a class that extends the `Keepsuit\Liquid\Filters\FiltersProvider` abstract class.
Each public method of the class will be registered as a filter.
You can "hide" a public method with the `Hidden` attribute, so it will not be registered as filter.
```php
use Keepsuit\Liquid\Filters\FiltersProvider;
class CustomFilters extends FiltersProvider
{
public function customFilter(string $value): string
{
return 'custom '.$value;
}
#[\Keepsuit\Liquid\Attributes\Hidden]
public function notAFilter(string $value): string
{
return 'hidden '.$value;
}
}
```
Then you need to register the filters provider in the environment:
```php
// register when building the environment
$environment = \Keepsuit\Liquid\EnvironmentFactory::new()
->registerFilters(CustomFilters::class)
->build();
// or directly in the environment
$environment->filterRegistry->register(CustomFilters::class);
```
### Extensions
Extensions allow you to add custom tags, filters, and other features to the liquid environment.
To create a custom extension, you need to create a class that extends the `Keepsuit\Liquid\Extensions\Extension` abstract class.
```php
class CustomExtension extends \Keepsuit\Liquid\Extensions\Extension
{
public function getTags() : array{
return [
CustomTag::class,
];
}
public function getFiltersProviders() : array{
return [
CustomFilters::class,
];
}
// custom registers passed to render context
public function getRegisters() : array {
return [
'custom' => fn() => 'custom value',
];
}
}
```
Then you need to register the extension in the environment:
```php
// register when building the environment
$environment = \Keepsuit\Liquid\EnvironmentFactory::new()
->addExtension(new CustomExtension())
->build();
// or directly in the environment
$environment->addExtension(new CustomExtension());
```
## Custom tags and filters
By default, only the standard liquid tags and filters are available.
But this package provides some custom tags and filters that you can use.
### Tags
- `DynamicRender`: This tag replace the default `Render` tag and allows to render dynamic templates (eg. read template name from a variable).
### Filters
- `TernaryFilter`
- `ternary`: adds a ternary operator.
```liquid
{{ condition | ternary: true_value, false_value }}
# Example
{{ true | ternary: 'yes', 'no' }} # yes
{{ false | ternary: 'yes', 'no' }} # no
```
## Testing
```bash
composer test
```
## Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
## Credits
- [Shopify](https://github.com/Shopify/liquid)
- [Fabio Capucci](https://github.com/cappuc)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.