Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.68% covered (warning)
75.68%
28 / 37
66.67% covered (warning)
66.67%
6 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ControllerBase
75.68% covered (warning)
75.68%
28 / 37
66.67% covered (warning)
66.67%
6 / 9
15.43
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 getSkipBaseName
n/a
0 / 0
n/a
0 / 0
0
 createLogic
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 getResponseHeaders
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 redirectUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 redirectPath
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 createViewActionResult
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 viewWithController
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 view
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 data
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\Mvc;
6
7use PeServer\Core\Http\HttpRequest;
8use PeServer\Core\Http\HttpStatus;
9use PeServer\Core\Log\ILogger;
10use PeServer\Core\Log\ILoggerFactory;
11use PeServer\Core\Log\Logging;
12use PeServer\Core\Mvc\ControllerArgument;
13use PeServer\Core\Mvc\DataContent;
14use PeServer\Core\Mvc\LogicBase;
15use PeServer\Core\Mvc\LogicParameter;
16use PeServer\Core\Mvc\Result\DataActionResult;
17use PeServer\Core\Mvc\Result\IActionResult;
18use PeServer\Core\Mvc\Result\RedirectActionResult;
19use PeServer\Core\Mvc\Result\ViewActionResult;
20use PeServer\Core\Mvc\Template\ITemplateFactory;
21use PeServer\Core\Mvc\Template\TemplateParameter;
22use PeServer\Core\ProgramContext;
23use PeServer\Core\ReflectionUtility;
24use PeServer\Core\Store\Stores;
25use PeServer\Core\Text;
26use PeServer\Core\Throws\InvalidOperationException;
27use PeServer\Core\Web\IUrlHelper;
28use PeServer\Core\Web\Url;
29use PeServer\Core\Web\UrlPath;
30use PeServer\Core\Web\UrlQuery;
31use PeServer\Core\Web\UrlUtility;
32use PeServer\Core\Web\WebSecurity;
33
34/**
35 * コントローラ基底処理。
36 */
37abstract 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}