Skip to content

Commit 21ad8ac

Browse files
committed
Add an optional unresolved variable error handler callback (closes #4)
1 parent b365541 commit 21ad8ac

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ $render->renderTemplateString(
134134
);
135135
```
136136

137+
By default, a used variable which is not provided will result in an `UndefinedSymbolException`. You can register a callback function via `onResolveError` to handle unresolved variable errors. The callback function must implement the signature `function (string $unresolvedVariable): string`. The provided `$unresolvedVariable` will contain the whole expression which failed to resolve (eg. `myUnresolvedVariable`, `myUnresolvedObject.render(var1, var2)`).
138+
139+
```php
140+
$render->onResolveError(function (string $var): string {
141+
return 'Undefined';
142+
});
143+
144+
// will result in "Person name: Undefined"
145+
$result = $render->renderTemplateString('Person name: {{ name }}');
146+
```
147+
137148
### Loops
138149

139150
If you assign an array or an iterable object you can use the *foreach* loop to iterate.

src/Render.php

+22-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
*/
2020
class Render
2121
{
22-
private const REGEX_VARIABLE = '(?<variable>\w+)(?<nestedVariable>(\.\w+)*)(\.(?<method>\w+)\((?<parameter>[^{}%]*)\))?';
22+
private const REGEX_VARIABLE = '(?<expression>(?<variable>\w+)(?<nestedVariable>(\.\w+)*?)(\.(?<method>\w+)\((?<parameter>[^{}%]*)\))?)';
2323

2424
/** @var array */
2525
private $templates = [];
2626
/** @var string */
2727
private $basePath = '';
28+
/** @var callable */
29+
private $resolveErrorCallback;
2830

2931
/**
3032
* Render constructor.
@@ -37,6 +39,21 @@ public function __construct(string $basePath = '')
3739
$this->basePath = $basePath;
3840
}
3941

42+
/**
43+
* Add a callback to handle resolve errors (eg. call to an unknown variable). By default a resolve error will lead
44+
* to an UnknownSymbolException.
45+
*
46+
* @param callable $resolveErrorCallback
47+
*
48+
* @return $this
49+
*/
50+
public function onResolveError(callable $resolveErrorCallback): self
51+
{
52+
$this->resolveErrorCallback = $resolveErrorCallback;
53+
54+
return $this;
55+
}
56+
4057
/**
4158
* Render a template file
4259
*
@@ -224,6 +241,10 @@ protected function getValue(array $matches, array $variables)
224241
foreach ($variablePath as $variable) {
225242
// first check via isset for faster lookup
226243
if (!isset($resolved[$variable]) && !array_key_exists($variable, $resolved)) {
244+
if ($this->resolveErrorCallback) {
245+
return ($this->resolveErrorCallback)($matches['expression']);
246+
}
247+
227248
throw new UndefinedSymbolException(sprintf('Unknown variable %s', implode('.', $variablePath)));
228249
}
229250

tests/RenderTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,30 @@ public function renderName(string $firstName, string $lastName): string
257257
)
258258
);
259259
}
260+
261+
/**
262+
* @dataProvider resolveErrorDataProvider
263+
*
264+
* @param string $var
265+
*/
266+
public function testResolveErrorCallback(string $var): void
267+
{
268+
$this->render->onResolveError(function (string $resolveError) use ($var): string {
269+
$this->assertSame($var, $resolveError);
270+
271+
return 'callback-result';
272+
});
273+
274+
$this->assertSame('callback-result', $this->render->renderTemplateString("{{ $var }}"));
275+
}
276+
277+
public function resolveErrorDataProvider()
278+
{
279+
return [
280+
'simple variable' => ['person'],
281+
'nested variable' => ['person.name'],
282+
'object function call' => ['person.renderName()'],
283+
'object function call with parameters' => ['person.renderName(firstname, lastname)'],
284+
];
285+
}
260286
}

0 commit comments

Comments
 (0)