Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.68% |
28 / 37 |
|
66.67% |
6 / 9 |
CRAP | |
0.00% |
0 / 1 |
ControllerBase | |
75.68% |
28 / 37 |
|
66.67% |
6 / 9 |
15.43 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
getSkipBaseName | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
createLogic | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
getResponseHeaders | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
redirectUrl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
redirectPath | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
createViewActionResult | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
viewWithController | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
view | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
data | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\Mvc; |
6 | |
7 | use PeServer\Core\Http\HttpRequest; |
8 | use PeServer\Core\Http\HttpStatus; |
9 | use PeServer\Core\Log\ILogger; |
10 | use PeServer\Core\Log\ILoggerFactory; |
11 | use PeServer\Core\Log\Logging; |
12 | use PeServer\Core\Mvc\ControllerArgument; |
13 | use PeServer\Core\Mvc\DataContent; |
14 | use PeServer\Core\Mvc\LogicBase; |
15 | use PeServer\Core\Mvc\LogicParameter; |
16 | use PeServer\Core\Mvc\Result\DataActionResult; |
17 | use PeServer\Core\Mvc\Result\IActionResult; |
18 | use PeServer\Core\Mvc\Result\RedirectActionResult; |
19 | use PeServer\Core\Mvc\Result\ViewActionResult; |
20 | use PeServer\Core\Mvc\Template\ITemplateFactory; |
21 | use PeServer\Core\Mvc\Template\TemplateParameter; |
22 | use PeServer\Core\ProgramContext; |
23 | use PeServer\Core\ReflectionUtility; |
24 | use PeServer\Core\Store\Stores; |
25 | use PeServer\Core\Text; |
26 | use PeServer\Core\Throws\InvalidOperationException; |
27 | use PeServer\Core\Web\IUrlHelper; |
28 | use PeServer\Core\Web\Url; |
29 | use PeServer\Core\Web\UrlPath; |
30 | use PeServer\Core\Web\UrlQuery; |
31 | use PeServer\Core\Web\UrlUtility; |
32 | use PeServer\Core\Web\WebSecurity; |
33 | |
34 | /** |
35 | * コントローラ基底処理。 |
36 | */ |
37 | abstract class ControllerBase |
38 | { |
39 | #region variable |
40 | |
41 | /** |
42 | * ロガー。 |
43 | */ |
44 | protected readonly ILogger $logger; |
45 | |
46 | /** |
47 | * ロガー生成器。 |
48 | */ |
49 | protected readonly ILoggerFactory $loggerFactory; |
50 | |
51 | protected readonly Stores $stores; |
52 | protected readonly ProgramContext $programContext; |
53 | protected readonly ILogicFactory $logicFactory; |
54 | protected readonly ITemplateFactory $templateFactory; |
55 | protected readonly IUrlHelper $urlHelper; |
56 | protected readonly WebSecurity $webSecurity; |
57 | |
58 | /** コントローラ内で今輝いてるロジック。よくないんよなぁ。 */ |
59 | protected ?LogicBase $logic = null; |
60 | |
61 | #endregion |
62 | |
63 | /** |
64 | * 生成。 |
65 | * |
66 | * @param ControllerArgument $argument コントローラ入力値(継承先でも必須となる)。 |
67 | */ |
68 | protected function __construct(ControllerArgument $argument) |
69 | { |
70 | $this->stores = $argument->stores; |
71 | $this->programContext = $argument->programContext; |
72 | $this->logicFactory = $argument->logicFactory; |
73 | $this->templateFactory = $argument->templateFactory; |
74 | $this->urlHelper = $argument->urlHelper; |
75 | $this->webSecurity = $argument->webSecurity; |
76 | $this->logger = $argument->logger; |
77 | $this->loggerFactory = $argument->loggerFactory; |
78 | } |
79 | |
80 | #region function |
81 | |
82 | /** |
83 | * コントローラ完全名からコントローラベース名を取得する際にスキップする文言(文字列長が使用される) |
84 | * |
85 | * @return string |
86 | */ |
87 | abstract protected function getSkipBaseName(): string; |
88 | |
89 | /** |
90 | * ロジック生成処理。 |
91 | * |
92 | * @param class-string<LogicBase> $logicClass ロジック完全名。 |
93 | * @param array<int|string,mixed> $arguments |
94 | * @return LogicBase |
95 | */ |
96 | protected function createLogic(string $logicClass, array $arguments = []): LogicBase |
97 | { |
98 | if ($this->logic !== null) { |
99 | throw new InvalidOperationException(); |
100 | } |
101 | |
102 | $logic = $this->logicFactory->createLogic($logicClass, $arguments); |
103 | |
104 | $this->logic = $logic; |
105 | return $logic; |
106 | } |
107 | |
108 | /** |
109 | * ロジック側で生成された応答ヘッダを取得。 |
110 | * |
111 | * @return array<non-empty-string,string[]> 応答ヘッダ。ロジック未生成の場合は空の応答ヘッダを返す。 |
112 | */ |
113 | private function getResponseHeaders(): array |
114 | { |
115 | /** @var array<non-empty-string,string[]> */ |
116 | $headers = []; |
117 | |
118 | if ($this->logic !== null) { |
119 | $headers = $this->logic->getResponseHeaders(); |
120 | } |
121 | |
122 | return $headers; |
123 | } |
124 | |
125 | /** |
126 | * URLリダイレクト。 |
127 | * |
128 | * @param Url $url |
129 | * @return RedirectActionResult |
130 | */ |
131 | protected function redirectUrl(Url $url): RedirectActionResult |
132 | { |
133 | return new RedirectActionResult($url, HttpStatus::Found); |
134 | } |
135 | |
136 | /** |
137 | * ドメイン内でリダイレクト。 |
138 | * 基本的にこれを使っておけばいいが、ドメイン周りはそれっぽく取得しているだけなので正確に対応するなら継承先でいい感じにすること。 |
139 | * |
140 | * @param UrlPath|string $path 行先。 |
141 | * @param UrlQuery|null $query 付与するクエリ。 |
142 | * @return RedirectActionResult |
143 | */ |
144 | protected function redirectPath(UrlPath|string $path, ?UrlQuery $query = null): RedirectActionResult |
145 | { |
146 | $url = $this->stores->special->getServerUrl(); |
147 | |
148 | if (is_string($path)) { |
149 | $path = new UrlPath($path); |
150 | } |
151 | $url = $url->changePath($path); |
152 | |
153 | if ($query !== null) { |
154 | $url = $url->changeQuery($query); |
155 | } |
156 | |
157 | return $this->redirectUrl($url); |
158 | } |
159 | |
160 | /** |
161 | * Viewを表示。 |
162 | * |
163 | * @param non-empty-string $templateBaseName |
164 | * @param non-empty-string $actionName |
165 | * @param TemplateParameter $templateParameter |
166 | * @param array<non-empty-string,string[]> $headers |
167 | * @param ITemplateFactory $templateFactory |
168 | * @param IUrlHelper $urlHelper |
169 | * @return ViewActionResult |
170 | */ |
171 | protected function createViewActionResult( |
172 | string $templateBaseName, |
173 | string $actionName, |
174 | TemplateParameter $templateParameter, |
175 | array $headers, |
176 | ProgramContext $programContext, |
177 | ITemplateFactory $templateFactory, |
178 | IUrlHelper $urlHelper, |
179 | WebSecurity $webSecurity, |
180 | ): ViewActionResult { |
181 | return new ViewActionResult($templateBaseName, $actionName, $templateParameter, $headers, $programContext, $templateFactory, $urlHelper, $webSecurity); |
182 | } |
183 | |
184 | /** |
185 | * Viewを表示。 |
186 | * |
187 | * @param non-empty-string $controllerName コントローラ完全名。 |
188 | * @param non-empty-string $action アクション名。 |
189 | * @param TemplateParameter $parameter View連携データ。 |
190 | * @return ViewActionResult |
191 | */ |
192 | protected function viewWithController(string $controllerName, string $action, TemplateParameter $parameter): ViewActionResult |
193 | { |
194 | $lastWord = 'Controller'; |
195 | |
196 | $skipBaseName = $this->getSkipBaseName(); |
197 | $index = Text::getPosition($controllerName, $skipBaseName); |
198 | $length = Text::getLength($skipBaseName); |
199 | |
200 | $controllerClassName = Text::substring($controllerName, $index + $length + 1); |
201 | $controllerBaseName = Text::substring($controllerClassName, 0, Text::getLength($controllerClassName) - Text::getLength($lastWord)); |
202 | |
203 | $templateDirPath = Text::replace($controllerBaseName, '\\', DIRECTORY_SEPARATOR); |
204 | assert($templateDirPath); |
205 | |
206 | return $this->createViewActionResult($templateDirPath, $action, $parameter, $this->getResponseHeaders(), $this->programContext, $this->templateFactory, $this->urlHelper, $this->webSecurity); |
207 | } |
208 | |
209 | /** |
210 | * Viewを表示。 |
211 | * |
212 | * `viewWithController` を調整すれば基本的にこれだけ使っておけばよい。 |
213 | * |
214 | * @param non-empty-string $action アクション名。 |
215 | * @param TemplateParameter $parameter View連携データ。 |
216 | * @return ViewActionResult |
217 | */ |
218 | protected function view(string $action, TemplateParameter $parameter): ViewActionResult |
219 | { |
220 | $className = get_class($this); |
221 | |
222 | return $this->viewWithController($className, $action, $parameter); |
223 | } |
224 | |
225 | /** |
226 | * データ応答。 |
227 | * |
228 | * @param DataContent $content |
229 | * @return DataActionResult |
230 | */ |
231 | protected function data(DataContent $content): DataActionResult |
232 | { |
233 | return new DataActionResult($content); |
234 | } |
235 | |
236 | #endregion |
237 | } |