Passed
Pull Request — master (#118)
by Dmitriy
15:03
created

ViewRenderer::withCsrf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
namespace App;
4
5
use Psr\Http\Message\ResponseInterface;
6
use Yiisoft\Aliases\Aliases;
7
use Yiisoft\Router\UrlMatcherInterface;
8
use Yiisoft\Strings\Inflector;
9
use Yiisoft\View\ViewContextInterface;
10
use Yiisoft\View\WebView;
11
use Yiisoft\DataResponse\DataResponseFactoryInterface;
12
use Yiisoft\Yii\Web\User\User;
13
14
final class ViewRenderer implements ViewContextInterface
15
{
16
    protected ?string $name = null;
17
    protected DataResponseFactoryInterface $responseFactory;
18
    protected User $user;
19
20
    private UrlMatcherInterface $urlMatcher;
21
    private Aliases $aliases;
22
    private WebView $view;
23
    private string $layout;
24
    private ?string $viewBasePath;
25
    private ?string $viewPath = null;
26
    private ?string $csrf = null;
27
28
    public function __construct(
29
        DataResponseFactoryInterface $responseFactory,
30
        User $user,
31
        Aliases $aliases,
32
        WebView $view,
33
        UrlMatcherInterface $urlMatcher,
34
        string $viewBasePath,
35
        string $layout
36
    ) {
37
        $this->responseFactory = $responseFactory;
38
        $this->user = $user;
39
        $this->aliases = $aliases;
40
        $this->view = $view;
41
        $this->urlMatcher = $urlMatcher;
42
        $this->viewBasePath = $viewBasePath;
43
        $this->layout = $layout;
44
    }
45
46
    public function getViewPath(): string
47
    {
48
        if ($this->viewPath !== null) {
49
            return $this->viewPath;
50
        }
51
52
        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

52
        return $this->aliases->get(/** @scrutinizer ignore-type */ $this->viewBasePath) . '/' . $this->name;
Loading history...
53
    }
54
55
    public function render(string $view, array $parameters = []): ResponseInterface
56
    {
57
        $contentRenderer = fn () => $this->renderProxy($view, $parameters);
58
59
        return $this->responseFactory->createResponse($contentRenderer);
60
    }
61
62
    public function renderPartial(string $view, array $parameters = []): ResponseInterface
63
    {
64
        $content = $this->view->render($view, $parameters, $this);
65
66
        return $this->responseFactory->createResponse($content);
67
    }
68
69
    public function withController(object $controller): self
70
    {
71
        $new = clone $this;
72
        $new->name = $this->getName($controller);
73
74
        return $new;
75
    }
76
77
    public function withControllerName(string $name): self
78
    {
79
        $new = clone $this;
80
        $new->name = $name;
81
82
        return $new;
83
    }
84
85
    public function withViewPath(string $viewPath): self
86
    {
87
        $new = clone $this;
88
        $new->viewPath = $viewPath;
89
90
        return $new;
91
    }
92
93
    public function withViewBasePath(string $viewBasePath): self
94
    {
95
        $new = clone $this;
96
        $new->viewBasePath = $viewBasePath;
97
98
        return $new;
99
    }
100
101
    public function withLayout(string $layout): self
102
    {
103
        $new = clone $this;
104
        $new->layout = $layout;
105
106
        return $new;
107
    }
108
109
    public function withCsrf(): self
110
    {
111
        $new = clone $this;
112
        $new->csrf = $this->getCsrf();
113
114
        return $new;
115
    }
116
117
    private function renderProxy(string $view, array $parameters = []): string
0 ignored issues
show
Unused Code introduced by
The parameter $parameters is not used and could be removed. ( Ignorable by Annotation )

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

117
    private function renderProxy(string $view, /** @scrutinizer ignore-unused */ array $parameters = []): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
118
    {
119
        $parameters = [];
120
        if ($this->csrf !== null) {
121
            $parameters['csrf'] = $this->csrf;
122
123
            $this->view->registerMetaTag(
124
                [
125
                    'name' => 'csrf',
126
                    'content' => $this->csrf,
127
                ],
128
                'csrf_meta_tags'
129
            );
130
        }
131
        $content = $this->view->render($view, $parameters, $this);
132
        $user = $this->user->getIdentity();
133
        $layout = $this->findLayoutFile($this->aliases->get($this->layout));
134
135
        if ($layout === null) {
0 ignored issues
show
introduced by
The condition $layout === null is always false.
Loading history...
136
            return $content;
137
        }
138
139
        $parameters['content'] = $content;
140
        $parameters['user'] = $user;
141
142
        return $this->view->renderFile(
143
            $layout,
144
            $parameters,
145
            $this,
146
        );
147
    }
148
149
    private function findLayoutFile(?string $file): ?string
150
    {
151
        if ($file === null) {
152
            return null;
153
        }
154
155
        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
156
            return $file;
157
        }
158
159
        return $file . '.' . $this->view->getDefaultExtension();
160
    }
161
162
    /**
163
     * Returns the controller name. Name should be converted to "id" case.
164
     * Method returns classname without `controller` on the ending.
165
     * If namespace is not contain `controller` or `controllers`
166
     * then returns only classname without `controller` on the ending
167
     * else returns all subnamespaces from `controller` (or `controllers`) to the end
168
     *
169
     * @return string
170
     * @example App\Controller\FooBar\BazController -> foo-bar/baz
171
     * @example App\Controllers\FooBar\BazController -> foo-bar/baz
172
     * @example Path\To\File\BlogController -> blog
173
     * @see Inflector::camel2id()
174
     */
175
    private function getName(object $controller): string
176
    {
177
        if ($this->name !== null) {
178
            return $this->name;
179
        }
180
181
        $regexp = '/((?<=controller\\\|s\\\)(?:[\w\\\]+)|(?:[a-z]+))controller/iuU';
182
        if (!preg_match($regexp, get_class($controller), $m) || empty($m[1])) {
183
            throw new \RuntimeException('Cannot detect controller name');
184
        }
185
186
        $inflector = new Inflector();
187
        $name = str_replace('\\', '/', $m[1]);
188
189
        return $this->name = $inflector->camel2id($name);
190
    }
191
192
    private function getCsrf(): string
193
    {
194
        return $this->urlMatcher->getLastMatchedRequest()->getAttribute('csrf_token');
195
    }
196
}
197