diff --git a/README.md b/README.md
index 6cd503e..0d2994c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# **PhpEcho**
-`2023-04-09` `PHP 8.0+` `v.5.3.1`
+`2023-04-17` `PHP 8.0+` `v.5.4.0`
## **A native PHP template engine : One class to rule them all**
## **VERSION 5.X IS ONLY FOR PHP 8 AND ABOVE**
@@ -35,6 +35,10 @@ The class will manage :
```bash
composer require rawsrc/phpecho
```
+
+**Changelog v5.4.0:**
+1. Add new abstract class `ViewBuilder` that help to manipulate abstract views as objects
+
**Changelog v5.3.1:**
1. Add option to return natively `null` when a key doesn't exist instead of throwing an `Exception`
By default this option is not activated. To activate, use: `PhpEcho::setNullIfNotExist(true);`; to deactivate,
@@ -149,6 +153,7 @@ www
| | | |--- header.php
| | | |--- home.php
| | | |--- navbar.php
+ | | | |--- login.php
| | | |--- ...
| | |--- layout
| | | |--- err.php
@@ -159,6 +164,7 @@ www
| | | |--- cart.php
| | | |--- err.php
| | | |--- homepage.php
+ | | | |--- login.php
| | | |--- ...
|--- bootstrap.php
|--- index.php
@@ -645,6 +651,69 @@ It's also possible to unset a parameter from the local and global context at onc
$this->unsetAnyParam('document.isPopup');
```
+## **Using the component `ViewBuilder` **
+For complex view, it's often easier to manipulate the whole view as an object.
+Let's have a look at the example about the login page.
+You can now consider this view as a class using `ViewBuilder`.
+We're going to reuse the whole code in a different way:
+
+```php
+namespace YourProject\View\Page;
+
+use rawsrc\PhpEcho\PhpEcho;
+use rawsrc\PhpEcho\ViewBuilder;
+
+class Login extends ViewBuilder
+{
+ public function build(): PhpEcho
+ {
+ // here you can build the page as you want
+ $layout = new PhpEcho('layout/main.php');
+ $layout['description'] = 'dummy.description';
+ $layout['title'] = 'dummy.title';
+ $layout['body'] = new PhpEcho('block/login.php', [
+ 'login' => 'rawsrc',
+ 'url_submit' => 'any/path/for/connection',
+ /*
+ * Note that the ViewBuilder implements the array access interface
+ * So you have plenty of ways to pass your values to the view,
+ * eg: passing values from the current ViewBuilder to the block view:
+ * 'abc' => $this['name'],
+ * 'def' => $this['postal.code'],
+ *
+ * 'abc' and 'def' are keys to be used in the block/login.php and
+ * 'name' and 'postal.code' are keys from the current ViewBuilder
+ * (see below)
+ */
+ ]);
+
+ return $layout;
+ }
+}
+````
+In a controller that must render the login page, you can now code something like that:
+```php
+namespace YourProject\Controller\Login;
+
+use YourProject\View\Page\Login;
+
+class Login
+extends YourAbstractController
+{
+ public function invoke(array $url_data = []): void
+ {
+ $page = new YourProject\View\Page\Login;
+ // we pass some values to the page builder
+ $page['name'] = 'rawsrc';
+ $page['postal.code'] = 'foo.bar';
+
+ // an example of ending the process sought from a framework
+ $this->task->setResponse(Response::$html($page));
+ }
+}
+```
+Much more easy with that addon.
+
## **Let's play with helpers**
As mentioned above, there's some new helpers that have been added to the standard helpers library `stdPhpEchoHelpers.php`.
These helpers will help you to render any HTML code and/or interact with any PhpEcho instance.
diff --git a/tests/README.md b/tests/README.md
index 20e9a55..240a31b 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -1,6 +1,6 @@
# PhpEcho : a native PHP templating engine in one class
-`2023-04-09` `PHP 8.0+` `v.5.3.1`
+`2023-04-17` `PHP 8.0+` `v.5.4.0`
## TESTS
diff --git a/tests/global_tests_result.jpg b/tests/global_tests_result.jpg
index 3d82f22..69fe8bf 100644
Binary files a/tests/global_tests_result.jpg and b/tests/global_tests_result.jpg differ
diff --git a/tests/tests.php b/tests/tests.php
index 9fa64ed..345d0f9 100644
--- a/tests/tests.php
+++ b/tests/tests.php
@@ -12,10 +12,11 @@
//region setup test environment
include_once '../vendor/exacodis/Pilot.php';
include_once '../PhpEcho.php';
+include_once '../ViewBuilder.php';
use Exacodis\Pilot;
-$pilot = new Pilot('PhpEcho - A native PHP template engine - v.5.3.1');
+$pilot = new Pilot('PhpEcho - A native PHP template engine - v.5.4.0');
$pilot->injectStandardHelpers();
include 'filepath.php';
@@ -25,5 +26,6 @@
include 'core.php';
include 'view.php';
include 'heredoc.php';
+include 'viewBuilder.php';
$pilot->createReport();
\ No newline at end of file
diff --git a/tests/viewBuilder.php b/tests/viewBuilder.php
new file mode 100644
index 0000000..3b5c31d
--- /dev/null
+++ b/tests/viewBuilder.php
@@ -0,0 +1,60 @@
+ 'run_txt_block_01']);
+
+ return $layout;
+ }
+}
+
+/** @var Pilot $pilot */
+
+$pilot->run(
+ id: 'view.builder.001',
+ test: function() {
+ $view = new Login();
+
+ return $view->build();
+ },
+ description: 'test the return value from view builder',
+);
+$pilot->assertIsInstanceOf(PhpEcho::class);
+
+$login = new Login();
+$login['foo'] = 'bar';
+$login['abc'] = '" < > "';
+$pilot->run(
+ id: 'view.builder.002',
+ test: fn() => $login['abc'],
+ description: 'array access, value never escaped',
+);
+$pilot->assertIsString();
+$pilot->assertEqual('" < > "');
+
+$pilot->run(
+ id: 'view.builder.003',
+ test: fn() => (string)new Login(),
+ description: 'return a view builder as a string',
+);
+$pilot->assertIsString();
+$pilot->assertEqual(<<
+
+
run_txt_block_01
+ + +html);