Passed
Push — master ( 6b1ebf...63457f )
by Melech
04:00
created

PhpRenderer::createTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\View;
15
16
use Valkyrja\Exception\RuntimeException;
17
use Valkyrja\View\Contract\Renderer as Contract;
18
use Valkyrja\View\Exception\InvalidConfigPath;
19
use Valkyrja\View\Template\Contract\Template;
20
use Valkyrja\View\Template\Template as DefaultTemplate;
21
22
use function explode;
23
use function extract;
24
use function implode;
25
use function ob_get_clean;
26
use function ob_start;
27
use function trim;
28
29
use const DIRECTORY_SEPARATOR;
30
use const EXTR_SKIP;
31
32
/**
33
 * Class PhpRenderer.
34
 *
35
 * @author Melech Mizrachi
36
 */
37
class PhpRenderer implements Contract
38
{
39
    /**
40
     * PhpRenderer constructor.
41
     *
42
     * @param array<string, string> $paths
43
     */
44
    public function __construct(
45
        protected string $dir,
46
        protected string $fileExtension = '.phtml',
47
        protected array $paths = [],
48
    ) {
49
    }
50
51
    /**
52
     * @inheritDoc
53
     */
54
    public function startRender(): void
55
    {
56
        ob_start();
57
    }
58
59
    /**
60
     * @inheritDoc
61
     */
62
    public function endRender(): string
63
    {
64
        $obClean = ob_get_clean();
65
66
        if ($obClean === false) {
67
            throw new RuntimeException('Render failed');
68
        }
69
70
        return $obClean;
71
    }
72
73
    /**
74
     * @inheritDoc
75
     */
76
    public function render(string $name, array $variables = []): string
77
    {
78
        return $this->createTemplate(name: $name, variables: $variables)->render();
79
    }
80
81
    /**
82
     * @inheritDoc
83
     */
84
    public function createTemplate(string $name, array $variables = []): Template
85
    {
86
        return new DefaultTemplate(
87
            renderer: $this,
88
            name: $name,
89
            variables: $variables
90
        );
91
    }
92
93
    /**
94
     * @inheritDoc
95
     */
96
    public function renderFile(string $name, array $variables = []): string
97
    {
98
        return $this->renderFullPath($this->getFullPath($name), $variables);
99
    }
100
101
    /**
102
     * Render a full file path.
103
     *
104
     * @param string               $path      The file path
105
     * @param array<string, mixed> $variables [optional] The variables
106
     *
107
     * @return string
108
     */
109
    protected function renderFullPath(string $path, array $variables = []): string
110
    {
111
        $this->startRender();
112
        $this->requirePath($path, $variables);
113
114
        return $this->endRender();
115
    }
116
117
    /**
118
     * Require a path to generate its contents with provided variables.
119
     *
120
     * @param string               $path      The file path
121
     * @param array<string, mixed> $variables [optional] The variables
122
     *
123
     * @return void
124
     */
125
    protected function requirePath(string $path, array $variables = []): void
126
    {
127
        if (is_file($path)) {
128
            extract($variables, EXTR_SKIP);
129
130
            require $path;
131
132
            return;
133
        }
134
135
        throw new RuntimeException("Path does not exist at $path");
136
    }
137
138
    /**
139
     * Get the full path for a given template name.
140
     *
141
     * @param string $template The template
142
     *
143
     * @throws InvalidConfigPath
144
     *
145
     * @return string
146
     */
147
    protected function getFullPath(string $template): string
148
    {
149
        // If the first character of the template is an @ symbol
150
        // Then this is a template from a path in the config
151
        if (str_starts_with($template, '@')) {
152
            $parts = explode(DIRECTORY_SEPARATOR, $template);
153
            $path  = $this->paths[$parts[0]] ?? null;
154
155
            // If there is no path
156
            if ($path === null) {
157
                // Then throw an exception
158
                throw new InvalidConfigPath(
159
                    'Invalid path '
160
                    . $parts[0]
161
                    . ' specified for template '
162
                    . $template
163
                );
164
            }
165
166
            // Remove any trailing slashes
167
            $parts[0] = DIRECTORY_SEPARATOR . trim($path, DIRECTORY_SEPARATOR);
168
169
            $path = implode(DIRECTORY_SEPARATOR, $parts);
170
        } else {
171
            $path = $this->getDir($template);
172
        }
173
174
        return $path . $this->fileExtension;
175
    }
176
177
    /**
178
     * Get the template directory.
179
     *
180
     * @param string|null $path [optional] The path to append
181
     *
182
     * @return string
183
     */
184
    protected function getDir(string|null $path = null): string
185
    {
186
        return $this->dir
187
            . ($path !== null && $path !== ''
188
                ? DIRECTORY_SEPARATOR . $path
189
                : '');
190
    }
191
}
192