Passed
Pull Request — master (#122)
by
unknown
10:47
created

ViewRenderer::injectParameters()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 2
b 0
f 0
nc 2
nop 2
dl 0
loc 6
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\ViewRenderer;
6
7
use Psr\Http\Message\ResponseInterface;
8
use Yiisoft\Aliases\Aliases;
9
use Yiisoft\Strings\Inflector;
10
use Yiisoft\View\ViewContextInterface;
11
use Yiisoft\View\WebView;
12
use Yiisoft\DataResponse\DataResponseFactoryInterface;
13
14
final class ViewRenderer implements ViewContextInterface
15
{
16
    protected ?string $name = null;
17
    protected DataResponseFactoryInterface $responseFactory;
18
19
    private Aliases $aliases;
20
    private CsrfInjection $csrfInjection;
21
    private WebView $view;
22
    private string $layout;
23
    private ?string $viewBasePath;
24
    private ?string $viewPath = null;
25
26
    private array $contentInjections;
27
    private array $layoutInjections;
28
29
    public function __construct(
30
        DataResponseFactoryInterface $responseFactory,
31
        Aliases $aliases,
32
        CsrfInjection $csrfInjection,
33
        WebView $view,
34
        string $viewBasePath,
35
        string $layout,
36
        array $contentInjections = [],
37
        array $layoutInjections = []
38
    ) {
39
        $this->responseFactory = $responseFactory;
40
        $this->aliases = $aliases;
41
        $this->csrfInjection = $csrfInjection;
42
        $this->view = $view;
43
        $this->viewBasePath = $viewBasePath;
44
        $this->layout = $layout;
45
        $this->contentInjections = $contentInjections;
46
        $this->layoutInjections = $layoutInjections;
47
    }
48
49
    public function getViewPath(): string
50
    {
51
        if ($this->viewPath !== null) {
52
            return $this->viewPath;
53
        }
54
55
        return $this->aliases->get($this->viewBasePath) . '/' . $this->name;
0 ignored issues
show
Bug introduced by
It seems like $this->viewBasePath can also be of type null; however, parameter $alias of Yiisoft\Aliases\Aliases::get() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

55
        return $this->aliases->get(/** @scrutinizer ignore-type */ $this->viewBasePath) . '/' . $this->name;
Loading history...
56
    }
57
58
    public function render(string $view, array $parameters = []): ResponseInterface
59
    {
60
        $contentRenderer = fn() => $this->renderProxy($view, $parameters);
61
62
        return $this->responseFactory->createResponse($contentRenderer);
63
    }
64
65
    public function renderPartial(string $view, array $parameters = []): ResponseInterface
66
    {
67
        $content = $this->view->render($view, $parameters, $this);
68
69
        return $this->responseFactory->createResponse($content);
70
    }
71
72
    public function withController(object $controller): self
73
    {
74
        $new = clone $this;
75
        $new->name = static::getName($controller);
76
77
        return $new;
78
    }
79
80
    public function withControllerName(string $name): self
81
    {
82
        $new = clone $this;
83
        $new->name = $name;
84
85
        return $new;
86
    }
87
88
    public function withViewPath(string $viewPath): self
89
    {
90
        $new = clone $this;
91
        $new->viewPath = $viewPath;
92
93
        return $new;
94
    }
95
96
    public function withViewBasePath(string $viewBasePath): self
97
    {
98
        $new = clone $this;
99
        $new->viewBasePath = $viewBasePath;
100
101
        return $new;
102
    }
103
104
    public function withLayout(string $layout): self
105
    {
106
        $new = clone $this;
107
        $new->layout = $layout;
108
109
        return $new;
110
    }
111
112
    public function withExtraContentInjections(array $injections): self
113
    {
114
        $new = clone $this;
115
        $new->contentInjections = array_merge($this->contentInjections, $injections);
116
        return $new;
117
    }
118
119
    public function withCsrf(?string $requestAttribute = null): self
120
    {
121
        $injecton = $requestAttribute === null ? $this->csrfInjection : $this->csrfInjection->withRequestAttribute($requestAttribute);
122
        return $this->withExtraContentInjections([$injecton]);
123
    }
124
125
    private function renderProxy(string $view, array $parameters = []): string
126
    {
127
        $parameters = $this->injectParameters($parameters, $this->contentInjections);
128
        $content = $this->view->render($view, $parameters, $this);
129
130
        $layout = $this->findLayoutFile($this->layout);
131
        if ($layout === null) {
0 ignored issues
show
introduced by
The condition $layout === null is always false.
Loading history...
132
            return $content;
133
        }
134
135
        $layoutParameters = $this->injectParameters(['content' => $content], $this->layoutInjections);
136
137
        return $this->view->renderFile(
138
            $layout,
139
            $layoutParameters,
140
            $this,
141
        );
142
    }
143
144
    /**
145
     * @param array $parameters
146
     * @param InjectionInterface[] $injections
147
     * @return array
148
     */
149
    private function injectParameters(array $parameters, array $injections): array
150
    {
151
        foreach ($injections as $injection) {
152
            $parameters = array_merge($parameters, $injection->getParams());
153
        }
154
        return $parameters;
155
    }
156
157
    private function findLayoutFile(?string $file): ?string
158
    {
159
        if ($file === null) {
160
            return null;
161
        }
162
163
        $file = $this->aliases->get($file);
164
165
        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
166
            return $file;
167
        }
168
169
        return $file . '.' . $this->view->getDefaultExtension();
170
    }
171
172
    /**
173
     * Returns the controller name. Name should be converted to "id" case.
174
     * Method returns classname without `controller` on the ending.
175
     * If namespace is not contain `controller` or `controllers`
176
     * then returns only classname without `controller` on the ending
177
     * else returns all subnamespaces from `controller` (or `controllers`) to the end
178
     *
179
     * @param object $controller
180
     * @return string
181
     * @example App\Controller\FooBar\BazController -> foo-bar/baz
182
     * @example App\Controllers\FooBar\BazController -> foo-bar/baz
183
     * @example Path\To\File\BlogController -> blog
184
     * @see Inflector::camel2id()
185
     */
186
    private static function getName(object $controller): string
187
    {
188
        static $cache = [];
189
190
        $class = get_class($controller);
191
        if (isset($cache[$class])) {
192
            return $cache[$class];
193
        }
194
195
        $regexp = '/((?<=controller\\\|s\\\)(?:[\w\\\]+)|(?:[a-z]+))controller/iuU';
196
        if (!preg_match($regexp, $class, $m) || empty($m[1])) {
197
            throw new \RuntimeException('Cannot detect controller name');
198
        }
199
200
        $inflector = new Inflector();
201
        $name = str_replace('\\', '/', $m[1]);
202
        $name = $inflector->camel2id($name);
203
204
        $cache[$class] = $name;
205
        return $name;
206
    }
207
}
208