pradosoft /
prado
| 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
Loading history...
|
|||||
| 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
Loading history...
|
|||||
| 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 |