tyam/bamboo

PHP template engine, which has variables-pulling facility.

0.2.0 2019-08-21 00:45 UTC

This package is not auto-updated.

Last update: 2024-05-12 03:11:59 UTC


README

Bamboo is a PHP template engine, which has the following characteristics:

  • native PHP as template language; no compile. familiar and well-defined syntax
  • using global functions as helpers/escapers; no redundant $e->h()/E::h(). just eh()
  • facility to PULL template variables from the program side

Installation

composer install tyam\bamboo

Basic Usage

PHP side

// You must specify template base directory to the bamboo engine.
$basedirs = ['/your/template/dirs', '/come/here'];
$engine = new \tyam\bamboo\Engine($basedirs);

// Render the template with template variables. You get an output string.
$variables = ['title' => $this->getTitle(), 'content' => $this->getContent()];
$output = $engine->render('mytpl', $variables);
$respond->setOutput($output);

template side: /your/template/dirs/mytpl.php

<?php /* You must escape string explicitly. */ ?>
<?= htmlspecialchars($title) ?> 
<div>
<?= $content ?>
</div>
  • A filename extension for template file is 'php'
  • Path separator for the base directory is fixed as slash. bamboo engine converts to host specific one.

Including another template

subject template: /your/template/dirs/mytpl.php

content comes here...
<?php $renderer->include('common/another'); ?>
your content continues...

another template: /your/template/dirs/common/another.php

write anything...
  • $renderer is a special variable from bamboo, which provides you the rendering instructions.
  • Template files can be grouped by subdirectories. Then a template name becomes 'subdir/template' (slash delimited).

Template inheritance

child template: /your/template/dirs/content.php

<?php $renderer->wrap('common/layout') ?>
content comes here...

parent template: /your/template/dirs/common/layout.php

<html>
<body>
header...
<div class="main">
<?php $renderer->content(); ?>
</div>
footer...
</body>
</html>
  • In child template, by wrap() you can specify parent template. The rest part becomes a content for the parent.
  • In parent template, by content() you can outpupt the content generated by a child.
  • Template inheritance must be single. You cannot specify wrap() twice.
  • Multi-level inheritance is supported. Note that content() outputs a result of exact child template. You must call content() every parent template.

Sections (Capturing block for other template)

child template: /your/template/dirs/content.php

<?php $renderer->wrap('common/layout'); ?>
<?php $renderer->section('title') ?>
<?= htmlspecialchars($title) ?>
<?php $renderer->endsection('title') ?>
content comes here...

parent template: /your/template/dirs/common/layout.php

<html>
<head>
<title><?php $renderer->yield('title') ?></title>
</head>
...
  • You can capture a block of content, called 'section', between section() and endsection().
  • Sections are named. Then, you can specify a name of a section to yield() to output a block.
  • Passing a section name to endsection() is redundant and optional, but useful as sanity check purpose. Bamboo throws an exception when the passed name was insane.
  • Note that evaluation order is matter. Bamboo always evaluates child first. So you can yield() only sections captured by descendant templates.

Variables passing

<?php $renderer->wrap('parent', ['title' => $title]) ?>
<?php $renderer->include('another', ['title' => $title]) ?>
  • You can pass template variables when you call another template.
  • Note that you must pass template variables for each template. Template variables are not shared among templates.

Variables pulling

On production sites, a page is filled by dynamic contents. User name, logged in or not, notifications, recommendations, advertisements, etc. They all are side contents, not a main content.
We must fetch them all from the Model layer, then bind to template as variables. That code makes our PHP side (controller, responder or anything) ugly.

To improve this situation, bamboo has the variable-pulling facility. With it, templates automatically pull template variables from PHP side. Then, you can separate the code of side contents from that of main content.
Variable-pulling is actualized by passing a variable provider to bamboo engine.

PHP side

use tyam\bamboo\Engine;
use tyam\bamboo\VariableProvider;

class MyController implements VariableProvider
{
    // This is the method declared abstract in VariableProvider and providing side contents.
    public function provideVariables(string $template): array
    {
        // Here you provide side contents.
        return [
            'user' => $this->getUser(), 
            'notifications' => $this->getNotifications(), 
            'recommendations' => $this->getRecommendations()
        ];
    }

    public function action()
    {
        // Get domain result in some way.
        $result = $this->callDomain();

        // Pass $this as VariableProvider to bamboo engine.
        $engine = new Engine(self::$basedirs, $this);

        // Just bind domain result to the template.
        $output = $engine->render('my/template', ['result' => $result]);

        // Then output. Thanks to variable-pulling, the code is quite straight forward.
        $this->response->output($output);
    }
}
  • Before rendering a template, bamboo calls provideVariables() with its name. Then, you return variables for the template.
  • In above example, template varialbes user, notifications and recommendations can be used in 'my/template', its parents and all included templates.
  • If variable names collided, explicit ones, which is passed via $engine->render(), $renderer->wrap() or $renderer->include(), hides implicit ones, which is pulled via provideVariables().

Getting section values

As a effect of template rendering, other than returned output, there exists section values.
Usually section values are consumed in templates, and PHP side has no interest in them.
But, when PHP side is requiring something other than body output, sections are good place to track.
You can pass an ArrayAccess object to $engine->render(). Then bamboo uses this array to hold all section values, and you can investigate it after rendering.

The good example is e-mail tamplating.

template: e-mail template

<?php /* Store Subject header in a section */ ?>
<?php $renderer->section('Subject') ?>
Confirmation instructions
<?php $renderer->endsection() ?>

<?php /* Store To header in a section */ ?>
<?php $renderer->section('To') ?>
<?= $user->getEmail() ?>
<?php $renderer->endsection() ?>

Thanks for signing up!
To get started, click the link below and confirm your account.
<?= $confirmationUrl ?>

PHP side

$variables = ['user' => $user, 'confirmationUrl' => $confirmationUrl];

// Pass an empty array for sections. 
$sections = new \ArrayObject();
$output = $engine->render('email/signup-confirm', $variables, $sections);

// Now we get Subject header and To header from sections
$mailer->setSubject($sections['Subject']);
$mailer->setTo($sections['To']);

$mailer->setBody($output);
$mailer->send();

In the example above, we use sections to track some e-mail header values.

Other topics

  • I recommend you define helper/escaper functions in the global namespace. If that is a bother, you can find some functions in 'function.php' in this package. Call Engine::loadFunctions() if you like it.

Lisence

Bamboo is MIT licensed. See LICENSE.