11# PHP Handlebars
22
3- A blazing fast, spec-compliant PHP implementation of [ Handlebars] ( https://handlebarsjs.com/ ) .
3+ A blazing fast, spec-compliant PHP implementation of [ Handlebars] ( https://handlebarsjs.com ) .
44
5- Originally based on [ LightnCandy] ( https://github.com/zordius/lightncandy ) , but rewritten to focus on
6- more robust Handlebars.js compatibility without the need for excessive feature flags.
5+ Originally based on [ LightnCandy] ( https://github.com/zordius/lightncandy ) , but rewritten to enable
6+ full Handlebars.js compatibility without excessive feature flags or performance tradeoffs.
7+
8+ PHP Handlebars compiles and executes complex templates up to 40% faster than LightnCandy:
9+
10+ | Library | Compile time | Runtime | Total time | Peak memory usage |
11+ | --------------------| --------------| ---------| ------------| -------------------|
12+ | LightnCandy 1.2.6 | 5.2 ms | 2.8 ms | 8.0 ms | 5.3 MB |
13+ | PHP Handlebars 1.0 | 3.5 ms | 1.6 ms | 5.1 ms | 3.6 MB |
14+
15+ _ Tested on PHP 8.5 with the JIT enabled. See the ` benchmark ` branch to run the same test._
716
817## Features
918
10- * Compile templates to pure PHP code.
19+ * Supports all Handlebars syntax and language features, including expressions, subexpressions, helpers,
20+ partials, hooks, and ` @data ` variables.
1121* Templates are parsed using [ PHP Handlebars Parser] ( https://github.com/devtheorem/php-handlebars-parser ) ,
12- which implements the same lexical analysis and grammar specification as Handlebars.js.
13- * Tested against the [ Handlebars.js spec] ( https://github.com/jbboehr/handlebars-spec ) .
22+ which implements the same lexical analysis and AST grammar specification as Handlebars.js.
23+ * Tested against the full [ Handlebars.js spec] ( https://github.com/jbboehr/handlebars-spec ) .
1424
1525## Installation
1626```
@@ -55,7 +65,7 @@ $template = Handlebars::compile('Hi {{first}} {{last}}!', new Options(
5565 strict: true,
5666));
5767
58- echo $template(['first' => 'John']); // Error: Runtime: last does not exist
68+ echo $template(['first' => 'John']); // Error: " last" not defined
5969```
6070
6171### Available Options
@@ -75,15 +85,26 @@ echo $template(['first' => 'John']); // Error: Runtime: last does not exist
7585 When set, blocks and partials that are on their own line will not remove the whitespace on that line.
7686* ` explicitPartialContext ` : Disables implicit context for partials.
7787 When enabled, partials that are not passed a context value will execute against an empty object.
78- * ` partials ` : Provide a ` name => value ` array of custom partial templates .
88+ * ` partials ` : Provide a ` name => value ` array of custom partial template strings .
7989* ` partialResolver ` : A closure which will be called for any partial not in the ` partials ` array to return a template for it.
8090
91+ ## Runtime Options
92+
93+ ` Handlebars::compile ` returns a closure which can be invoked as ` $template($context, $options) ` .
94+ The ` $options ` parameter takes an array of runtime options, accepting the following keys:
95+
96+ * ` data ` : An array to define custom ` @variable ` private variables.
97+ * ` helpers ` : An ` array<string, \Closure> ` containing custom helpers to add to the built-in helpers.
98+ * ` partials ` : An ` array<string, \Closure> ` containing partial functions precompiled with ` Handlebars::compile ` .
99+ This is useful if multiple templates sharing the same partials need to be compiled and rendered, and you don't want
100+ to recompile the same partials over and over for each template.
101+
81102## Custom Helpers
82103
83104Helper functions will be passed any arguments provided to the helper in the template.
84105If needed, a final ` $options ` parameter can be included which will be passed a ` HelperOptions ` instance.
85- This object contains properties for accessing ` hash ` arguments, ` data ` , and the current ` scope ` , as well as
86- ` fn() ` and ` inverse() ` methods to render the block and else contents, respectively.
106+ This object contains properties for accessing ` hash ` arguments, ` data ` , and the current ` scope ` , ` name ` ,
107+ as well as ` fn() ` and ` inverse() ` methods to render the block and else contents, respectively.
87108
88109For example, a custom ` #equals ` helper with JS equality semantics could be implemented as follows:
89110
@@ -94,17 +115,13 @@ $template = Handlebars::compile('{{#equals my_var false}}Equal to false{{else}}N
94115$options = [
95116 'helpers' => [
96117 'equals' => function (mixed $a, mixed $b, HelperOptions $options) {
97- $jsEquals = function (mixed $a, mixed $b): bool {
98- if ($a === null || $b === null || is_string($a) && is_string($b)) {
99- // In JS, null is not equal to blank string or false or zero,
100- // and when both operands are strings no coercion is performed.
101- return $a === $b;
102- }
103-
104- return $a == $b;
105- };
106-
107- return $jsEquals($a, $b) ? $options->fn() : $options->inverse();
118+ // In JS, null is not equal to blank string or false or zero,
119+ // and when both operands are strings no coercion is performed.
120+ $equal = ($a === null || $b === null || is_string($a) && is_string($b))
121+ ? $a === $b
122+ : $a == $b;
123+
124+ return $equal ? $options->fn() : $options->inverse();
108125 },
109126 ],
110127];
@@ -164,3 +181,5 @@ with the following exceptions:
164181
165182* Custom Decorators have not been implemented, as they are [ deprecated in Handlebars.js] ( https://github.com/handlebars-lang/handlebars.js/blob/master/docs/decorators-api.md ) .
166183* The ` data ` and ` compat ` compilation options have not been implemented.
184+ * The [ runtime options to control prototype access] ( https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access ) ,
185+ along with the ` lookupProperty() ` helper option method have not been implemented, since they aren't relevant for PHP.
0 commit comments