Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.39% covered (danger)
48.39%
15 / 31
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResponsePrinter
48.39% covered (danger)
48.39%
15 / 31
75.00% covered (warning)
75.00%
3 / 4
51.20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getContentLength
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 output
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 execute
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2
3declare(strict_types=1);
4
5namespace PeServer\Core\Http;
6
7use PeServer\Core\Binary;
8use PeServer\Core\Http\HttpMethod;
9use PeServer\Core\Http\HttpResponse;
10use PeServer\Core\Http\HttpStatus;
11use PeServer\Core\Http\ICallbackContent;
12use PeServer\Core\Text;
13
14/**
15 * HTTPレスポンス出力処理。
16 *
17 * 本クラス処理前後(execute前後)には何も出力しないのがお行儀良い処理。
18 */
19class ResponsePrinter
20{
21    /**
22     * 生成。
23     *
24     * @param HttpRequest $request
25     * @param HttpResponse $response
26     */
27    public function __construct(
28        protected readonly HttpRequest $request,
29        protected readonly HttpResponse $response
30    ) {
31    }
32
33    #region function
34
35    /**
36     * 応答ヘッダ: Content-Length を取得。
37     *
38     * @return int 0以上の場合は決定された出力byte数。負数は不明。
39     * @phpstan-return non-negative-int|ICallbackContent::UNKNOWN
40     */
41    private function getContentLength(): int
42    {
43        if ($this->response->content instanceof ICallbackContent) {
44            $length = $this->response->content->getLength();
45            if (0 <= $length) {
46                return $length;
47            }
48        } elseif ($this->response->content instanceof Binary) {
49            return $this->response->content->count();
50        } elseif (is_string($this->response->content)) {
51            return Text::getByteCount($this->response->content);
52        }
53
54        return ICallbackContent::UNKNOWN;
55    }
56
57    /**
58     * 応答本文出力処理。
59     */
60    private function output(): void
61    {
62        if ($this->response->content instanceof ICallbackContent) {
63            // 処理は自分で出力を頑張ること
64            $this->response->content->output();
65        } elseif ($this->response->content instanceof Binary) {
66            echo $this->response->content->raw;
67        } else {
68            echo $this->response->content;
69        }
70    }
71
72    /**
73     * 応答出力。
74     *
75     * @return void
76     */
77    public function execute(): void
78    {
79        // リダイレクト未設定の場合はステータスコード設定
80        if (!$this->response->header->existsRedirect()) {
81            http_response_code($this->response->status->value);
82        }
83
84        // 設定済みヘッダ出力
85        foreach ($this->response->header->getHeaders() as $name => $value) {
86            header($name . ': ' . $value);
87        }
88
89        if ($this->response->header->existsRedirect()) {
90            $redirect = $this->response->header->getRedirect();
91            if ($redirect->status === HttpStatus::MovedPermanently) {
92                header('Location: ' . $redirect->url->toString());
93            } else {
94                header('Location: ' . $redirect->url->toString(), true, $redirect->status->value);
95            }
96            return;
97        }
98
99        // ヘッダ: Content-Length
100        $contentLength = $this->getContentLength();
101        if (0 <= $contentLength) {
102            header('Content-Length: ' . $contentLength);
103        }
104
105        if ($this->request->httpMethod === HttpMethod::Head) {
106            // HEAD 処理は出力を抑制
107            return;
108        }
109
110        $this->output();
111    }
112
113    #endregion
114}