Passed
Pull Request — master (#115)
by Dmitriy
15:25
created

ViewRenderer::getName()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

48
        return $this->aliases->get(/** @scrutinizer ignore-type */ $this->viewBasePath) . '/' . $this->name;
Loading history...
49
    }
50
51
    public function render(string $view, array $parameters = []): ResponseInterface
52
    {
53
        $contentRenderer = fn() => $this->renderProxy($view, $parameters);
54
55
        return $this->responseFactory->createResponse($contentRenderer);
56
    }
57
58
    public function renderPartial(string $view, array $parameters = []): ResponseInterface
59
    {
60
        $content = $this->view->render($view, $parameters, $this);
61
62
        return $this->responseFactory->createResponse($content);
63
    }
64
65
    public function withController(object $controller): self
66
    {
67
        $new = clone $this;
68
        $new->name = $this->getName($controller);
69
70
        return $new;
71
    }
72
73
    public function withControllerName(string $name): self
74
    {
75
        $new = clone $this;
76
        $new->name = $name;
77
78
        return $new;
79
    }
80
81
    public function withViewPath(string $viewPath): self
82
    {
83
        $new = clone $this;
84
        $new->viewPath = $viewPath;
85
86
        return $new;
87
    }
88
89
    public function withViewBasePath(string $viewBasePath): self
90
    {
91
        $new = clone $this;
92
        $new->viewBasePath = $viewBasePath;
93
94
        return $new;
95
    }
96
97
    public function withLayout(string $layout): self
98
    {
99
        $new = clone $this;
100
        $new->layout = $layout;
101
102
        return $new;
103
    }
104
105
    private function renderProxy(string $view, array $parameters = []): string
106
    {
107
        $content = $this->view->render($view, $parameters, $this);
108
        $user = $this->user->getIdentity();
109
        $layout = $this->findLayoutFile($this->aliases->get($this->layout));
110
111
        if ($layout === null) {
0 ignored issues
show
introduced by
The condition $layout === null is always false.
Loading history...
112
            return $content;
113
        }
114
        return $this->view->renderFile(
115
            $layout,
116
            [
117
                'content' => $content,
118
                'user' => $user,
119
            ],
120
            $this
121
        );
122
    }
123
124
    private function findLayoutFile(?string $file): ?string
125
    {
126
        if ($file === null) {
127
            return null;
128
        }
129
130
        if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
131
            return $file;
132
        }
133
134
        return $file . '.' . $this->view->getDefaultExtension();
135
    }
136
137
    /**
138
     * Returns the controller name. Name should be converted to "id" case.
139
     * Method returns classname without `controller` on the ending.
140
     * If namespace is not contain `controller` or `controllers`
141
     * then returns only classname without `controller` on the ending
142
     * else returns all subnamespaces from `controller` (or `controllers`) to the end
143
     *
144
     * @return string
145
     * @example App\Controller\FooBar\BazController -> foo-bar/baz
146
     * @example App\Controllers\FooBar\BazController -> foo-bar/baz
147
     * @example Path\To\File\BlogController -> blog
148
     * @see Inflector::camel2id()
149
     */
150
    private function getName(object $controller): string
151
    {
152
        if ($this->name !== null) {
153
            return $this->name;
154
        }
155
156
        $regexp = '/((?<=controller\\\|s\\\)(?:[\w\\\]+)|(?:[a-z]+))controller/iuU';
157
        if (!preg_match($regexp, get_class($controller), $m) || empty($m[1])) {
158
            throw new \RuntimeException('Cannot detect controller name');
159
        }
160
161
        $inflector = new Inflector();
162
        $name = str_replace('\\', '/', $m[1]);
163
164
        return $this->name = $inflector->camel2id($name);
165
    }
166
}
167