blob: 3a701ae618467d9ada05c37582db0f391249148b [file] [log] [blame]
<?php
/**
* File containing the ezcMvcHttpResponseWriter class
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @version //autogentag//
* @filesource
* @package MvcTools
*/
/**
* Request parser that uses HTTP headers to populate an ezcMvcRequest object.
*
* @package MvcTools
* @version //autogentag//
* @mainclass
*/
class ezcMvcHttpResponseWriter extends ezcMvcResponseWriter
{
/**
* Contains the response struct
*
* @var ezcMvcResponse
*/
protected $response;
/**
* Contains an array of header name to value mappings
*
* @var array(string=>string)
*/
public $headers;
/**
* Creates a new ezcMvcHttpResponseWriter class to write $response
*
* @param ezcMvcResponse $response
*/
public function __construct( ezcMvcResponse $response )
{
$this->response = $response;
$this->headers = array();
}
/**
* Takes the raw protocol depending response body, and the protocol
* abstract response headers and forges a response to the client. Then it sends
* the assembled response to the client.
*/
public function handleResponse()
{
// process all headers
$this->processStandardHeaders();
if ( $this->response->cache instanceof ezcMvcResultCache )
{
$this->processCacheHeaders();
}
if ( $this->response->content instanceof ezcMvcResultContent )
{
$this->processContentHeaders();
}
// process the status headers through objects
if ( $this->response->status instanceof ezcMvcResultStatusObject )
{
$this->response->status->process( $this );
}
// automatically add content-length header
$this->headers['Content-Length'] = strlen( $this->response->body );
// write output
foreach ( $this->headers as $header => $value )
{
header( "$header: $value" );
}
// do cookies
foreach ( $this->response->cookies as $cookie )
{
$this->processCookie( $cookie );
}
echo $this->response->body;
}
/**
* Takes a $cookie and uses PHP's setcookie() function to add cookies to the output stream.
*
* @param ezcMvcResultCookie $cookie
*/
private function processCookie( ezcMvcResultCookie $cookie )
{
$args = array();
$args[] = $cookie->name;
$args[] = $cookie->value;
if ( $cookie->expire instanceof DateTime )
{
$args[] = $cookie->expire->format( 'U' );
}
else
{
$args[] = null;
}
$args[] = $cookie->domain;
$args[] = $cookie->path;
$args[] = $cookie->secure;
$args[] = $cookie->httpOnly;
call_user_func_array( 'setcookie', $args );
}
/**
* Checks whether there is a DateTime object in $obj->$prop and sets a header accordingly.
*
* @param Object $obj
* @param string $prop
* @param string $headerName
* @param bool $default
*/
private function doDate( $obj, $prop, $headerName, $default = false )
{
if ( $obj->$prop instanceof DateTime )
{
$headerDate = clone $obj->$prop;
$headerDate->setTimezone( new DateTimeZone( "UTC" ) );
$this->headers[$headerName] = $headerDate->format( 'D, d M Y H:i:s \G\M\T' );
return;
}
if ( $default )
{
$headerDate = new DateTime( "UTC" );
$this->headers[$headerName] = $headerDate->format( 'D, d M Y H:i:s \G\M\T' );
}
}
/**
* Processes the standard headers that are not subdivided into other structs.
*/
protected function processStandardHeaders()
{
$res = $this->response;
// generator
$this->headers['X-Powered-By'] = $res->generator !== ''
? $res->generator
: "Apache Zeta Components MvcTools";
$this->doDate( $res, 'date', 'Date', true );
}
/**
* Processes the caching related headers.
*/
protected function processCacheHeaders()
{
$cache = $this->response->cache;
if ( $cache->vary )
{
$this->headers['Vary'] = $cache->vary;
}
$this->doDate( $cache, 'expire', 'Expires' );
if ( count( $cache->controls ) )
{
$this->headers['Cache-Control'] = join( ', ', $cache->controls );
}
if ( $cache->pragma )
{
$this->headers['Pragma'] = $cache->pragma;
}
$this->doDate( $cache, 'lastModified', 'Last-Modified' );
}
/**
* Processes the content type related headers.
*/
protected function processContentHeaders()
{
$content = $this->response->content;
$defaultContentType = 'text/html';
if ( $content->language )
{
$this->headers['Content-Language'] = $content->language;
}
if ( $content->type || $content->charset )
{
$contentType = $content->type ? $content->type : $defaultContentType;
if ( $content->charset )
{
$contentType .= '; charset=' . $content->charset;
}
$this->headers['Content-Type'] = $contentType;
}
if ( $content->encoding )
{
$this->headers['Content-Encoding'] = $content->encoding;
}
if ( $content->disposition instanceof ezcMvcResultContentDisposition )
{
$this->processContentDispositionHeaders( $content->disposition );
}
}
/**
* Processed the content disposition related headers.
*
* See http://tools.ietf.org/html/rfc2183#section-2, but implemented with limitations.
*
* @param ezcMvcResultContentDisposition $disp
*/
protected function processContentDispositionHeaders( ezcMvcResultContentDisposition $disp )
{
// type
$value = $disp->type;
// filename
if ( $disp->filename !== null )
{
$value .= "; filename";
if ( strpbrk( $disp->filename,
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" ) === false )
{
// case 1: ASCII characters only
if ( strpbrk( $disp->filename, '\(\)<>@,;:\\"/\[\]?= ' ) === false )
{
// case 1a: no tspecials
$value .= '=' . $disp->filename;
}
else
{
// case 1b: with tspecials
$value .= '="' . str_replace( '"', '\"', $disp->filename ) . '"';
}
}
else
{
// case 2: non-ASCII characters (and thus UTF-8 encoded)
$value .= "*=utf-8''" . urlencode( $disp->filename );
}
}
// dates
if ( $disp->creationDate !== null )
{
$value .= '; creation-date="' . $disp->creationDate->format( DateTime::RFC2822 ) . '"';
}
if ( $disp->modificationDate !== null )
{
$value .= '; modification-date="' . $disp->modificationDate->format( DateTime::RFC2822 ) . '"';
}
if ( $disp->readDate !== null )
{
$value .= '; read-date="' . $disp->readDate->format( DateTime::RFC2822 ) . '"';
}
// size
if ( $disp->size !== null )
{
$value .= "; size=" . (int) $disp->size;
}
$this->headers['Content-Disposition'] = $value;
}
}
?>