Passed
Pull Request — master (#122)
by
unknown
10:47
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 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