You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

227 lines
7.3 KiB

<?php
/**
* li₃: the most RAD framework for PHP (http://li3.me)
*
* Copyright 2009, Union of RAD. All rights reserved. This source
* code is distributed under the terms of the BSD 3-Clause License.
* The full license text can be found in the LICENSE.txt file.
*/
namespace lithium\action;
/**
* A `Response` object is typically instantiated automatically by the `Controller`. It is assigned
* any headers set in the course of the request, as well as any content rendered by the
* `Controller`. Once completed, the `Controller` returns the `Response` object to the `Dispatcher`.
*
* The `Response` object is responsible for writing its body content to output, and writing any
* headers to the browser.
*
* @see lithium\action\Dispatcher
* @see lithium\action\Controller
*/
class Response extends \lithium\net\http\Response {
/**
* Classes used by Response.
*
* @var array
*/
protected $_classes = [
'router' => 'lithium\net\http\Router',
'media' => 'lithium\net\http\Media',
'auth' => 'lithium\net\http\Auth'
];
/**
* Auto configuration properties.
*
* @var array
*/
protected $_autoConfig = ['classes' => 'merge'];
/**
* Constructor. Adds config values to the public properties when a new object is created.
* Config options also include default values for `Response::body()` when called from
* `Response::render()`.
*
* @see lithium\net\http\Message::body()
* @see lithium\net\http\Response::__construct()
* @see lithium\net\http\Message::__construct()
* @see lithium\net\Message::__construct()
* @param array $config The available configuration options are the following. Further
* options are inherited from the parent classes.
* - `'buffer'` _integer_: Defaults to `null`
* - `'decode'` _boolean_: Defaults to `null`.
* - `'location'` _array|string|null_: Defaults to `null`.
* - `'request'` _object_: Defaults to `null`.
* @return void
*/
public function __construct(array $config = []) {
$defaults = [
'buffer' => 8192,
'location' => null,
'request' => null,
'decode' => false
];
parent::__construct($config + $defaults);
}
/**
* Sets the Location header using `$config['location']` and `$config['request']` passed in
* through the constructor if provided.
*
* @return void
*/
protected function _init() {
parent::_init();
$router = $this->_classes['router'];
if ($this->_config['location']) {
$location = $router::match($this->_config['location'], $this->_config['request']);
$this->headers('Location', $location);
}
}
/**
* Controls how or whether the client browser and web proxies should cache this response.
*
* @param mixed $expires This can be a Unix timestamp indicating when the page expires, or a
* string indicating the relative time offset that a page should expire, i.e. `"+5 hours".
* Finally, `$expires` can be set to `false` to completely disable browser or proxy
* caching.
* @return void
*/
public function cache($expires) {
if ($expires === false) {
$headers = [
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
'Cache-Control' => [
'no-store, no-cache, must-revalidate',
'post-check=0, pre-check=0',
'max-age=0'
],
'Pragma' => 'no-cache'
];
} else {
$expires = is_int($expires) ? $expires : strtotime($expires);
$headers = [
'Expires' => gmdate('D, d M Y H:i:s', $expires) . ' GMT',
'Cache-Control' => 'max-age=' . ($expires - time()),
'Pragma' => 'cache'
];
}
$this->headers($headers);
}
/**
* Sets/Gets the content type. If `'type'` is null, the method will attempt to determine the
* type from the params, then from the environment setting
*
* @param string $type a full content type i.e. `'application/json'` or simple name `'json'`
* @return string A simple content type name, i.e. `'html'`, `'xml'`, `'json'`, etc., depending
* on the content type of the request.
*/
public function type($type = null) {
if ($type === null && $this->_type === null) {
$type = 'html';
}
return parent::type($type);
}
/**
* Render a response by writing headers and output. Output is echoed in
* chunks because of an issue where `echo` time increases exponentially
* on long message bodies.
*
* Reponses which have a `Location` header set are indicating a
* redirect, will get their status code automatically adjusted to `302`
* (Found/Moved Temporarily) in case the status code before was `200`
* (OK). This is to allow easy redirects by setting just the `Location`
* header and is assumed to be the original intent of the user.
*
* On responses with status codes `204` (No Content) and `302` (Found)
* a message body - even if one is set - will never be send. These
* status codes either don't have a message body as per their nature or
* they are ignored and can thus be omitted for performance reasons.
*
* @return void
*/
public function render() {
$code = null;
if (isset($this->headers['location']) || isset($this->headers['Location'])) {
if ($this->status['code'] === 200) {
$this->status(302);
}
$code = $this->status['code'];
}
if ($cookies = $this->_cookies()) {
$this->headers('Set-Cookie', $cookies);
}
$this->_writeHeaders($this->status() ?: $this->status(500));
$this->_writeHeaders($this->headers(), $code);
if ($this->status['code'] === 302 || $this->status['code'] === 204) {
return;
}
foreach ($this->body(null, $this->_config) as $chunk) {
echo $chunk;
}
}
/**
* Casts the Response object to a string. This doesn't actually return a string, but does
* a direct render and returns an empty string.
*
* @return string Just an empty string to satify requirements of this magic method.
*/
public function __toString() {
$this->render();
return '';
}
/**
* Writes raw headers to output.
*
* @param string|array $headers Either a raw header string, or an array of header strings. Use
* an array if a single header must be written multiple times with different values.
* Otherwise, additional values for duplicate headers will overwrite previous values.
* @param integer $code Optional. If present, forces a specific HTTP response code. Used
* primarily in conjunction with the 'Location' header.
* @return void
*/
protected function _writeHeaders($headers, $code = null) {
foreach ((array) $headers as $header) {
$code ? header($header, false, $code) : header($header, false);
}
}
/* Deprecated / BC */
/**
* Expands on `\net\http\Message::headers()` with some magic conversions for shorthand headers.
*
* @deprecated This method will be removed in a future version. Note that the parent `header()`
* wil continue to exist.
* @param string|array $key
* @param mixed $value
* @param boolean $replace
* @return mixed
*/
public function headers($key = null, $value = null, $replace = true) {
if ($key === 'download' || $key === 'Download') {
$message = "Shorthand header `Download` with `<FILENAME>` has been deprecated ";
$message .= "because it's too magic. Please use `Content-Disposition` ";
$message .= "with `attachment; filename=\"<FILENAME>\"` instead.";
trigger_error($message, E_USER_DEPRECATED);
$key = 'Content-Disposition';
$value = 'attachment; filename="' . $value . '"';
}
return parent::headers($key, $value, $replace);
}
}
?>