Skip to content

Commit 0f47a80

Browse files
dinhquochanbrendt
andauthored
feat(view): add twig support (#841)
Co-authored-by: Brent Roose <[email protected]>
1 parent 09ced7a commit 0f47a80

File tree

7 files changed

+145
-1
lines changed

7 files changed

+145
-1
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"rector/rector": "^2.0-rc2",
5151
"spatie/phpunit-snapshot-assertions": "^5.1.6",
5252
"spaze/phpstan-disallowed-calls": "^4.0",
53-
"symplify/monorepo-builder": "^11.2"
53+
"symplify/monorepo-builder": "^11.2",
54+
"twig/twig": "^3.16"
5455
},
5556
"replace": {
5657
"tempest/auth": "self.version",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\View\Renderers;
6+
7+
final readonly class TwigConfig
8+
{
9+
/**
10+
* @see \Twig\Environment::__construct()
11+
*/
12+
public function __construct(
13+
public array $viewPaths = [],
14+
public ?string $cachePath = null,
15+
public bool $debug = false,
16+
public string $charset = 'utf-8',
17+
public bool $strictVariables = false,
18+
public string $autoescape = 'html',
19+
public ?bool $autoReload = null,
20+
public int $optimizations = -1,
21+
) {
22+
}
23+
24+
public function toArray(): array
25+
{
26+
return [
27+
'debug' => $this->debug,
28+
'charset' => $this->charset,
29+
'strict_variables' => $this->strictVariables,
30+
'autoescape' => $this->autoescape,
31+
'cache' => $this->cachePath ?: false,
32+
'auto_reload' => $this->autoReload,
33+
'optimizations' => $this->optimizations,
34+
];
35+
}
36+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\View\Renderers;
6+
7+
use Tempest\Container\Container;
8+
use Tempest\Container\DynamicInitializer;
9+
use Tempest\Container\Singleton;
10+
use Tempest\Reflection\ClassReflector;
11+
use Twig\Environment;
12+
use Twig\Loader\FilesystemLoader;
13+
14+
final readonly class TwigInitializer implements DynamicInitializer
15+
{
16+
public function canInitialize(ClassReflector $class): bool
17+
{
18+
if (! class_exists(Environment::class)) {
19+
return false;
20+
}
21+
22+
return $class->getName() === Environment::class;
23+
}
24+
25+
#[Singleton]
26+
public function initialize(ClassReflector $class, Container $container): object
27+
{
28+
$twigConfig = $container->get(TwigConfig::class);
29+
$twigLoader = new FilesystemLoader($twigConfig->viewPaths);
30+
31+
return new Environment($twigLoader, $twigConfig->toArray());
32+
}
33+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\View\Renderers;
6+
7+
use Tempest\View\View;
8+
use Tempest\View\ViewRenderer;
9+
use Twig\Environment;
10+
11+
final readonly class TwigViewRenderer implements ViewRenderer
12+
{
13+
public function __construct(private Environment $twig)
14+
{
15+
}
16+
17+
public function render(View|string|null $view): string
18+
{
19+
if ($view === null) {
20+
return '';
21+
}
22+
23+
return trim($this->twig->render($view->getPath(), $view->getData()));
24+
}
25+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Tempest\Integration\View;
6+
7+
use Tempest\View\Renderers\TwigConfig;
8+
use Tempest\View\Renderers\TwigViewRenderer;
9+
use Tempest\View\ViewConfig;
10+
use Tempest\View\ViewRenderer;
11+
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
12+
use function Tempest\view;
13+
14+
/**
15+
* @internal
16+
*/
17+
final class TwigViewRendererTest extends FrameworkIntegrationTestCase
18+
{
19+
public function test_twig(): void
20+
{
21+
$viewConfig = $this->container->get(ViewConfig::class);
22+
23+
$viewConfig->rendererClass = TwigViewRenderer::class;
24+
25+
$this->container->config(new TwigConfig(
26+
viewPaths: [__DIR__ . '/twig'],
27+
cachePath: __DIR__ . '/../../../.cache/tempest/twig/cache',
28+
));
29+
30+
$renderer = $this->container->get(ViewRenderer::class);
31+
32+
$html = $renderer->render(view('index.twig', ...['foo' => 'bar']));
33+
34+
$this->assertStringEqualsStringIgnoringLineEndings(<<<HTML
35+
<html>
36+
<span>bar</span>
37+
</html>
38+
HTML
39+
, $html);
40+
}
41+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<html>
2+
{% block content %}{% endblock %}
3+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{% extends "base.twig" %}
2+
3+
{% block content %}
4+
<span>{{ foo }}</span>
5+
{% endblock %}

0 commit comments

Comments
 (0)