Passed
Push — master ( b817d9...04ae87 )
by 世昌
02:23
created

HTTPResponse::closeConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 3
rs 10
1
<?php
2
namespace suda\framework\http;
3
4
use Exception;
5
use function header as send_header;
6
use suda\framework\http\stream\DataStream;
7
8
/**
9
 * 原始HTTP响应
10
 */
11
class HTTPResponse implements Response
12
{
13
    /**
14
     * 头部代码
15
     *
16
     * @var HeaderContainer
17
     */
18
    protected $header;
19
20
    /**
21
     * 状态码
22
     *
23
     * @var int
24
     */
25
    protected $status = 200;
26
27
    /**
28
     * 响应版本
29
     *
30
     * @var string
31
     */
32
    protected $version = '1.1';
33
34
    /**
35
     * Cookie代码
36
     *
37
     * @var Cookie[]
38
     */
39
    protected $cookie;
40
41
    /**
42
     * 是否发送
43
     *
44
     * @var boolean
45
     */
46
    protected $send = false;
47
48
    public function __construct()
49
    {
50
        $this->cookie = [];
51
        $this->header = new HeaderContainer;
52
    }
53
54
    /**
55
     * 设置状态码
56
     *
57
     * @param integer $statusCode
58
     * @return $this
59
     */
60
    public function status(int $statusCode)
61
    {
62
        $this->status = $statusCode;
63
        return $this;
64
    }
65
66
    /**
67
     * 设置响应版本
68
     *
69
     * @param string $version
70
     * @return $this
71
     */
72
    public function version(string $version)
73
    {
74
        $this->version = $version;
75
        return $this;
76
    }
77
78
    /**
79
     * 判断是否发送
80
     *
81
     * @return boolean
82
     */
83
    public function isSend(): bool
84
    {
85
        return $this->send || headers_sent();
86
    }
87
88
    /**
89
     * 设置头部信息
90
     *
91
     * @param string $name
92
     * @param string $value
93
     * @param bool $replace
94
     * @param bool $ucfirst
95
     * @return $this
96
     */
97
    public function header(string $name, string $value, bool $replace = false, bool $ucfirst = true)
98
    {
99
        $this->header->add(new Header($name, $value, $ucfirst), $replace);
100
        return $this;
101
    }
102
103
    /**
104
     * 设置Cookie信息
105
     *
106
     * @param Cookie $cookie
107
     * @return $this
108
     */
109
    public function cookie(Cookie $cookie)
110
    {
111
        $this->cookie[$cookie->getName()] = $cookie;
112
        return $this;
113
    }
114
115
    /**
116
     * 写数据
117
     *
118
     * @param Stream|string $data
119
     * @return void
120
     */
121
    public function write($data)
122
    {
123
        $this->sendHeaders();
124
        $this->writeOutput($data);
125
    }
126
127
    /**
128
     * 发送数据
129
     *
130
     * @param Stream|string $data
131
     * @return void
132
     */
133
    public function send($data)
134
    {
135
        $this->sendHeaders();
136
        $this->writeOutput($data);
137
        $this->closeConnection();
138
    }
139
140
    /**
141
     * 发送文件内容
142
     *
143
     * @param string $filename
144
     * @param integer $offset
145
     * @param integer $length
146
     * @return void
147
     * @throws Exception
148
     */
149
    public function sendFile(string $filename, int $offset = 0, int $length = null)
150
    {
151
        if (!file_exists($filename)) {
152
            throw new Exception('file no found: '.$filename);
153
        }
154
        $data = new DataStream($filename, $offset, $length);
155
        $this->sendHeaders();
156
        $this->writeOutput($data);
157
        $this->closeConnection();
158
    }
159
160
    /**
161
     * 跳转
162
     *
163
     * @param string $url
164
     * @param integer $httpCode
165
     * @return void
166
     */
167
    public function redirect(string $url, int $httpCode = 302)
168
    {
169
        $this->header('Location', $url);
170
        $this->status($httpCode);
171
    }
172
173
    /**
174
     * 请求结束处理
175
     *
176
     * @return void
177
     */
178
    public function end()
179
    {
180
        $this->sendHeaders();
181
        $this->closeConnection();
182
        $this->send = true;
183
    }
184
185
    /**
186
     * @return $this
187
     */
188
    protected function sendHeaders()
189
    {
190
        if ($this->isSend()) {
191
            return $this;
192
        }
193
        $this->prepareCookieHeader();
194
        $this->sendHeader();
195
        $this->send = true;
196
        return $this;
197
    }
198
199
    /**
200
     * 发送头部信息
201
     *
202
     * @return $this
203
     */
204
    private function sendHeader()
205
    {
206
        foreach ($this->header->all() as $name => $values) {
207
            foreach ($values as $header) {
208
                send_header($header, false, $this->status);
209
            }
210
        }
211
        send_header(sprintf(
212
            'HTTP/%s %s %s',
213
            $this->version,
214
            $this->status,
215
            Status::toText($this->status)
216
        ), true, $this->status);
217
        return $this;
218
    }
219
220
    /**
221
     * 准备Cookie头
222
     *
223
     * @return $this
224
     */
225
    private function prepareCookieHeader()
226
    {
227
        foreach ($this->cookie as $cookie) {
228
            $cookie->send($this);
229
        }
230
        return $this;
231
    }
232
233
    /**
234
     * 关闭所有缓存
235
     *
236
     * @param boolean $flush
237
     * @return void
238
     */
239
    protected function closeOutputBuffer(bool $flush)
240
    {
241
        while (ob_get_level() > 0) {
242
            $flush?ob_end_flush():ob_end_clean();
243
        }
244
    }
245
246
    /**
247
     * 发送数据内容
248
     *
249
     * @param Stream|string $data
250
     * @return void
251
     */
252
    protected function writeOutput($data)
253
    {
254
        $this->closeOutputBuffer(false);
255
        if ($data instanceof Stream) {
256
            $data->echo();
257
        } else {
258
            echo $data;
259
        }
260
    }
261
262
    protected function closeConnection() {
263
        if (function_exists('fastcgi_finish_request')) {
264
            fastcgi_finish_request();
265
        }
266
    }
267
}
268