Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/.github export-ignore
/build export-ignore
/docs export-ignore
/releases export-ignore
/scripts export-ignore
/tests export-ignore
/vendor export-ignore

/.gitignore export-ignore
/.phpunit.result.cache export-ignore
/composer.lock export-ignore
/phpstan-baseline.neon export-ignore
/phpstan.neon export-ignore
/phpunit.xml export-ignore
3 changes: 1 addition & 2 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name: PHPStan

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:

concurrency:
group: phpstan-${{ github.ref }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
from pathlib import Path

version = os.environ["RELEASE_VERSION"]
notes = Path("docs/ChangeLog/releases") / f"v{version}.md"
notes = Path("releases") / f"v{version}.md"

if not notes.is_file():
raise SystemExit(
Expand Down Expand Up @@ -140,7 +140,7 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.meta.outputs.tag }}
name: Div ${{ steps.meta.outputs.version }}
name: Div PHP Template Engine ${{ steps.meta.outputs.version }}
body_path: ${{ steps.notes.outputs.path }}
files: |
build/div-${{ steps.meta.outputs.version }}.zip
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name: Tests

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
workflow_dispatch:

concurrency:
group: tests-${{ github.ref }}
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,5 @@ For complete guides, usage examples, and advanced topics, please visit the [proj

If you find something missing or have improvements, feel free to contribute directly to the Wiki!

---

Powered by [Divengine Software Solutions](https://divengine.com)

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"code generator"
],
"homepage": "https://divengine.com",
"version": "6.1.2",
"version": "6.1.3",
"authors": [
{
"name": "Rafa Rodriguez",
Expand Down
32 changes: 32 additions & 0 deletions docs/01 Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 1. Introduction

Div is a template engine that runs in PHP.

At its core, Div takes a template and a data model and produces text:

```php
echo new div($template, $data);
```

That is the fundamental contract. The output is always text.

Div is also used as a code generator and a data transformation tool because templates are not limited to HTML. A template can generate source code, configuration files, structured data, or even other templates. What Div generates can be reused as input for subsequent executions of the engine.

Div is the cornerstone of Divengine Software Solutions and has been developed continuously since 2011.

The template language is designed to be compact (minimal syntax for common operations), flexible (dialects allow alternative syntaxes), and descriptive (templates read as self-explanatory documents). Div assumes a clear division of concerns:

- The model specifies what data and rules are available.
- The template specifies the expected output structure.
- The engine provides the execution mechanism.

Capabilities span both template authoring and system integration:

- Variable replacement, formatting, modifiers, substring operations, and object property access within the provided model.
- Lists, iterations, conditional blocks, and other repetitive or branching constructs.
- Includes, inheritance, locations, and recursive processing until convergence.
- Formulas, macros, aggregate functions, and output cleanup (including HTML-to-text conversion).
- Configuration of defaults, globals, allowed functions, and ignored variables.
- Custom sub-parsers, hooks, logging, and use of a div instance as a string.

When applied consistently, this approach reduces repetitive work, enables reuse of models, supports multi-target outputs, and improves collaboration across stakeholders.
13 changes: 13 additions & 0 deletions docs/01.01 Scope and purpose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 1.1 Scope and purpose

Div is designed to support workflows based on templates and models, reduce repetition, and enable consistent generation of text-based outputs.

Because the output of the engine is plain text, it can be reused freely:

- as another template,
- as a data model (for example, JSON),
- or as part of a composed result.

This makes it possible to chain executions, build generation pipelines, transform data through templates, and compose multiple outputs into a final result. Code generation, data transformation, and multi-stage compilation all emerge naturally from this model.

In server-side rendering for websites, Div can also be used to maintain a clear separation between logic and presentation by keeping templates focused on structure and models focused on data.
19 changes: 19 additions & 0 deletions docs/01.02 Engine behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 1.2 Engine behavior

Div does not process templates in a single top-down pass.

Instead, the engine repeatedly parses and transforms the entire template until no further changes can be made. Each cycle applies the parsing rules (includes, conditions, loops, replacements, modifiers, and related blocks) to the current state of the template.

The process stops when a stable result is reached (a fixed point) or when a safety limit is met.

Pseudo-code (conceptual):

```text
template = original
do:
previous = template
template = parse_pass(template, data)
while template != previous and cycles < MAX
```

This iterative behavior allows templates to generate intermediate structures that are evaluated in later cycles. As a result, Div supports meta-templates and templates that generate other templates without relying on recursive calls or hidden execution state.
13 changes: 13 additions & 0 deletions docs/01.03 Dialect system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 1.3 Dialect system

Div supports multiple template dialects.

A dialect is defined externally as a JSON file. It describes how an alternative template syntax maps to Div’s canonical internal syntax. Dialects do not change the engine or its behavior; they only affect how template syntax is interpreted.

Templates can reference which dialect they are written in using a dedicated tag. This allows different templates, or included fragments, to use different dialects within the same execution.

Dialect translation occurs before parsing, so the internal parser remains consistent regardless of the syntax used in source templates. This makes it possible to:

- avoid syntax collisions with the language or format being generated,
- preserve validity constraints (such as XML-valid templates),
- and work with multiple syntactic conventions in a single generation pipeline.
9 changes: 9 additions & 0 deletions docs/01.04 Core operations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 1.4 Core operations

Div exposes three fundamental operations:

- Compile: merge a template with a model and produce text output.
- Transform: generate a new model by reusing compilation.
- Compose: combine the results of multiple executions into a single deliverable.

These operations can be freely combined to build complex generation and transformation workflows.
5 changes: 5 additions & 0 deletions docs/01.05 Install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 1.5 Install

```bash
composer require divengine/div
```
5 changes: 5 additions & 0 deletions docs/01.06 Upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 1.6 Upgrade

```bash
composer upgrade
```
10 changes: 10 additions & 0 deletions docs/01.07 Related topics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 1.7 Related topics

[[01.13 The div class]]
[[01.14 The best practices]]
[[02 Template Features]]
[[03 PHP Features]]
[[04 Mechanisms]]
[[05 Appendixes]]

See also [Release notes](../releases/README.md).
7 changes: 7 additions & 0 deletions docs/01.09 Goals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# 1.9 Goals

- Maintain a single, cohesive class and file for the engine core.
- Provide a minimal and expressive template syntax.
- Avoid mandatory caching by design.
- Improve parsing algorithms over time.
- Encourage reuse of mechanisms and domain knowledge.
11 changes: 11 additions & 0 deletions docs/01.10 Reasons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 1.10 Reasons

Div was developed to reuse existing developer knowledge rather than introduce a new, complex template language. The goal is to reduce the learning curve while preserving expressive power.

Features are added only when they are necessary and when they cannot be implemented through existing mechanisms. When a new mechanism is required, the intent is to document it with a clear, reproducible example.

Performance tests indicated that direct string replacement is faster than invoking PHP includes in many scenarios. Although string replacement can use more memory, the tradeoff is acceptable for typical template sizes.

Div also avoids a mandatory caching subsystem. Given its deterministic parsing behavior, learning or memoization strategies can be applied externally when needed.

Finally, the engine is implemented as a single class in a single file to simplify integration into diverse environments.
61 changes: 61 additions & 0 deletions docs/01.13 The div class.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 1.13 The div class

All engine features are provided through the div class. If your project already defines a class named div, you can rename the Div class or use a namespace alias.

## 4.1 Setup and namespace

```php
<?php

include "div.php";

// Or using Composer
include "path/to/vendor/autoload.php";

// div class is inside the "divengine" namespace
use divengine\div;
```

## 4.2 Usage variants

1. Inline template

```php
echo new div('Hello {$name}', [
'name' => 'Peter'
]);
```

2. Instantiate first, render later

```php
$t = new div('Hello {$name}', ['name' => 'Peter']);

echo $t; /* or $t->show(); */
```

3. Template from external file

```php
/* The file index.tpl contains the template code */

echo new div('index.tpl', ['name' => 'Peter']);
```

4. Data provided as JSON string

```php
echo new div('Hello {$name}', '{name: "Peter"}');
```

5. Data loaded from a JSON file

```php
/* The file index.json contains the data as JSON */

echo new div('index.tpl', 'index.json');
```

Related topics:

[[02.45 Ignore specific variables (the third parameter of constructor)]]
13 changes: 13 additions & 0 deletions docs/01.14 The best practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 1.14 The best practices

## 5.1 Keep business logic out of templates

Templates should not compute domain totals or infer missing data. For example, invoice totals should be computed in PHP and passed to the template. The template language is intended for presentation, not for deriving business rules.

## 5.2 Balance template and data responsibilities

Avoid passing excessive data that the template will never use, and avoid placing excessive logic in templates that the engine will discard. Keep the data model and the template in balance so each does only what it is designed for.

## 5.3 Prefer small, reusable templates

Split templates into focused components that serve a single purpose. Do not place unrelated layouts in one file with large conditional blocks. If multiple layouts are needed, use includes or pre-processed templates to compose them intentionally.
49 changes: 49 additions & 0 deletions docs/02 Template Features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 2. Template Features

This chapter enumerates the core language features and links to their detailed specifications.

- [[02.01 Understanding the syntax]]
- [[02.02 Variables (information, content...)]]
- [[02.03 Simple replacements]]
- [[02.04 Special replacements]]
- [[02.05 Variable's modifiers]]
- [[02.06 Multiple variable's modifiers]]
- [[02.07 String's dissection]]
- [[02.08 Data formats]]
- [[02.09 Formulas]]
- [[02.10 Lists (loops)]]
- [[02.11 Dynamic vars inside a loop]]
- [[02.12 Iterations]]
- [[02.13 Conditional parts]]
- [[02.14 Conditions]]
- [[02.15 Default replacements]]
- [[02.16 Default replacement for a variable]]
- [[02.17 Multi replacements]]
- [[02.18 Capsules]]
- [[02.19 Locations]]
- [[02.20 Friendly tags]]
- [[02.21 Comments]]
- [[02.22 Ignored parts (escaping Div parsing)]]
- [[02.23 Strip or clean the resulting code]]
- [[02.24 HTML to plain text]]
- [[02.25 Global vars]]
- [[02.26 Aggregate functions]]
- [[02.27 Macros]]
- [[02.28 Sub-parsers]]
- [[02.29 Pre-defined sub-parsers]]
- [[02.30 Sub-parser's events]]
- [[02.31 System vars]]
- [[02.32 Template's variables]]
- [[02.33 Template's properties]]
- [[02.34 Template's documentation]]
- [[02.35 Including another templates]]
- [[02.36 Including pre-processed templates]]
- [[02.37 Dialects]]
- [[02.38 Multiple dialects]]
- [[02.39 Dialect translator]]
- [[02.40 Custom modifiers]]
- [[02.41 Object Oriented Programming]]
- [[02.42 Content like an object (intelligent data)]]
- [[02.43 Hooks]]
- [[02.44 The __toString magic method]]
- [[02.45 Ignore specific variables (the third parameter of constructor)]]
47 changes: 47 additions & 0 deletions docs/02.01 Understanding the syntax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 2.1 Understanding the syntax

Div recognizes a small set of block structures. The classification is based on how opening and closing tags are formed and whether whitespace is significant.

## 6.1.1 Rigid blocks

|Prefix|Rigid syntax|Suffix|
|---|---|---|
|PREFIX|RIGID SYNTAX|SUFFIX|

Rigid blocks treat every character as significant. Spaces, tabs, and newlines are part of the syntax and cannot be inserted for formatting. For example, in `{$text }` the variable name includes the trailing space, so the engine looks for `text ` rather than `text`.

Typical rigid blocks: [[02.03 Simple replacements]], [[02.35 Including another templates]].

## 6.1.2 Simple blocks

|Begin|Flexible syntax|End|
|---|---|---|
|BEGIN|FLEXIBLE SYNTAX|END|

Simple blocks allow extra whitespace for readability. The opening and closing tags are required, but the content inside the tags is parsed with flexible spacing rules.

Typical simple blocks: [[02.22 Ignored parts (escaping Div parsing)]], [[02.21 Comments]], [[02.23 Strip or clean the resulting code]].

## 6.1.3 No-keyword blocks

|Begin prefix|Flexible syntax|Begin suffix|
|---|---|---|
|BEGIN_PREFIX|FLEXIBLE SYNTAX|BEGIN_SUFFIX|
|ANY CODE + SPECIAL TAGS|||
|END|||

No-keyword blocks encode the opening tag using a prefix and suffix, while the closing tag does not repeat the keyword. This form is used when the closing tag is unambiguous.

Typical no-keyword blocks: [[02.14 Conditions]], [[02.12 Iterations]].

## 6.1.4 Keyword blocks

|Begin prefix|Keyword|Begin suffix|
|---|---|---|
|BEGIN_PREFIX|KEYWORD|BEGIN_SUFFIX|
|ANY CODE + SPECIAL TAGS|||
|END_PREFIX|KEYWORD|END_SUFFIX|

Keyword blocks repeat the keyword in both the opening and closing tags. This form is used when the block is anchored to a specific variable or identifier.

Typical keyword blocks: [[02.13 Conditional parts]], [[02.10 Lists (loops)]].
Loading