1 <?php
2
3 namespace Docolight\Http;
4
5 use ArrayAccess;
6 use CApplicationComponent;
7
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
45 class Response extends CApplicationComponent implements ArrayAccess
46 {
47 48 49
50 protected $status = 200;
51
52 53 54
55 public $headers = null;
56
57 58 59
60 protected $body = '';
61
62 63 64
65 protected $length = 0;
66
67 68 69
70 protected static $messages = array(
71
72 100 => '100 Continue',
73 101 => '101 Switching Protocols',
74 102 => '102 Processing',
75
76 200 => '200 OK',
77 201 => '201 Created',
78 202 => '202 Accepted',
79 203 => '203 Non-Authoritative Information',
80 204 => '204 No Content',
81 205 => '205 Reset Content',
82 206 => '206 Partial Content',
83 207 => '207 Multi-Status',
84 208 => '208 Already Reported',
85 226 => '226 IM Used',
86
87 300 => '300 Multiple Choices',
88 301 => '301 Moved Permanently',
89 302 => '302 Found',
90 303 => '303 See Other',
91 304 => '304 Not Modified',
92 305 => '305 Use Proxy',
93 306 => '306 (Unused)',
94 307 => '307 Temporary Redirect',
95 308 => '308 Permanent Redirect',
96
97 400 => '400 Bad Request',
98 401 => '401 Unauthorized',
99 402 => '402 Payment Required',
100 403 => '403 Forbidden',
101 404 => '404 Not Found',
102 405 => '405 Method Not Allowed',
103 406 => '406 Not Acceptable',
104 407 => '407 Proxy Authentication Required',
105 408 => '408 Request Timeout',
106 409 => '409 Conflict',
107 410 => '410 Gone',
108 411 => '411 Length Required',
109 412 => '412 Precondition Failed',
110 413 => '413 Request Entity Too Large',
111 414 => '414 Request-URI Too Long',
112 415 => '415 Unsupported Media Type',
113 416 => '416 Requested Range Not Satisfiable',
114 417 => '417 Expectation Failed',
115 418 => '418 I\'m a teapot',
116 419 => '419 Authentication Timeout',
117 420 => '420 Method Failure',
118 421 => '421 Misdirected Request',
119 422 => '422 Unprocessable Entity',
120 423 => '423 Locked',
121 424 => '424 Failed Dependency',
122 426 => '426 Upgrade Required',
123 428 => '428 Precondition Required',
124 429 => '429 Too Many Requests',
125 431 => '431 Request Header Fields Too Large',
126 451 => '451 Unavailable For Legal Reasons',
127
128 500 => '500 Internal Server Error',
129 501 => '501 Not Implemented',
130 502 => '502 Bad Gateway',
131 503 => '503 Service Unavailable',
132 504 => '504 Gateway Timeout',
133 505 => '505 HTTP Version Not Supported',
134 506 => '506 Variant Also Negotiates',
135 507 => '507 Insufficient Storage',
136 508 => '508 Loop Detected',
137 509 => '509 Bandwidth Limit Exceeded',
138 510 => '510 Not Extended',
139 511 => '511 Network Authentication Required',
140 520 => '520 Unknown Error',
141 598 => '598 Network Read Timeout Error',
142 599 => '599 Network Connect Timeout Error',
143 );
144
145 146 147 148 149
150 public function __construct(Headers $headers)
151 {
152 $this->headers = $headers;
153
154 $this->init();
155 }
156
157 158 159 160 161
162 public function getStatus()
163 {
164 return $this->status;
165 }
166
167 168 169 170 171 172 173
174 public function setStatus($status)
175 {
176 $this->status = (int) $status;
177
178 return $this;
179 }
180
181 182 183 184 185
186 public function getBody()
187 {
188 return $this->body;
189 }
190
191 192 193 194 195 196 197
198 public function setBody($content)
199 {
200 $this->write($content, true);
201
202 return $this;
203 }
204
205 206 207 208 209 210 211 212
213 public function write($body, $replace = false)
214 {
215 $body = (string) $body;
216
217 if ($replace) {
218 $this->body = $body;
219 } else {
220 $this->body .= $body;
221 }
222
223 $this->length = strlen($this->body);
224
225 return $this;
226 }
227
228 229 230 231 232
233 public function getLength()
234 {
235 return $this->length;
236 }
237
238 239 240 241 242 243
244 public function finalize()
245 {
246
247 if (in_array($this->status, array(204, 304))) {
248 $this->headers->remove('Content-Type');
249 $this->headers->remove('Content-Length');
250 $this->setBody($this->getEmpty());
251 }
252
253 return array($this->status, $this->headers, $this->getBody());
254 }
255
256 257 258 259 260
261 public function send()
262 {
263 list($status, $headers, $body) = $this->finalize();
264
265 $this->prepareHeaders($status, $headers);
266 $this->sendBody($body);
267 }
268
269 270 271 272 273 274 275
276 protected function sendBody($body)
277 {
278 if (trim(strtoupper(def($_SERVER, 'REQUEST_METHOD', ''))) !== 'HEAD') {
279 echo $body;
280 }
281 }
282
283 284 285 286 287 288 289 290
291 protected function prepareHeaders($status, Headers $headers)
292 {
293 if (headers_sent() === false) {
294
295 if (strpos(PHP_SAPI, 'cgi') === 0) {
296 header(sprintf('Status: %s', static::getMessageForCode($status)));
297 } else {
298 header(sprintf('HTTP/%s %s', '1.1', static::getMessageForCode($status)));
299 }
300
301
302 foreach ($headers as $name => $header) {
303 foreach (explode("\n", $header) as $value) {
304 header("$name: $value", false);
305 }
306 }
307 }
308 }
309
310 311 312 313 314 315 316 317 318 319 320
321 public function redirect($url, $status = 302)
322 {
323 $this->setStatus($status);
324 $this->headers->set('Location', $url);
325
326 return $this;
327 }
328
329 330 331 332 333
334 public function offsetExists($offset)
335 {
336 return isset($this->headers[$offset]);
337 }
338
339 340 341 342 343 344 345
346 public function offsetGet($offset)
347 {
348 return $this->headers[$offset];
349 }
350
351 352 353 354 355 356 357 358
359 public function offsetSet($offset, $value)
360 {
361 $this->headers[$offset] = $value;
362 }
363
364 365 366 367 368 369 370
371 public function offsetUnset($offset)
372 {
373 unset($this->headers[$offset]);
374 }
375
376 377 378 379 380 381 382
383 public static function getMessageForCode($status)
384 {
385 $status = (int) $status;
386
387 return isset(self::$messages[$status]) ? self::$messages[$status] : null;
388 }
389
390 391 392 393 394 395
396 public static function setMessage($status, $message)
397 {
398 $status = (int) $status;
399
400 static::$messages[$status] = $messages;
401 }
402
403 404 405 406 407
408 public function init()
409 {
410 parent::init();
411 }
412
413 414 415 416 417
418 protected function getEmpty()
419 {
420 return '';
421 }
422 }
423