Passed
Pull Request — master (#122)
by
unknown
13:01
created

ViewRenderer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 8
dl 0
loc 18
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 $contentInjections;
28
    private array $layoutInjections;
29
30
    public function __construct(
31
        DataResponseFactoryInterface $responseFactory,
32
        Aliases $aliases,
33
        CsrfInjection $csrfInjection,
34
        WebView $view,
35
        string $viewBasePath,
36
        string $layout,
37
        array $contentInjections = [],
38
        array $layoutInjections = []
39
    ) {
40
        $this->responseFactory = $responseFactory;
41
        $this->aliases = $aliases;
42
        $this->csrfInjection = $csrfInjection;
43
        $this->view = $view;
44
        $this->viewBasePath = $viewBasePath;
45
        $this->layout = $layout;
46
        $this->contentInjections = $contentInjections;
47
        $this->layoutInjections = $layoutInjections;
48
    }
49
50
    public function getViewPath(): string
51
    {
52
        if ($this->viewPath !== null) {
53
            return $this->viewPath;
54
        }
55
56
        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

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