julienmonnerie/kirby/src/Template/Template.php
2023-04-14 16:34:06 +02:00

208 lines
4.5 KiB
PHP

<?php
namespace Kirby\Template;
use Exception;
use Kirby\Cms\App;
use Kirby\Filesystem\F;
use Kirby\Toolkit\Tpl;
/**
* Represents a Kirby template and takes care
* of loading the correct file.
*
* @package Kirby Template
* @author Bastian Allgeier <bastian@getkirby.com>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*/
class Template
{
/**
* Global template data
*/
public static array $data = [];
/**
* Default template type if no specific type is set
*/
protected string $defaultType;
/**
* The name of the template
*/
protected string $name;
/**
* Template type (html, json, etc.)
*/
protected string $type;
/**
* Creates a new template object
*/
public function __construct(string $name, string $type = 'html', string $defaultType = 'html')
{
$this->name = strtolower($name);
$this->type = $type;
$this->defaultType = $defaultType;
}
/**
* Converts the object to a simple string
* This is used in template filters for example
*/
public function __toString(): string
{
return $this->name;
}
/**
* Returns the default template type
*/
public function defaultType(): string
{
return $this->defaultType;
}
/**
* Checks if the template exists
*/
public function exists(): bool
{
if ($file = $this->file()) {
return file_exists($file);
}
return false;
}
/**
* Returns the expected template file extension
*/
public function extension(): string
{
return 'php';
}
/**
* Detects the location of the template file
* if it exists.
*/
public function file(): string|null
{
$name = $this->name();
$extension = $this->extension();
$store = $this->store();
$root = $this->root();
if ($this->hasDefaultType() === true) {
try {
// Try the default template in the default template directory.
return F::realpath($root . '/' . $name . '.' . $extension, $root);
} catch (Exception) {
// ignore errors, continue searching
}
// Look for the default template provided by an extension.
$path = App::instance()->extension($store, $name);
if ($path !== null) {
return $path;
}
}
$name .= '.' . $this->type();
try {
// Try the template with type extension in the default template directory.
return F::realpath($root . '/' . $name . '.' . $extension, $root);
} catch (Exception) {
// Look for the template with type extension provided by an extension.
// This might be null if the template does not exist.
return App::instance()->extension($store, $name);
}
}
/**
* Checks if the template uses the default type
*/
public function hasDefaultType(): bool
{
return $this->type() === $this->defaultType();
}
/**
* Returns the template name
*/
public function name(): string
{
return $this->name;
}
/**
* Renders the template with the given template data
*/
public function render(array $data = []): string
{
// if the template is rendered inside a snippet,
// we need to keep the "outside" snippet object
// to compare it later
$snippet = Snippet::$current;
// load the template
$template = Tpl::load($this->file(), $data);
// if last `endsnippet()` inside the current template
// has been omitted (= snippet was used as layout snippet),
// `Snippet::$current` will point to a snippet that was
// opened inside the template; if that snippet is the direct
// child of the snippet that was open before the template was
// rendered (which could be `null` if no snippet was open),
// take the buffer output from the template as default slot
// and render the snippet as final template output
if (
Snippet::$current === null ||
Snippet::$current->parent() !== $snippet
) {
return $template;
}
// no slots have been defined, but the template code
// should be used as default slot
if (Snippet::$current->slots()->count() === 0) {
return Snippet::$current->render($data, [
'default' => $template
]);
}
// let the snippet close and render natively
return Snippet::$current->render($data);
}
/**
* Returns the root to the templates directory
*/
public function root(): string
{
return App::instance()->root($this->store());
}
/**
* Returns the place where templates are located
* in the site folder and and can be found in extensions
*/
public function store(): string
{
return 'templates';
}
/**
* Returns the template type
*/
public function type(): string
{
return $this->type;
}
}