bzr branch
http://bzr.ed.am/www/slight
1
by Tim Marston
initial commit |
1 |
<?php |
2 |
/** |
|
3 |
* Flight: An extensible micro-framework. |
|
4 |
* |
|
5 |
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com> |
|
6 |
* @license MIT, http://flightphp.com/license |
|
7 |
*/ |
|
8 |
||
9 |
namespace flight\net; |
|
10 |
||
11 |
/** |
|
12 |
* The Response class represents an HTTP response. The object |
|
13 |
* contains the response headers, HTTP status code, and response |
|
14 |
* body. |
|
15 |
*/ |
|
16 |
class Response { |
|
17 |
/** |
|
18 |
* @var int HTTP status |
|
19 |
*/ |
|
20 |
protected $status = 200; |
|
21 |
||
22 |
/** |
|
23 |
* @var array HTTP headers |
|
24 |
*/ |
|
25 |
protected $headers = array(); |
|
26 |
||
27 |
/** |
|
28 |
* @var string HTTP response body |
|
29 |
*/ |
|
30 |
protected $body; |
|
31 |
||
32 |
/** |
|
33 |
* @var array HTTP status codes |
|
34 |
*/ |
|
35 |
public static $codes = array( |
|
36 |
100 => 'Continue', |
|
37 |
101 => 'Switching Protocols', |
|
38 |
102 => 'Processing', |
|
39 |
||
40 |
200 => 'OK', |
|
41 |
201 => 'Created', |
|
42 |
202 => 'Accepted', |
|
43 |
203 => 'Non-Authoritative Information', |
|
44 |
204 => 'No Content', |
|
45 |
205 => 'Reset Content', |
|
46 |
206 => 'Partial Content', |
|
47 |
207 => 'Multi-Status', |
|
48 |
208 => 'Already Reported', |
|
49 |
||
50 |
226 => 'IM Used', |
|
51 |
||
52 |
300 => 'Multiple Choices', |
|
53 |
301 => 'Moved Permanently', |
|
54 |
302 => 'Found', |
|
55 |
303 => 'See Other', |
|
56 |
304 => 'Not Modified', |
|
57 |
305 => 'Use Proxy', |
|
58 |
306 => '(Unused)', |
|
59 |
307 => 'Temporary Redirect', |
|
60 |
308 => 'Permanent Redirect', |
|
61 |
||
62 |
400 => 'Bad Request', |
|
63 |
401 => 'Unauthorized', |
|
64 |
402 => 'Payment Required', |
|
65 |
403 => 'Forbidden', |
|
66 |
404 => 'Not Found', |
|
67 |
405 => 'Method Not Allowed', |
|
68 |
406 => 'Not Acceptable', |
|
69 |
407 => 'Proxy Authentication Required', |
|
70 |
408 => 'Request Timeout', |
|
71 |
409 => 'Conflict', |
|
72 |
410 => 'Gone', |
|
73 |
411 => 'Length Required', |
|
74 |
412 => 'Precondition Failed', |
|
75 |
413 => 'Payload Too Large', |
|
76 |
414 => 'URI Too Long', |
|
77 |
415 => 'Unsupported Media Type', |
|
78 |
416 => 'Range Not Satisfiable', |
|
79 |
417 => 'Expectation Failed', |
|
80 |
||
81 |
422 => 'Unprocessable Entity', |
|
82 |
423 => 'Locked', |
|
83 |
424 => 'Failed Dependency', |
|
84 |
||
85 |
426 => 'Upgrade Required', |
|
86 |
||
87 |
428 => 'Precondition Required', |
|
88 |
429 => 'Too Many Requests', |
|
89 |
||
90 |
431 => 'Request Header Fields Too Large', |
|
91 |
||
92 |
500 => 'Internal Server Error', |
|
93 |
501 => 'Not Implemented', |
|
94 |
502 => 'Bad Gateway', |
|
95 |
503 => 'Service Unavailable', |
|
96 |
504 => 'Gateway Timeout', |
|
97 |
505 => 'HTTP Version Not Supported', |
|
98 |
506 => 'Variant Also Negotiates', |
|
99 |
507 => 'Insufficient Storage', |
|
100 |
508 => 'Loop Detected', |
|
101 |
||
102 |
510 => 'Not Extended', |
|
103 |
511 => 'Network Authentication Required' |
|
104 |
); |
|
105 |
||
106 |
/** |
|
107 |
* Sets the HTTP status of the response. |
|
108 |
* |
|
109 |
* @param int $code HTTP status code. |
|
110 |
* @return object Self reference |
|
111 |
* @throws \Exception If invalid status code |
|
112 |
*/ |
|
113 |
public function status($code = null) { |
|
114 |
if ($code === null) { |
|
115 |
return $this->status; |
|
116 |
} |
|
117 |
||
118 |
if (array_key_exists($code, self::$codes)) { |
|
119 |
$this->status = $code; |
|
120 |
} |
|
121 |
else { |
|
122 |
throw new \Exception('Invalid status code.'); |
|
123 |
} |
|
124 |
||
125 |
return $this; |
|
126 |
} |
|
127 |
||
128 |
/** |
|
129 |
* Adds a header to the response. |
|
130 |
* |
|
131 |
* @param string|array $name Header name or array of names and values |
|
132 |
* @param string $value Header value |
|
133 |
* @return object Self reference |
|
134 |
*/ |
|
135 |
public function header($name, $value = null) { |
|
136 |
if (is_array($name)) { |
|
137 |
foreach ($name as $k => $v) { |
|
138 |
$this->headers[$k] = $v; |
|
139 |
} |
|
140 |
} |
|
141 |
else { |
|
142 |
$this->headers[$name] = $value; |
|
143 |
} |
|
144 |
||
145 |
return $this; |
|
146 |
} |
|
147 |
||
148 |
/** |
|
149 |
* Returns the headers from the response |
|
150 |
* @return array |
|
151 |
*/ |
|
152 |
public function headers() { |
|
153 |
return $this->headers; |
|
154 |
} |
|
155 |
||
156 |
/** |
|
157 |
* Writes content to the response body. |
|
158 |
* |
|
159 |
* @param string $str Response content |
|
160 |
* @return object Self reference |
|
161 |
*/ |
|
162 |
public function write($str) { |
|
163 |
$this->body .= $str; |
|
164 |
||
165 |
return $this; |
|
166 |
} |
|
167 |
||
168 |
/** |
|
169 |
* Clears the response. |
|
170 |
* |
|
171 |
* @return object Self reference |
|
172 |
*/ |
|
173 |
public function clear() { |
|
174 |
$this->status = 200; |
|
175 |
$this->headers = array(); |
|
176 |
$this->body = ''; |
|
177 |
||
178 |
return $this; |
|
179 |
} |
|
180 |
||
181 |
/** |
|
182 |
* Sets caching headers for the response. |
|
183 |
* |
|
184 |
* @param int|string $expires Expiration time |
|
185 |
* @return object Self reference |
|
186 |
*/ |
|
187 |
public function cache($expires) { |
|
188 |
if ($expires === false) { |
|
189 |
$this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; |
|
190 |
$this->headers['Cache-Control'] = array( |
|
191 |
'no-store, no-cache, must-revalidate', |
|
192 |
'post-check=0, pre-check=0', |
|
193 |
'max-age=0' |
|
194 |
); |
|
195 |
$this->headers['Pragma'] = 'no-cache'; |
|
196 |
} |
|
197 |
else { |
|
198 |
$expires = is_int($expires) ? $expires : strtotime($expires); |
|
199 |
$this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT'; |
|
200 |
$this->headers['Cache-Control'] = 'max-age='.($expires - time()); |
|
201 |
if (isset($this->headers['Pragma']) && $this->headers['Pragma'] == 'no-cache'){ |
|
202 |
unset($this->headers['Pragma']); |
|
203 |
} |
|
204 |
} |
|
205 |
return $this; |
|
206 |
} |
|
207 |
||
208 |
/** |
|
209 |
* Sends HTTP headers. |
|
210 |
* |
|
211 |
* @return object Self reference |
|
212 |
*/ |
|
213 |
public function sendHeaders() { |
|
214 |
// Send status code header |
|
215 |
if (strpos(php_sapi_name(), 'cgi') !== false) { |
|
216 |
header( |
|
217 |
sprintf( |
|
218 |
'Status: %d %s', |
|
219 |
$this->status, |
|
220 |
self::$codes[$this->status] |
|
221 |
), |
|
222 |
true |
|
223 |
); |
|
224 |
} |
|
225 |
else { |
|
226 |
header( |
|
227 |
sprintf( |
|
228 |
'%s %d %s', |
|
229 |
(isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'), |
|
230 |
$this->status, |
|
231 |
self::$codes[$this->status]), |
|
232 |
true, |
|
233 |
$this->status |
|
234 |
); |
|
235 |
} |
|
236 |
||
237 |
// Send other headers |
|
238 |
foreach ($this->headers as $field => $value) { |
|
239 |
if (is_array($value)) { |
|
240 |
foreach ($value as $v) { |
|
241 |
header($field.': '.$v, false); |
|
242 |
} |
|
243 |
} |
|
244 |
else { |
|
245 |
header($field.': '.$value); |
|
246 |
} |
|
247 |
} |
|
248 |
||
249 |
// Send content length |
|
250 |
if (($length = strlen($this->body)) > 0) { |
|
251 |
header('Content-Length: '.$length); |
|
252 |
} |
|
253 |
||
254 |
return $this; |
|
255 |
} |
|
256 |
||
257 |
/** |
|
258 |
* Sends a HTTP response. |
|
259 |
*/ |
|
260 |
public function send() { |
|
261 |
if (ob_get_length() > 0) { |
|
262 |
ob_end_clean(); |
|
263 |
} |
|
264 |
||
265 |
if (!headers_sent()) { |
|
266 |
$this->sendHeaders(); |
|
267 |
} |
|
268 |
||
269 |
exit($this->body); |
|
270 |
} |
|
271 |
} |
|
272 |