Passed
Branch 6.0 (c154c1)
by Olivier
11:35
created

Renderer::resolve_options()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 4
nop 2
dl 0
loc 33
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ICanBoogie\Render;
13
14
use function assert;
15
use function is_string;
16
use function iterator_to_array;
17
18
/**
19
 * Renders a target or an array of options.
20
 */
21
class Renderer
22
{
23
    public const VARIABLE_CONTENT = 'content';
24
25
    /**
26
     * @var string[]
27
     */
28
    private readonly array $extensions;
29
30
    public function __construct(
31
        public readonly TemplateResolver $template_resolver,
32
        private readonly EngineProvider $engines
33
    ) {
34
        $this->extensions = array_keys(iterator_to_array($this->engines));
0 ignored issues
show
Bug introduced by
The property extensions is declared read-only in ICanBoogie\Render\Renderer.
Loading history...
35
    }
36
37
    /**
38
     * Resolve a template pathname from its name and type.
39
     *
40
     * @return string Template pathname.
41
     *
42
     * @throws TemplateNotFound if the template pathname cannot be resolved.
43
     */
44
    public function resolve_template(string $name): string
45
    {
46
        $tried = [];
47
        $template_pathname = $this->template_resolver->resolve($name, $this->extensions, $tried);
48
49
        if (!$template_pathname) {
50
            throw new TemplateNotFound("There is no template matching `$name`.", $tried);
51
        }
52
53
        return $template_pathname;
54
    }
55
56
    /**
57
     * Renders a content.
58
     */
59
    public function render(mixed $content, RenderOptions $options = new RenderOptions()): string
60
    {
61
        $variables = $options->locals;
62
63
        if ($options->partial) {
64
            $content = $this->render_partial($options->partial, $content, $variables);
65
        }
66
67
        if ($options->template) {
68
            $content = $this->render_template($options->template, $content, $variables);
69
        }
70
71
        if ($options->layout) {
72
            $content = $this->render_layout($options->layout, [ self::VARIABLE_CONTENT => $content ] + $variables);
73
        }
74
75
        if ($content === null) {
76
            return '';
77
        }
78
79
        return $content;
80
    }
81
82
    /**
83
     * @param array<string, mixed> $variables
84
     */
85
    private function render_partial(string $template, mixed $content, array $variables): string
86
    {
87
        return $this->render_template(
88
            $this->resolve_template_name($template)->as_partial,
89
            $content,
90
            $variables
91
        );
92
    }
93
94
    /**
95
     * @param array<string, mixed> $variables
96
     */
97
    private function render_layout(string $template, array $variables): string
98
    {
99
        return $this->render_template(
100
            $this->resolve_template_name($template)->as_layout,
101
            null,
102
            $variables
103
        );
104
    }
105
106
    /**
107
     * @param array<string, mixed> $variables
108
     */
109
    private function render_template(string $name, mixed $content, array $variables): string
110
    {
111
        $template_pathname = $this->resolve_template($name);
112
        $extension = ExtensionResolver::resolve($template_pathname);
113
114
        assert(is_string($extension));
115
116
        $engine = $this->engines->engine_for_extension($extension)
117
            ?? throw new EngineNotAvailable("There is no engine available to render template `$template_pathname`.");
118
119
        return $engine->render($template_pathname, $content, $variables);
120
    }
121
122
    protected function resolve_template_name(mixed $content): TemplateName
123
    {
124
        return TemplateName::from($content);
125
    }
126
}
127