Passed
Pull Request — master (#118)
by Dmitriy
11:28
created

ViewRenderer::getCsrf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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