Passed
Pull Request — master (#122)
by
unknown
11:03
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\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 addContentInjections(array $injections): self
113
    {
114
        $new = clone $this;
115
        $new->contentInjections = array_merge($this->contentInjections, $injections);
116
        return $new;
117
    }
118
119
    public function withContentInjections(array $injections): self
120
    {
121
        $new = clone $this;
122
        $new->contentInjections = $injections;
123
        return $new;
124
    }
125
126
    public function addLayoutInjections(array $injections): self
127
    {
128
        $new = clone $this;
129
        $new->layoutInjections = array_merge($this->layoutInjections, $injections);
130
        return $new;
131
    }
132
133
    public function withLayoutInjections(array $injections): self
134
    {
135
        $new = clone $this;
136
        $new->layoutInjections = $injections;
137
        return $new;
138
    }
139
140
    public function withCsrf(?string $requestAttribute = null): self
141
    {
142
        $injecton = $requestAttribute === null ? $this->csrfInjection : $this->csrfInjection->withRequestAttribute($requestAttribute);
143
        return $this->addContentInjections([$injecton]);
144
    }
145
146
    private function renderProxy(string $view, array $parameters = []): string
147
    {
148
        $parameters = $this->injectParameters($parameters, $this->contentInjections);
149
        $content = $this->view->render($view, $parameters, $this);
150
151
        $layout = $this->findLayoutFile($this->layout);
152
        if ($layout === null) {
0 ignored issues
show
introduced by
The condition $layout === null is always false.
Loading history...
153
            return $content;
154
        }
155
156
        $layoutParameters = $this->injectParameters(['content' => $content], $this->layoutInjections);
157
158
        return $this->view->renderFile(
159
            $layout,
160
            $layoutParameters,
161
            $this,
162
        );
163
    }
164
165
    /**
166
     * @param array $parameters
167
     * @param InjectionInterface[] $injections
168
     * @return array
169
     */
170
    private function injectParameters(array $parameters, array $injections): array
171
    {
172
        foreach ($injections as $injection) {
173
            $parameters = array_merge($parameters, $injection->getParams());
174
        }
175
        return $parameters;
176
    }
177
178
    private function findLayoutFile(?string $file): ?string
179
    {
180
        if ($file === null) {
181
            return null;
182
        }
183
184
        $file = $this->aliases->get($file);
185
186
        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
187
            return $file;
188
        }
189
190
        return $file . '.' . $this->view->getDefaultExtension();
191
    }
192
193
    /**
194
     * Returns the controller name. Name should be converted to "id" case.
195
     * Method returns classname without `controller` on the ending.
196
     * If namespace is not contain `controller` or `controllers`
197
     * then returns only classname without `controller` on the ending
198
     * else returns all subnamespaces from `controller` (or `controllers`) to the end
199
     *
200
     * @param object $controller
201
     * @return string
202
     * @example App\Controller\FooBar\BazController -> foo-bar/baz
203
     * @example App\Controllers\FooBar\BazController -> foo-bar/baz
204
     * @example Path\To\File\BlogController -> blog
205
     * @see Inflector::camel2id()
206
     */
207
    private static function getName(object $controller): string
208
    {
209
        static $cache = [];
210
211
        $class = get_class($controller);
212
        if (isset($cache[$class])) {
213
            return $cache[$class];
214
        }
215
216
        $regexp = '/((?<=controller\\\|s\\\)(?:[\w\\\]+)|(?:[a-z]+))controller/iuU';
217
        if (!preg_match($regexp, $class, $m) || empty($m[1])) {
218
            throw new \RuntimeException('Cannot detect controller name');
219
        }
220
221
        $inflector = new Inflector();
222
        $name = str_replace('\\', '/', $m[1]);
223
        $name = $inflector->camel2id($name);
224
225
        $cache[$class] = $name;
226
        return $name;
227
    }
228
}
229