PHPackages                             pyrocms/lex - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. pyrocms/lex

ActiveLibrary[Templating &amp; Views](/categories/templating)

pyrocms/lex
===========

A lightweight template parser.

2.3.2(12y ago)11128.3k↓30%30[1 issues](https://github.com/pyrocms/lex/issues)2MITPHPPHP &gt;=5.3.0

Since Aug 9Pushed 11y ago13 watchersCompare

[ Source](https://github.com/pyrocms/lex)[ Packagist](https://packagist.org/packages/pyrocms/lex)[ RSS](/packages/pyrocms-lex/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (14)Used By (2)

Lex
===

[](#lex)

[![Build Status](https://camo.githubusercontent.com/6624f895f9b07446e66d31dd3930924329c88795b16f086ff51ee72e95d25452/68747470733a2f2f7365637572652e7472617669732d63692e6f72672f7079726f636d732f6c65782e706e673f6272616e63683d6d6173746572)](http://travis-ci.org/pyrocms/lex)

Lex is a lightweight template parser.

*Lex is released under the MIT License and is Copyrighted 2011 - 2014 PyroCMS Team.*

Change Log
==========

[](#change-log)

2.3.2
-----

[](#232)

- Convert objects with `->toArray()` at the beginning of the `parser()` method only.
- As much as we want to say goodbye to PHP 5.3, we brought it back for now.

2.3.1
-----

[](#231)

- Added an ArrayableInterface that automatically converts objects to arrays. This allows an opportunity to define how an object will be converted to an array by adding a `->toArray()` method.
- Looped data is now checked if it is an array or if it implements \\IteratorAggregate. This prevents "invalid arguments in foreach" errors while allowing arrays and Collection objects to be iterated.
- Dropped support for PHP 5.3

2.3.0
-----

[](#230)

- Added support for self-closing Callback Tags (e.g. `{{ foo.bar /}}` instead of `{{ foo.bar }}{{ /foo.bar }}`).
- Rolled back the change to make Callback Tags less greedy. Callback Tags are now greedy again. If you want to use both Single and Block tags in the same template, you must close the Single tags.

2.2.3
-----

[](#223)

- Fixes issue which caused all callbacks to be processed as single tags, even if they were a block.

2.2.2
-----

[](#222)

- Fixed #7 - Conditionals inside looped data tags now work as expected.

2.2.1
-----

[](#221)

- Changed injectNoparse to be static.

2.2.0
-----

[](#220)

- Fixed a test which was PHP 5.4 only.
- Added PHPUnit as a composer dev requirement.
- Added a Lex\\ParsingException class which is thrown when a parsing exception occurs.

2.1.1
-----

[](#211)

- Fixed an issue where strings returned by callbacks inside a comparison conditional were being processed incorrectly, causing the conditional to always fail.

2.1.0
-----

[](#210)

- Undefined variables in a conditional now evaluate to NULL, so `{{ if foo }}` now works properly.
- Added the `exists` keyword.
- Added the `not` keyword.

2.0.3
-----

[](#203)

- Fixes composer autoloading.
- Moved classes into lib folder.

2.0.2
-----

[](#202)

- Fixed a bug introduced in 2.0.1 where NULL variables were not being displayed.

2.0.1
-----

[](#201)

- Fixed a bug where variables with a "falsey" (e.g. 0, "0", -1, etc.) value were not displayed.

2.0.0
-----

[](#200)

- All code now follows PSR-0, 1 and 2.
- Lex\_Parser has been moved to the `Lex` namespace and renamed to `Parser`.
- Lex\_Autoloader has been removed. It is now PSR-0 compliant.
- Added the support for `{{ unless }}` and `{{ elseunless }}`.

Basic Usage
===========

[](#basic-usage)

Using Lex
---------

[](#using-lex)

Lex is a Composer package named `pyrocms/lex`. To use it, simply add it to the `require` section of you `composer.json` file.

```
{
    "require": {
        "pyrocms/lex": "2.2.*"
    }
}

```

After adding Lex to your `composer.json` file, simply use the class as normal.

```
$parser = new Lex\Parser();

```

Using Lex
---------

[](#using-lex-1)

Basic parsing of a file:

```
$parser = new Lex\Parser();
$template = $parser->parse(file_get_contents('template.lex'), $data);

```

You can also set the Scope Glue (see "Scope Glue" under Syntax below):

```
$parser = new Lex\Parser();
$parser->scopeGlue(':');
$template = $parser->parse(file_get_contents('template.lex'), $data);

```

To allow noparse extractions to accumulate so they don't get parsed by a later call to the parser set cumulativeNoparse to true:

```
$parser = new Lex\Parser();
$parser->cumulativeNoparse(true);
$template = $parser->parse(file_get_contents('template.lex'), $data);
// Second parse on the same text somewhere else in your app
$template = $parser->parse($template, $data);
// Now that all parsing is done we inject the contents between the {{ noparse }} tags back into the template text
Lex\Parser::injectNoparse($template);

```

If you only want to parse a data array and not worry about callback tags or comments, you can do use the `parseVariables()` method:

```
$parser = new Lex\Parser();
$template = $parser->parseVariables(file_get_contents('template.lex'), $data);

```

### PHP in Templates

[](#php-in-templates)

By default PHP is encoded, and not executed. This is for security reasons. However, you may at times want to enable it. To do that simply send `true` as the fourth parameter to your `parse()` call.

```
$parser = new Lex\Parser();
$template = $parser->parse(file_get_contents('template.lex'), $data, $callback, true);

```

Syntax
======

[](#syntax)

General
-------

[](#general)

All Lex code is delimeted by double curly braces (`{{ }}`). These delimeters were chosen to reduce the chance of conflicts with JavaScript and CSS.

Here is an example of some Lex template code:

```
Hello, {{name}}

```

Scope Glue
----------

[](#scope-glue)

Scope Glue is/are the character(s) used by Lex to trigger a scope change. A scope change is what happens when, for instance, you are accessing a nested variable inside and array/object, or when scoping a custom callback tag.

By default a dot (`.`) is used as the Scope Glue, although you can select any character(s).

`Setting Scope Glue`

```
$parser->scopeGlue(':');

```

Whitespace
----------

[](#whitespace)

Whitespace before or after the delimeters is allowed, however, in certain cases, whitespace within the tag is prohibited (explained in the following sections).

**Some valid examples:**

```
{{ name }}
{{name }}
{{ name}}
{{  name  }}
{{
  name
}}

```

**Some invalid examples:**

```
{{ na me }}
{ {name} }

```

Comments
--------

[](#comments)

You can add comments to your templates by wrapping the text in `{{# #}}`.

**Example**

```
{{# This will not be parsed or shown in the resulting HTML #}}

{{#
    They can be multi-line too.
#}}

```

Prevent Parsing
---------------

[](#prevent-parsing)

You can prevent the parser from parsing blocks of code by wrapping it in `{{ noparse }}{{ /noparse }}` tags.

**Example**

```
{{ noparse }}
    Hello, {{ name }}!
{{ /noparse }}

```

Variable Tags
-------------

[](#variable-tags)

When dealing with variables, you can: access single variables, access deeply nested variables inside arrays/objects, and loop over an array. You can even loop over nested arrays.

### Simple Variable Tags

[](#simple-variable-tags)

For our basic examples, lets assume you have the following array of variables (sent to the parser):

```
array(
    'title'     => 'Lex is Awesome!',
    'name'      => 'World',
    'real_name' => array(
        'first' => 'Lex',
        'last'  => 'Luther',
    )
)

```

**Basic Example:**

```
{{# Parsed: Hello, World! #}}
Hello, {{ name }}!

{{# Parsed: Lex is Awesome! #}}
{{ title }}

{{# Parsed: My real name is Lex Luther! #}}
My real name is {{ real_name.first }} {{ real_name.last }}

```

The `{{ real_name.first }}` and `{{ real_name.last }}` tags check if `real_name` exists, then check if `first` and `last` respectively exist inside the `real_name` array/object then returns it.

### Looped Variable Tags

[](#looped-variable-tags)

Looped Variable tags are just like Simple Variable tags, except they correspond to an array of arrays/objects, which is looped over.

A Looped Variable tag is a closed tag which wraps the looped content. The closing tag must match the opening tag exactly, except it must be prefixed with a forward slash (`/`). There can be **no** whitespace between the forward slash and the tag name (whitespace before the forward slash is allowed).

**Valid Example:**

```
{{ projects }} Some Content Here {{ /projects }}

```

**Invalid Example:**

```
{{ projects }} Some Content Here {{/ projects }}

```

The looped content is what is contained between the opening and closing tags. This content is looped through and output for every item in the looped array.

When in a Looped Tag you have access to any sub-variables for the current element in the loop.

In the following example, let's assume you have the following array/object of variables:

```
array(
    'title'     => 'Current Projects',
    'projects'  => array(
        array(
            'name' => 'Acme Site',
            'assignees' => array(
                array('name' => 'Dan'),
                array('name' => 'Phil'),
            ),
        ),
        array(
            'name' => 'Lex',
            'contributors' => array(
                array('name' => 'Dan'),
                array('name' => 'Ziggy'),
				array('name' => 'Jerel')
            ),
        ),
    ),
)

```

In the template, we will want to display the title, followed by a list of projects and their assignees.

```
{{ title }}
{{ projects }}
    {{ name }}
    Assignees

    {{ assignees }}
        {{ name }}
    {{ /assignees }}

{{ /projects }}

```

As you can see inside each project element we have access to that project's assignees. You can also see that you can loop over sub-values, exactly like you can any other array.

Conditionals
------------

[](#conditionals)

Conditionals in Lex are simple and easy to use. It allows for the standard `if`, `elseif`, and `else` but it also adds `unless` and `elseunless`.

The `unless` and `elseunless` are the EXACT same as using `{{ if ! (expression) }}` and `{{ elseif ! (expression) }}` respectively. They are added as a nicer, more understandable syntax.

All `if` blocks must be closed with the `{{ endif }}` tag.

Variables inside of if Conditionals, do not, and should not, use the Tag delimeters (it will cause wierd issues with your output).

A Conditional can contain any Comparison Operators you would do in PHP (`==`, `!=`, `===`, `!==`, `>`, `parse(file_get_contents('template.lex'), $data, 'my_callback');

```

The callback must accept the 3 parameters below (in this order):

```
$name - The name of the callback tag (it would be "template.partial" in the above examples)
$attributes - An associative array of the attributes set
$content - If it the tag is a block tag, it will be the content contained, else a blank string

```

The callback must also return a string, which will replace the tag in the content.

**Example**

```
function my_callback($name, $attributes, $content)
{
    // Do something useful
    return $result;
}

```

### Closing Callback Tags

[](#closing-callback-tags)

If a Callback Tag can be used in single or block form, then when using it in it's singular form, it must be closed (just like HTML).

**Example**

```
{{ foo.bar.baz }}{{ /foo.bar.baz }}

{{ foo.bar.baz }}
    Content
{{ /foo.bar.baz }}

```

#### Self Closing Callback Tags

[](#self-closing-callback-tags)

You can shorten the above by using self-closing tags, just like in HTML. You simply put a `/` at the end of the tag (there MUST be NO space between the `/` and the `}}`).

**Example**

```
{{ foo.bar.baz /}}

{{ foo.bar.baz }}
    Content
{{ /foo.bar.baz }}

```

Recursive Callback Blocks
-------------------------

[](#recursive-callback-blocks)

The recursive callback tag allows you to loop through a child's element with the same output as the main block. It is triggered by using the ***recursive*** keyword along with the array key name. The two words must be surrounded by asterisks as shown in the example below.

**Example**

```
function my_callback($name, $attributes, $content)
{
	$data = array(
			'url' 		=> 'url_1',
			'title' 	=> 'First Title',
			'children'	=> array(
				array(
					'url' 		=> 'url_2',
					'title'		=> 'Second Title',
					'children' 	=> array(
						array(
							'url' 	=> 'url_3',
							'title'	=> 'Third Title'
						)
					)
				),
				array(
					'url'		=> 'url_4',
					'title'		=> 'Fourth Title',
					'children'	=> array(
						array(
							'url' 	=> 'url_5',
							'title'	=> 'Fifth Title'
						)
					)
				)
			)
	);

	$parser = new Lex\Parser();
	return $parser->parse($content, $data);
}

```

In the template set it up as shown below. If `children` is not empty Lex will parse the contents between the `{{ navigation }}` tags again for each of `children`'s arrays. The resulting text will then be inserted in place of `{{ *recursive children* }}`. This can be done many levels deep.

```

	{{ navigation }}
		{{ title }}
			{{ if children }}

					{{ *recursive children* }}

			{{ endif }}

	{{ /navigation }}

```

**Result**

```

	First Title

			Second Title

					Third Title

			Fourth Title

					Fifth Title

```

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity44

Moderate usage in the ecosystem

Community28

Small or concentrated contributor base

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 72.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~49 days

Recently: every ~98 days

Total

12

Last Release

4484d ago

PHP version history (2 changes)2.0.0PHP &gt;=5.3.0

2.3.1PHP &gt;=5.4.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/b79ef64367fa25261d8353dbd941670f2b5f2617de247e4b98c089a025ca543a?d=identicon)[philsturgeon](/maintainers/philsturgeon)

![](https://www.gravatar.com/avatar/941d9ad3ed290089ca20acec9ac6736bffcc123262196d80046bf82884e9c3f8?d=identicon)[PyroCMS](/maintainers/PyroCMS)

---

Top Contributors

[![dhrrgn](https://avatars.githubusercontent.com/u/149921?v=4)](https://github.com/dhrrgn "dhrrgn (103 commits)")[![ziggys](https://avatars.githubusercontent.com/u/617609?v=4)](https://github.com/ziggys "ziggys (15 commits)")[![jerel](https://avatars.githubusercontent.com/u/322706?v=4)](https://github.com/jerel "jerel (10 commits)")[![brignoni](https://avatars.githubusercontent.com/u/558767?v=4)](https://github.com/brignoni "brignoni (6 commits)")[![frankdejonge](https://avatars.githubusercontent.com/u/534693?v=4)](https://github.com/frankdejonge "frankdejonge (4 commits)")[![chadwithuhc](https://avatars.githubusercontent.com/u/259196?v=4)](https://github.com/chadwithuhc "chadwithuhc (2 commits)")[![Nyholm](https://avatars.githubusercontent.com/u/1275206?v=4)](https://github.com/Nyholm "Nyholm (1 commits)")[![philsturgeon](https://avatars.githubusercontent.com/u/67381?v=4)](https://github.com/philsturgeon "philsturgeon (1 commits)")

---

Tags

parsertemplate

### Embed Badge

![Health badge](/badges/pyrocms-lex/health.svg)

```
[![Health](https://phpackages.com/badges/pyrocms-lex/health.svg)](https://phpackages.com/packages/pyrocms-lex)
```

###  Alternatives

[phpoffice/phpword

PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)

7.6k34.7M186](/packages/phpoffice-phpword)[rize/uri-template

PHP URI Template (RFC 6570) supports both expansion &amp; extraction

420137.3M46](/packages/rize-uri-template)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
