PHPackages                             8ctopus/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. 8ctopus/lex

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

8ctopus/lex
===========

A lightweight template parser.

1.0.0(3y ago)055MITPHPPHP &gt;=8.0

Since Jan 26Pushed 1y agoCompare

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

READMEChangelogDependencies (4)Versions (3)Used By (0)

Lex
===

[](#lex)

[![packagist](https://camo.githubusercontent.com/fb4ad91d6267180ebed7a0ee6bfabbeebf14456f628a8aa7c57b01b6f4326670/687474703a2f2f706f7365722e707567782e6f72672f3863746f7075732f6c65782f76)](https://packagist.org/packages/8ctopus/lex)[![downloads](https://camo.githubusercontent.com/dc41f1efe9aa34909d9e600d1dc2a6d3e4d6dd02f62086be2edfae94d33fe36c/687474703a2f2f706f7365722e707567782e6f72672f3863746f7075732f6c65782f646f776e6c6f616473)](https://packagist.org/packages/8ctopus/lex)[![min php version](https://camo.githubusercontent.com/e6ed8c24d8e4d9e811caef04733e8a217a3293720d837e1cc8144cd99f4a809c/687474703a2f2f706f7365722e707567782e6f72672f3863746f7075732f6c65782f726571756972652f706870)](https://packagist.org/packages/8ctopus/lex)[![license](https://camo.githubusercontent.com/71a77323e6350158ddbfbc2e03a339eda31b16d72222662ea9d3ea32a0bb5016/687474703a2f2f706f7365722e707567782e6f72672f3863746f7075732f6c65782f6c6963656e7365)](https://packagist.org/packages/8ctopus/lex)[![tests](https://github.com/8ctopus/lex/actions/workflows/tests.yml/badge.svg)](https://github.com/8ctopus/lex/actions/workflows/tests.yml)[![code coverage badge](https://raw.githubusercontent.com/8ctopus/lex/image-data/coverage.svg)](https://raw.githubusercontent.com/8ctopus/lex/image-data/coverage.svg)[![lines of code](https://raw.githubusercontent.com/8ctopus/lex/image-data/lines.svg)](https://raw.githubusercontent.com/8ctopus/lex/image-data/lines.svg)

Lex is a lightweight template parser.

This project is a detached fork from .

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

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

27

—

LowBetter than 49% of packages

Maintenance30

Infrequent updates — may be unmaintained

Popularity9

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 53.4% 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

Unknown

Total

1

Last Release

1188d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4dafd5f7ef8134a5c9b231686c5da3d6416db09139b45aac0b26952178dffb8a?d=identicon)[8ctopus](/maintainers/8ctopus)

---

Top Contributors

[![dhrrgn](https://avatars.githubusercontent.com/u/149921?v=4)](https://github.com/dhrrgn "dhrrgn (103 commits)")[![8ctopus](https://avatars.githubusercontent.com/u/13252042?v=4)](https://github.com/8ctopus "8ctopus (51 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

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[phpoffice/phpword

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

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

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

420137.3M46](/packages/rize-uri-template)[pyrocms/lex

A lightweight template parser.

11128.3k2](/packages/pyrocms-lex)

PHPackages © 2026

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