1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * THttpResponse class |
||||
5 | * |
||||
6 | * @author Qiang Xue <[email protected]> |
||||
7 | * @link https://github.com/pradosoft/prado |
||||
8 | * @license https://github.com/pradosoft/prado/blob/master/LICENSE |
||||
9 | */ |
||||
10 | |||||
11 | namespace Prado\Web; |
||||
12 | |||||
13 | use Prado\Exceptions\TExitException; |
||||
14 | use Prado\Exceptions\TInvalidDataValueException; |
||||
15 | use Prado\Exceptions\TInvalidOperationException; |
||||
16 | use Prado\Prado; |
||||
17 | use Prado\TPropertyValue; |
||||
18 | |||||
19 | /** |
||||
20 | * THttpResponse class |
||||
21 | * |
||||
22 | * THttpResponse implements the mechanism for sending output to client users. |
||||
23 | * |
||||
24 | * To output a string to client, use {@see write()}. By default, the output is |
||||
25 | * buffered until {@see flush()} is called or the application ends. The output in |
||||
26 | * the buffer can also be cleaned by {@see clear()}. To disable output buffering, |
||||
27 | * set BufferOutput property to false. |
||||
28 | * |
||||
29 | * To send cookies to client, use {@see getCookies()}. |
||||
30 | * To redirect client browser to a new URL, use {@see redirect()}. |
||||
31 | * To send a file to client, use {@see writeFile()}. |
||||
32 | * |
||||
33 | * By default, THttpResponse is registered with {@see \Prado\TApplication} as the |
||||
34 | * response module. It can be accessed via {@see \Prado\TApplication::getResponse()}. |
||||
35 | * |
||||
36 | * THttpResponse may be configured in application configuration file as follows |
||||
37 | * |
||||
38 | * <module id="response" class="Prado\Web\THttpResponse" CacheExpire="20" CacheControl="nocache" BufferOutput="true" /> |
||||
39 | * |
||||
40 | * where {@see getCacheExpire CacheExpire}, {@see getCacheControl CacheControl} |
||||
41 | * and {@see getBufferOutput BufferOutput} are optional properties of THttpResponse. |
||||
42 | * |
||||
43 | * THttpResponse sends charset header if either {@see setCharset() Charset} |
||||
44 | * or {@see \Prado\I18N\TGlobalization::setCharset() TGlobalization.Charset} is set. |
||||
45 | * |
||||
46 | * Since 3.1.2, HTTP status code can be set with the {@see setStatusCode StatusCode} property. |
||||
47 | * |
||||
48 | * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@see setStatusCode StatusCode} |
||||
49 | * in your application, be sure to add theses informations. |
||||
50 | * E.g : to make an http authentication : |
||||
51 | * ```php |
||||
52 | * public function clickAuth ($sender, $param) |
||||
53 | * { |
||||
54 | * $response=$this->getResponse(); |
||||
55 | * $response->setStatusCode(401); |
||||
56 | * $response->appendHeader('WWW-Authenticate: Basic realm="Test"'); |
||||
57 | * } |
||||
58 | * ``` |
||||
59 | * |
||||
60 | * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This |
||||
61 | * will force the browser to ask for a username and a password. |
||||
62 | * |
||||
63 | * @author Qiang Xue <[email protected]> |
||||
64 | * @since 3.0 |
||||
65 | */ |
||||
66 | class THttpResponse extends \Prado\TModule implements \Prado\IO\ITextWriter |
||||
67 | { |
||||
68 | public const DEFAULT_CONTENTTYPE = 'text/html'; |
||||
69 | public const DEFAULT_CHARSET = 'UTF-8'; |
||||
70 | |||||
71 | /** |
||||
72 | * @var array<int, string> The differents defined status code by RFC 2616 {@see http://www.faqs.org/rfcs/rfc2616} |
||||
73 | */ |
||||
74 | private static $HTTP_STATUS_CODES = [ |
||||
75 | 100 => 'Continue', 101 => 'Switching Protocols', |
||||
76 | 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', |
||||
77 | 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', |
||||
78 | 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', |
||||
79 | 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', |
||||
80 | ]; |
||||
81 | |||||
82 | /** |
||||
83 | * @var bool whether to buffer output |
||||
84 | */ |
||||
85 | private $_bufferOutput = true; |
||||
86 | /** |
||||
87 | * @var bool if the application is initialized |
||||
88 | */ |
||||
89 | private $_initialized = false; |
||||
90 | /** |
||||
91 | * @var THttpCookieCollection list of cookies to return |
||||
92 | */ |
||||
93 | private $_cookies; |
||||
94 | /** |
||||
95 | * @var int response status code |
||||
96 | */ |
||||
97 | private $_status = 200; |
||||
98 | /** |
||||
99 | * @var string reason correspond to status code |
||||
100 | */ |
||||
101 | private $_reason = 'OK'; |
||||
102 | /** |
||||
103 | * @var string HTML writer type |
||||
104 | */ |
||||
105 | private $_htmlWriterType = '\Prado\Web\UI\THtmlWriter'; |
||||
106 | /** |
||||
107 | * @var string content type |
||||
108 | */ |
||||
109 | private $_contentType; |
||||
110 | /** |
||||
111 | * @var bool|string character set, e.g. UTF-8 or false if no character set should be send to client |
||||
112 | */ |
||||
113 | private $_charset = ''; |
||||
114 | /** |
||||
115 | * @var THttpResponseAdapter adapter. |
||||
116 | */ |
||||
117 | private $_adapter; |
||||
118 | /** |
||||
119 | * @var bool whether http response header has been sent |
||||
120 | */ |
||||
121 | private $_httpHeaderSent; |
||||
122 | /** |
||||
123 | * @var bool whether content-type header has been sent |
||||
124 | */ |
||||
125 | private $_contentTypeHeaderSent; |
||||
126 | |||||
127 | /** |
||||
128 | * Destructor. |
||||
129 | * Flushes any existing content in buffer. |
||||
130 | */ |
||||
131 | 5 | public function __destruct() |
|||
132 | { |
||||
133 | //if($this->_bufferOutput) |
||||
134 | // @ob_end_flush(); |
||||
135 | 5 | parent::__destruct(); |
|||
136 | } |
||||
137 | |||||
138 | /** |
||||
139 | * @param THttpResponseAdapter $adapter response adapter |
||||
140 | */ |
||||
141 | public function setAdapter(THttpResponseAdapter $adapter) |
||||
142 | { |
||||
143 | $this->_adapter = $adapter; |
||||
144 | } |
||||
145 | |||||
146 | /** |
||||
147 | * @return THttpResponseAdapter response adapter, null if not exist. |
||||
148 | */ |
||||
149 | public function getAdapter() |
||||
150 | { |
||||
151 | return $this->_adapter; |
||||
152 | } |
||||
153 | |||||
154 | /** |
||||
155 | * @return bool true if adapter exists, false otherwise. |
||||
156 | */ |
||||
157 | public function getHasAdapter() |
||||
158 | { |
||||
159 | return $this->_adapter !== null; |
||||
160 | } |
||||
161 | |||||
162 | /** |
||||
163 | * Initializes the module. |
||||
164 | * This method is required by IModule and is invoked by application. |
||||
165 | * It starts output buffer if it is enabled. |
||||
166 | * @param \Prado\Xml\TXmlElement $config module configuration |
||||
167 | 8 | */ |
|||
168 | public function init($config) |
||||
169 | 8 | { |
|||
170 | 8 | if ($this->_bufferOutput) { |
|||
171 | ob_start(); |
||||
172 | 8 | } |
|||
173 | 8 | $this->_initialized = true; |
|||
174 | 8 | $this->getApplication()->setResponse($this); |
|||
175 | parent::init($config); |
||||
176 | } |
||||
177 | |||||
178 | /** |
||||
179 | 1 | * @return int time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180. |
|||
180 | */ |
||||
181 | 1 | public function getCacheExpire() |
|||
182 | { |
||||
183 | return session_cache_expire(); |
||||
184 | } |
||||
185 | |||||
186 | /** |
||||
187 | 1 | * @param int $value time-to-live for cached session pages in minutes, this has no effect for nocache limiter. |
|||
188 | */ |
||||
189 | 1 | public function setCacheExpire($value) |
|||
190 | 1 | { |
|||
191 | session_cache_expire(TPropertyValue::ensureInteger($value)); |
||||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | 1 | * @return string cache control method to use for session pages |
|||
196 | */ |
||||
197 | 1 | public function getCacheControl() |
|||
198 | { |
||||
199 | return session_cache_limiter(); |
||||
200 | } |
||||
201 | |||||
202 | /** |
||||
203 | * @param string $value cache control method to use for session pages. Valid values |
||||
204 | 1 | * include none/nocache/private/private_no_expire/public |
|||
205 | */ |
||||
206 | 1 | public function setCacheControl($value) |
|||
207 | 1 | { |
|||
208 | session_cache_limiter(TPropertyValue::ensureEnum($value, ['none', 'nocache', 'private', 'private_no_expire', 'public'])); |
||||
209 | } |
||||
210 | |||||
211 | /** |
||||
212 | * @param string $type content type, default is text/html |
||||
213 | 1 | */ |
|||
214 | public function setContentType($type) |
||||
215 | 1 | { |
|||
216 | if ($this->_contentTypeHeaderSent) { |
||||
217 | throw new \Exception('Unable to alter content-type as it has been already sent'); |
||||
218 | 1 | } |
|||
219 | 1 | $this->_contentType = $type; |
|||
220 | } |
||||
221 | |||||
222 | /** |
||||
223 | * @return string current content type |
||||
224 | 1 | */ |
|||
225 | public function getContentType() |
||||
226 | 1 | { |
|||
227 | return $this->_contentType; |
||||
228 | } |
||||
229 | |||||
230 | /** |
||||
231 | * @return bool|string output charset. |
||||
232 | 1 | */ |
|||
233 | public function getCharset() |
||||
234 | 1 | { |
|||
235 | return $this->_charset; |
||||
236 | } |
||||
237 | |||||
238 | /** |
||||
239 | * @param bool|string $charset output charset. |
||||
240 | 1 | */ |
|||
241 | public function setCharset($charset) |
||||
242 | 1 | { |
|||
243 | 1 | $this->_charset = (strToLower($charset) === 'false') ? false : (string) $charset; |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
244 | } |
||||
245 | |||||
246 | /** |
||||
247 | * @return bool whether to enable output buffer |
||||
248 | 1 | */ |
|||
249 | public function getBufferOutput() |
||||
250 | 1 | { |
|||
251 | return $this->_bufferOutput; |
||||
252 | } |
||||
253 | |||||
254 | /** |
||||
255 | * @param bool $value whether to enable output buffer |
||||
256 | * @throws TInvalidOperationException if session is started already |
||||
257 | 1 | */ |
|||
258 | public function setBufferOutput($value) |
||||
259 | 1 | { |
|||
260 | 1 | if ($this->_initialized) { |
|||
261 | throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); |
||||
262 | 1 | } else { |
|||
263 | $this->_bufferOutput = TPropertyValue::ensureBoolean($value); |
||||
264 | 1 | } |
|||
265 | } |
||||
266 | |||||
267 | /** |
||||
268 | * @return int HTTP status code, defaults to 200 |
||||
269 | 1 | */ |
|||
270 | public function getStatusCode() |
||||
271 | 1 | { |
|||
272 | return $this->_status; |
||||
273 | } |
||||
274 | |||||
275 | /** |
||||
276 | * Set the HTTP status code for the response. |
||||
277 | * The code and its reason will be sent to client using the currently requested http protocol version (see {@see \Prado\Web\THttpRequest::getHttpProtocolVersion}) |
||||
278 | * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1 |
||||
279 | * |
||||
280 | * @param int $status HTTP status code |
||||
281 | * @param null|string $reason HTTP status reason, defaults to standard HTTP reasons |
||||
282 | 1 | */ |
|||
283 | public function setStatusCode($status, $reason = null) |
||||
284 | 1 | { |
|||
285 | if ($this->_httpHeaderSent) { |
||||
286 | throw new \Exception('Unable to alter response as HTTP header already sent'); |
||||
287 | 1 | } |
|||
288 | 1 | $status = TPropertyValue::ensureInteger($status); |
|||
289 | 1 | if (isset(self::$HTTP_STATUS_CODES[$status])) { |
|||
290 | $this->_reason = self::$HTTP_STATUS_CODES[$status]; |
||||
291 | } else { |
||||
292 | if ($reason === null || $reason === '') { |
||||
293 | throw new TInvalidDataValueException("response_status_reason_missing"); |
||||
294 | } |
||||
295 | $reason = TPropertyValue::ensureString($reason); |
||||
296 | if (strpos($reason, "\r") != false || strpos($reason, "\n") != false) { |
||||
0 ignored issues
–
show
|
|||||
297 | throw new TInvalidDataValueException("response_status_reason_barchars"); |
||||
298 | } |
||||
299 | $this->_reason = $reason; |
||||
300 | 1 | } |
|||
301 | 1 | $this->_status = $status; |
|||
302 | } |
||||
303 | |||||
304 | /** |
||||
305 | * @return string HTTP status reason |
||||
306 | */ |
||||
307 | public function getStatusReason() |
||||
308 | { |
||||
309 | return $this->_reason; |
||||
310 | } |
||||
311 | |||||
312 | /** |
||||
313 | * @return THttpCookieCollection list of output cookies |
||||
314 | 1 | */ |
|||
315 | public function getCookies() |
||||
316 | 1 | { |
|||
317 | 1 | if ($this->_cookies === null) { |
|||
318 | $this->_cookies = new THttpCookieCollection($this); |
||||
319 | 1 | } |
|||
320 | return $this->_cookies; |
||||
321 | } |
||||
322 | |||||
323 | /** |
||||
324 | * Outputs a string. |
||||
325 | * It may not be sent back to user immediately if output buffer is enabled. |
||||
326 | * @param string $str string to be output |
||||
327 | */ |
||||
328 | public function write($str) |
||||
329 | { |
||||
330 | // when starting output make sure we send the headers first |
||||
331 | if (!$this->_bufferOutput && !$this->_httpHeaderSent) { |
||||
332 | $this->ensureHeadersSent(); |
||||
333 | } |
||||
334 | echo $str; |
||||
335 | } |
||||
336 | |||||
337 | /** |
||||
338 | * Sends a file back to user. |
||||
339 | * Make sure not to output anything else after calling this method. |
||||
340 | * @param string $fileName file name |
||||
341 | * @param null|string $content content to be set. If null, the content will be read from the server file pointed to by $fileName. |
||||
342 | * @param null|string $mimeType mime type of the content. |
||||
343 | * @param null|array $headers list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain'). |
||||
344 | * @param null|bool $forceDownload force download of file, even if browser able to display inline. Defaults to 'true'. |
||||
345 | * @param null|string $clientFileName force a specific file name on client side. Defaults to 'null' means auto-detect. |
||||
346 | * @param null|int $fileSize size of file or content in bytes if already known. Defaults to 'null' means auto-detect. |
||||
347 | * @throws TInvalidDataValueException if the file cannot be found |
||||
348 | */ |
||||
349 | public function writeFile($fileName, $content = null, $mimeType = null, $headers = null, $forceDownload = true, $clientFileName = null, $fileSize = null) |
||||
350 | { |
||||
351 | static $defaultMimeTypes = [ |
||||
352 | 'css' => 'text/css', |
||||
353 | 'gif' => 'image/gif', |
||||
354 | 'png' => 'image/png', |
||||
355 | 'jpg' => 'image/jpeg', |
||||
356 | 'jpeg' => 'image/jpeg', |
||||
357 | 'htm' => 'text/html', |
||||
358 | 'html' => 'text/html', |
||||
359 | 'js' => 'javascript/js', |
||||
360 | 'pdf' => 'application/pdf', |
||||
361 | 'xls' => 'application/vnd.ms-excel', |
||||
362 | ]; |
||||
363 | |||||
364 | if ($mimeType === null) { |
||||
365 | $mimeType = 'text/plain'; |
||||
366 | if (function_exists('mime_content_type')) { |
||||
367 | $mimeType = mime_content_type($fileName); |
||||
368 | } elseif (($ext = strrchr($fileName, '.')) !== false) { |
||||
369 | $ext = substr($ext, 1); |
||||
370 | if (isset($defaultMimeTypes[$ext])) { |
||||
371 | $mimeType = $defaultMimeTypes[$ext]; |
||||
372 | } |
||||
373 | } |
||||
374 | } |
||||
375 | |||||
376 | if ($clientFileName === null) { |
||||
377 | $clientFileName = basename($fileName); |
||||
378 | } else { |
||||
379 | $clientFileName = basename($clientFileName); |
||||
380 | } |
||||
381 | |||||
382 | if ($fileSize === null || $fileSize < 0) { |
||||
383 | $fileSize = ($content === null ? filesize($fileName) : strlen($content)); |
||||
384 | } |
||||
385 | |||||
386 | $this->sendHttpHeader(); |
||||
387 | if (is_array($headers)) { |
||||
388 | foreach ($headers as $h) { |
||||
389 | header($h); |
||||
390 | } |
||||
391 | } else { |
||||
392 | header('Pragma: public'); |
||||
393 | header('Expires: 0'); |
||||
394 | header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); |
||||
395 | header("Content-Type: $mimeType"); |
||||
396 | $this->_contentTypeHeaderSent = true; |
||||
397 | } |
||||
398 | |||||
399 | header('Content-Length: ' . $fileSize); |
||||
400 | header("Content-Disposition: " . ($forceDownload ? 'attachment' : 'inline') . "; filename=\"$clientFileName\""); |
||||
401 | header('Content-Transfer-Encoding: binary'); |
||||
402 | if ($content === null) { |
||||
403 | readfile($fileName); |
||||
404 | } else { |
||||
405 | echo $content; |
||||
406 | } |
||||
407 | } |
||||
408 | |||||
409 | /** |
||||
410 | * Redirects the browser to the specified URL. |
||||
411 | * The current application will be terminated after this method is invoked. |
||||
412 | * @param string $url URL to be redirected to. If the URL is a relative one, the base URL of |
||||
413 | * the current request will be inserted at the beginning. |
||||
414 | */ |
||||
415 | public function redirect($url) |
||||
416 | { |
||||
417 | if ($this->getHasAdapter()) { |
||||
418 | $this->_adapter->httpRedirect($url); |
||||
419 | } else { |
||||
420 | $this->httpRedirect($url); |
||||
421 | } |
||||
422 | } |
||||
423 | |||||
424 | /** |
||||
425 | * Redirect the browser to another URL and exists the current application. |
||||
426 | * This method is used internally. Please use {@see redirect} instead. |
||||
427 | * |
||||
428 | * @since 3.1.5 |
||||
429 | * You can set the set {@see setStatusCode StatusCode} to a value between 300 and 399 before |
||||
430 | * calling this function to change the type of redirection. |
||||
431 | * If not specified, StatusCode will be 302 (Found) by default |
||||
432 | * |
||||
433 | * @param string $url URL to be redirected to. If the URL is a relative one, the base URL of |
||||
434 | * the current request will be inserted at the beginning. |
||||
435 | */ |
||||
436 | public function httpRedirect($url) |
||||
437 | { |
||||
438 | $this->ensureHeadersSent(); |
||||
439 | |||||
440 | // Under IIS, explicitly send an HTTP response including the status code |
||||
441 | // this is handled automatically by PHP on Apache and others |
||||
442 | $isIIS = (stripos($this->getRequest()->getServerSoftware(), "microsoft-iis") !== false); |
||||
443 | if ($url[0] === '/') { |
||||
444 | $url = $this->getRequest()->getBaseUrl() . $url; |
||||
445 | } |
||||
446 | if ($this->_status >= 300 && $this->_status < 400) { |
||||
447 | // The status code has been modified to a valid redirection status, send it |
||||
448 | if ($isIIS) { |
||||
449 | header('HTTP/1.1 ' . $this->_status . ' ' . self::$HTTP_STATUS_CODES[ |
||||
450 | array_key_exists($this->_status, self::$HTTP_STATUS_CODES) |
||||
451 | ? $this->_status |
||||
452 | : 302 |
||||
453 | ]); |
||||
454 | } |
||||
455 | header('Location: ' . str_replace('&', '&', $url), true, $this->_status); |
||||
456 | } else { |
||||
457 | if ($isIIS) { |
||||
458 | header('HTTP/1.1 302 ' . self::$HTTP_STATUS_CODES[302]); |
||||
459 | } |
||||
460 | header('Location: ' . str_replace('&', '&', $url)); |
||||
461 | } |
||||
462 | |||||
463 | if (!$this->getApplication()->getRequestCompleted()) { |
||||
464 | throw new TExitException(); |
||||
465 | } |
||||
466 | |||||
467 | exit(); |
||||
0 ignored issues
–
show
|
|||||
468 | } |
||||
469 | |||||
470 | /** |
||||
471 | * Reloads the current page. |
||||
472 | * The effect of this method call is the same as user pressing the |
||||
473 | * refresh button on his browser (without post data). |
||||
474 | **/ |
||||
475 | public function reload() |
||||
476 | { |
||||
477 | $this->redirect($this->getRequest()->getRequestUri()); |
||||
478 | } |
||||
479 | |||||
480 | /** |
||||
481 | * Flush the response contents and headers. |
||||
482 | * @param bool $continueBuffering |
||||
483 | */ |
||||
484 | public function flush($continueBuffering = true) |
||||
485 | { |
||||
486 | if ($this->getHasAdapter()) { |
||||
487 | $this->_adapter->flushContent($continueBuffering); |
||||
488 | } else { |
||||
489 | $this->flushContent($continueBuffering); |
||||
490 | } |
||||
491 | } |
||||
492 | |||||
493 | /** |
||||
494 | * Ensures that HTTP response and content-type headers are sent |
||||
495 | */ |
||||
496 | public function ensureHeadersSent() |
||||
497 | { |
||||
498 | $this->ensureHttpHeaderSent(); |
||||
499 | $this->ensureContentTypeHeaderSent(); |
||||
500 | } |
||||
501 | |||||
502 | /** |
||||
503 | * Outputs the buffered content, sends content-type and charset header. |
||||
504 | * This method is used internally. Please use {@see flush} instead. |
||||
505 | * @param bool $continueBuffering whether to continue buffering after flush if buffering was active |
||||
506 | */ |
||||
507 | public function flushContent($continueBuffering = true) |
||||
508 | { |
||||
509 | Prado::trace("Flushing output", THttpResponse::class); |
||||
510 | $this->ensureHeadersSent(); |
||||
511 | if ($this->_bufferOutput) { |
||||
512 | // avoid forced send of http headers (ob_flush() does that) if there's no output yet |
||||
513 | if (ob_get_length() > 0) { |
||||
514 | if (!$continueBuffering) { |
||||
515 | $this->_bufferOutput = false; |
||||
516 | ob_end_flush(); |
||||
517 | } else { |
||||
518 | ob_flush(); |
||||
519 | } |
||||
520 | flush(); |
||||
521 | } |
||||
522 | } else { |
||||
523 | flush(); |
||||
524 | } |
||||
525 | } |
||||
526 | |||||
527 | /** |
||||
528 | * Ensures that the HTTP header with the status code and status reason are sent |
||||
529 | */ |
||||
530 | protected function ensureHttpHeaderSent() |
||||
531 | { |
||||
532 | if (!$this->_httpHeaderSent) { |
||||
533 | $this->sendHttpHeader(); |
||||
534 | } |
||||
535 | } |
||||
536 | |||||
537 | /** |
||||
538 | * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK) |
||||
539 | */ |
||||
540 | protected function sendHttpHeader() |
||||
541 | { |
||||
542 | $protocol = $this->getRequest()->getHttpProtocolVersion(); |
||||
543 | if ($this->getRequest()->getHttpProtocolVersion() === null) { |
||||
0 ignored issues
–
show
|
|||||
544 | $protocol = 'HTTP/1.1'; |
||||
545 | } |
||||
546 | |||||
547 | header($protocol . ' ' . $this->_status . ' ' . $this->_reason, true, TPropertyValue::ensureInteger($this->_status)); |
||||
548 | |||||
549 | $this->_httpHeaderSent = true; |
||||
550 | } |
||||
551 | |||||
552 | /** |
||||
553 | * Ensures that the HTTP header with the status code and status reason are sent |
||||
554 | */ |
||||
555 | protected function ensureContentTypeHeaderSent() |
||||
556 | { |
||||
557 | if (!$this->_contentTypeHeaderSent) { |
||||
558 | $this->sendContentTypeHeader(); |
||||
559 | } |
||||
560 | } |
||||
561 | |||||
562 | /** |
||||
563 | * Sends content type header with optional charset. |
||||
564 | */ |
||||
565 | protected function sendContentTypeHeader() |
||||
566 | { |
||||
567 | $contentType = $this->_contentType === null ? self::DEFAULT_CONTENTTYPE : $this->_contentType; |
||||
568 | $charset = $this->getCharset(); |
||||
569 | if ($charset === false) { |
||||
570 | $this->appendHeader('Content-Type: ' . $contentType); |
||||
571 | return; |
||||
572 | } |
||||
573 | |||||
574 | if ($charset === '' && ($globalization = $this->getApplication()->getGlobalization(false)) !== null) { |
||||
575 | $charset = $globalization->getCharset(); |
||||
576 | } |
||||
577 | |||||
578 | if ($charset === '') { |
||||
579 | $charset = self::DEFAULT_CHARSET; |
||||
580 | } |
||||
581 | $this->appendHeader('Content-Type: ' . $contentType . ';charset=' . $charset); |
||||
0 ignored issues
–
show
Are you sure
$charset of type string|true can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
582 | |||||
583 | $this->_contentTypeHeaderSent = true; |
||||
584 | } |
||||
585 | |||||
586 | /** |
||||
587 | * Returns the content in the output buffer. |
||||
588 | * The buffer will NOT be cleared after calling this method. |
||||
589 | * Use {@see clear()} is you want to clear the buffer. |
||||
590 | * @return string output that is in the buffer. |
||||
591 | */ |
||||
592 | public function getContents() |
||||
593 | { |
||||
594 | Prado::trace("Retrieving output", THttpResponse::class); |
||||
595 | return $this->_bufferOutput ? ob_get_contents() : ''; |
||||
596 | } |
||||
597 | |||||
598 | /** |
||||
599 | * Clears any existing buffered content. |
||||
600 | */ |
||||
601 | public function clear() |
||||
602 | { |
||||
603 | if ($this->_bufferOutput && ob_get_length() > 0) { |
||||
604 | ob_clean(); |
||||
605 | } |
||||
606 | Prado::trace("Clearing output", THttpResponse::class); |
||||
607 | } |
||||
608 | |||||
609 | /** |
||||
610 | * @param null|int $case Either {@see CASE_UPPER} or {@see CASE_LOWER} or as is null (default) |
||||
611 | * @return array |
||||
612 | */ |
||||
613 | public function getHeaders($case = null) |
||||
614 | { |
||||
615 | $result = []; |
||||
616 | $headers = headers_list(); |
||||
617 | foreach ($headers as $header) { |
||||
618 | $tmp = explode(':', $header); |
||||
619 | $key = trim(array_shift($tmp)); |
||||
620 | $value = trim(implode(':', $tmp)); |
||||
621 | if (isset($result[$key])) { |
||||
622 | $result[$key] .= ', ' . $value; |
||||
623 | } else { |
||||
624 | $result[$key] = $value; |
||||
625 | } |
||||
626 | } |
||||
627 | |||||
628 | if ($case !== null) { |
||||
629 | return array_change_key_case($result, $case); |
||||
630 | } |
||||
631 | |||||
632 | return $result; |
||||
633 | } |
||||
634 | |||||
635 | /** |
||||
636 | * Sends a header. |
||||
637 | * @param string $value header |
||||
638 | * @param bool $replace whether the header should replace a previous similar header, or add a second header of the same type |
||||
639 | */ |
||||
640 | public function appendHeader($value, $replace = true) |
||||
641 | { |
||||
642 | Prado::trace("Sending header '$value'", THttpResponse::class); |
||||
643 | header($value, $replace); |
||||
644 | } |
||||
645 | |||||
646 | /** |
||||
647 | * Writes a log message into error log. |
||||
648 | * This method is simple wrapper of PHP function error_log. |
||||
649 | * @param string $message The error message that should be logged |
||||
650 | * @param int $messageType where the error should go |
||||
651 | * @param string $destination The destination. Its meaning depends on the message parameter as described above |
||||
652 | * @param string $extraHeaders The extra headers. It's used when the message parameter is set to 1. This message type uses the same internal function as mail() does. |
||||
653 | * @see http://us2.php.net/manual/en/function.error-log.php |
||||
654 | */ |
||||
655 | public function appendLog($message, $messageType = 0, $destination = '', $extraHeaders = '') |
||||
656 | { |
||||
657 | error_log($message, $messageType, $destination, $extraHeaders); |
||||
658 | } |
||||
659 | |||||
660 | /** |
||||
661 | * Sends a cookie. |
||||
662 | * Do not call this method directly. Operate with the result of {@see getCookies} instead. |
||||
663 | * @param THttpCookie $cookie cook to be sent |
||||
664 | */ |
||||
665 | public function addCookie($cookie) |
||||
666 | { |
||||
667 | $request = $this->getRequest(); |
||||
668 | if ($request->getEnableCookieValidation()) { |
||||
669 | $value = $this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); |
||||
670 | } else { |
||||
671 | $value = $cookie->getValue(); |
||||
672 | } |
||||
673 | |||||
674 | setcookie( |
||||
675 | $cookie->getName(), |
||||
676 | $value, |
||||
677 | $cookie->getPhpOptions() |
||||
678 | ); |
||||
679 | } |
||||
680 | |||||
681 | /** |
||||
682 | * Deletes a cookie. |
||||
683 | * Do not call this method directly. Operate with the result of {@see getCookies} instead. |
||||
684 | * @param THttpCookie $cookie cook to be deleted |
||||
685 | */ |
||||
686 | public function removeCookie($cookie) |
||||
687 | { |
||||
688 | $options = $cookie->getPhpOptions(); |
||||
689 | $options['expires'] = 0; |
||||
690 | setcookie( |
||||
691 | $cookie->getName(), |
||||
692 | null, |
||||
693 | $options |
||||
694 | ); |
||||
695 | } |
||||
696 | |||||
697 | /** |
||||
698 | * @return string the type of HTML writer to be used, defaults to THtmlWriter |
||||
699 | */ |
||||
700 | public function getHtmlWriterType() |
||||
701 | { |
||||
702 | return $this->_htmlWriterType; |
||||
703 | } |
||||
704 | |||||
705 | /** |
||||
706 | * @param string $value the type of HTML writer to be used, may be the class name or the namespace |
||||
707 | */ |
||||
708 | public function setHtmlWriterType($value) |
||||
709 | { |
||||
710 | $this->_htmlWriterType = $value; |
||||
711 | } |
||||
712 | |||||
713 | /** |
||||
714 | * Creates a new instance of HTML writer. |
||||
715 | * If the type of the HTML writer is not supplied, {@see getHtmlWriterType HtmlWriterType} will be assumed. |
||||
716 | * @param string $type type of the HTML writer to be created. If null, {@see getHtmlWriterType HtmlWriterType} will be assumed. |
||||
717 | */ |
||||
718 | public function createHtmlWriter($type = null) |
||||
719 | { |
||||
720 | if ($type === null) { |
||||
721 | $type = $this->getHtmlWriterType(); |
||||
722 | } |
||||
723 | if ($this->getHasAdapter()) { |
||||
724 | return $this->_adapter->createNewHtmlWriter($type, $this); |
||||
725 | } else { |
||||
726 | return $this->createNewHtmlWriter($type, $this); |
||||
727 | } |
||||
728 | } |
||||
729 | |||||
730 | /** |
||||
731 | * Create a new html writer instance. |
||||
732 | * This method is used internally. Please use {@see createHtmlWriter} instead. |
||||
733 | * @param string $type type of HTML writer to be created. |
||||
734 | * @param \Prado\IO\ITextWriter $writer text writer holding the contents. |
||||
735 | */ |
||||
736 | public function createNewHtmlWriter($type, $writer) |
||||
737 | { |
||||
738 | return Prado::createComponent($type, $writer); |
||||
739 | } |
||||
740 | } |
||||
741 |