Passed
Pull Request — master (#122)
by
unknown
16:22 queued 01:22
created

ViewRenderer::renderProxy()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 3
b 0
f 0
nc 2
nop 2
dl 0
loc 16
rs 9.9332
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\Arrays\ArrayHelper;
10
use Yiisoft\Strings\Inflector;
11
use Yiisoft\View\ViewContextInterface;
12
use Yiisoft\View\WebView;
13
use Yiisoft\DataResponse\DataResponseFactoryInterface;
14
15
final class ViewRenderer implements ViewContextInterface
16
{
17
    protected ?string $name = null;
18
    protected DataResponseFactoryInterface $responseFactory;
19
20
    private Aliases $aliases;
21
    private CsrfInjection $csrfInjection;
22
    private WebView $view;
23
    private string $layout;
24
    private ?string $viewBasePath;
25
    private ?string $viewPath = null;
26
27
    private array $injections;
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 $injections = []
37
    ) {
38
        $this->responseFactory = $responseFactory;
39
        $this->aliases = $aliases;
40
        $this->csrfInjection = $csrfInjection;
41
        $this->view = $view;
42
        $this->viewBasePath = $viewBasePath;
43
        $this->layout = $layout;
44
        $this->injections = $injections;
45
    }
46
47
    public function getViewPath(): string
48
    {
49
        if ($this->viewPath !== null) {
50
            return $this->viewPath;
51
        }
52
53
        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

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