Twig und Plugins
Die Verzeichnisstruktur des Beispiels ist sehr einfach:
Diese drei Templates leiten von einander ab:
{% base_extends "index.twig" %}
{% block blubb %}
{{ parent() }}
3<br/>
{% endblock %}
{% base_extends "index.twig" %}
{% block blubb %}
2<br/>
{% endblock %}
<strong>
{% block blubb %}
1
{% endblock %}
</strong>
Am Ende wollen wir folgendes Ergebnis haben:
<strong>
2<br/>
3<br/>
</strong>
Die Basislogik habe ich in einer einfachen Function zusammen gefasst. Hier wird entweder das Plugin heraus gesucht mit dem angefangen werden muss oder das Plugin, das auf das aktuelle folgt und auch dieses Template mitbringt.
function findNewBase($template, $list = [], $currentBase = null) {
$result = 'base';
$found = $currentBase == null; //if null, took the first one
foreach($list as $key => $path) {
if($key == $currentBase) {
$found = true;
}
else if ($found && file_exists($path . '/' . $template)) {
$result = $key;
break;
}
}
return $result;
}
Die Integration wird über ein Token-Parser implementiert.
final class ExtTokenParser extends AbstractTokenParser {
/**
* @var Parser
*/
protected $parser;
private $list = [];
public function __construct(array $list)
{
$this->list = $list;
}
public function getTag(): string
{
return 'base_extends';
}
/**
* @return Node
*/
public function parse(Token $token)
{
$stream = $this->parser->getStream();
$source = $stream->getSourceContext()->getName();
$template = $stream->next()->getValue();
$parts = preg_split("/\//i", preg_replace("/@/", '', $source));
$newBase = findNewBase($template, $this->list, $parts[0]);
$parent = '@' . $newBase . '/' . $template;
$stream->next();
$stream->injectTokens([
new Token(Token::BLOCK_START_TYPE, '', 2),
new Token(Token::NAME_TYPE, 'extends', 2),
new Token(Token::STRING_TYPE, $parent, 2),
new Token(Token::BLOCK_END_TYPE, '', 2),
]);
return new Node();
}
}
Das eigentliche Beispiel sieht dann so aus:
$list = [
'plugin2' => 'templates/path3',
'plugin1' => 'templates/path2',
'base' => 'templates/path1'
];
$loader = new \Twig\Loader\FilesystemLoader();
foreach($list as $plugin => $path) {
$loader->addPath($path, $plugin); //plugin as namespace
}
$twig = new \Twig\Environment($loader);
$twig->addTokenParser(new ExtTokenParser($list));
echo $twig->render('@' . findNewBase('index.twig', $list) . '/index.twig', []);
Und am Ende kommt raus:
Jetzt fehlt nur noch eine passende include-Funktion und man kann sich selbst ein System bauen, desen Templates sich über Plugins ohne Probleme erweitern lassen. Ich arbeite daran....
Edit: Die vollständige Implementierung mit extends und include ist jetzt auf GitHub zu finden.