root/trunk/framework/Web/THttpResponse.php

Revision 2482, 16.7 kB (checked in by knut, 4 months ago)

updated copyright

  • Property svn:keywords set to Id
Line 
1<?php
2/**
3 * THttpResponse class
4 *
5 * @author Qiang Xue <qiang.xue@gmail.com>
6 * @link http://www.pradosoft.com/
7 * @copyright Copyright &copy; 2005-2008 PradoSoft
8 * @license http://www.pradosoft.com/license/
9 * @version $Id$
10 * @package System.Web
11 */
12
13/**
14 * Includes the THttpResponse adapter.
15 */
16Prado::using('System.Web.THttpResponseAdapter');
17
18/**
19 * THttpResponse class
20 *
21 * THttpResponse implements the mechanism for sending output to client users.
22 *
23 * To output a string to client, use {@link write()}. By default, the output is
24 * buffered until {@link flush()} is called or the application ends. The output in
25 * the buffer can also be cleaned by {@link clear()}. To disable output buffering,
26 * set BufferOutput property to false.
27 *
28 * To send cookies to client, use {@link getCookies()}.
29 * To redirect client browser to a new URL, use {@link redirect()}.
30 * To send a file to client, use {@link writeFile()}.
31 *
32 * By default, THttpResponse is registered with {@link TApplication} as the
33 * response module. It can be accessed via {@link TApplication::getResponse()}.
34 *
35 * THttpResponse may be configured in application configuration file as follows
36 *
37 * <module id="response" class="System.Web.THttpResponse" CacheExpire="20" CacheControl="nocache" BufferOutput="true" />
38 *
39 * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl}
40 * and {@link getBufferOutput BufferOutput} are optional properties of THttpResponse.
41 *
42 * THttpResponse sends charset header if either {@link setCharset() Charset}
43 * or {@link TGlobalization::setCharset() TGlobalization.Charset} is set.
44 *
45 * Since 3.1.2, HTTP status code can be set with the {@link setStatusCode StatusCode} property.
46 *
47 * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@link setStatusCode StatusCode}
48 * in your application, be sure to add theses informations.
49 * E.g : to make an http authentication :
50 * <code>
51 *  public function clickAuth ($sender, $param)
52 *  {
53 *     $response=$this->getResponse();
54 *     $response->setStatusCode(401);
55 *     $response->appendHeader('WWW-Authenticate: Basic realm="Test"');
56 *  }
57 * </code>
58 *   
59 * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This
60 * will force the browser to ask for a username and a password.
61 * 
62 * @author Qiang Xue <qiang.xue@gmail.com>
63 * @version $Id$
64 * @package System.Web
65 * @since 3.0
66 */
67class THttpResponse extends TModule implements ITextWriter
68{
69    /**
70     * @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616}
71     */
72    private static $HTTP_STATUS_CODES = array(
73        100 => 'Continue', 101 => 'Switching Protocols',
74        200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
75        300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
76        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',
77        500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
78    );
79
80    /**
81     * @var boolean whether to buffer output
82     */
83    private $_bufferOutput=true;
84    /**
85     * @var boolean if the application is initialized
86     */
87    private $_initialized=false;
88    /**
89     * @var THttpCookieCollection list of cookies to return
90     */
91    private $_cookies=null;
92    /**
93     * @var integer response status code
94     */
95    private $_status=200;
96    /**
97     * @var string reason correspond to status code
98     */
99    private $_reason='OK';
100    /**
101     * @var string HTML writer type
102     */
103    private $_htmlWriterType='System.Web.UI.THtmlWriter';
104    /**
105     * @var string content type
106     */
107    private $_contentType=null;
108    /**
109     * @var string character set, e.g. UTF-8
110     */
111    private $_charset='';
112    /**
113     * @var THttpResponseAdapter adapter.
114     */
115    private $_adapter;
116
117    /**
118     * Destructor.
119     * Flushes any existing content in buffer.
120     */
121    public function __destruct()
122    {
123        //if($this->_bufferOutput)
124        //    @ob_end_flush();
125    }
126
127    /**
128     * @param THttpResponseAdapter response adapter
129     */
130    public function setAdapter(THttpResponseAdapter $adapter)
131    {
132        $this->_adapter=$adapter;
133    }
134
135    /**
136     * @return THttpResponseAdapter response adapter, null if not exist.
137     */
138    public function getAdapter()
139    {
140        return $this->_adapter;
141    }
142
143    /**
144     * @return boolean true if adapter exists, false otherwise.
145     */
146    public function getHasAdapter()
147    {
148        return !is_null($this->_adapter);
149    }
150
151    /**
152     * Initializes the module.
153     * This method is required by IModule and is invoked by application.
154     * It starts output buffer if it is enabled.
155     * @param TXmlElement module configuration
156     */
157    public function init($config)
158    {
159        if($this->_bufferOutput)
160            ob_start();
161        $this->_initialized=true;
162        $this->getApplication()->setResponse($this);
163    }
164
165    /**
166     * @return integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180.
167     */
168    public function getCacheExpire()
169    {
170        return session_cache_expire();
171    }
172
173    /**
174     * @param integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter.
175     */
176    public function setCacheExpire($value)
177    {
178        session_cache_expire(TPropertyValue::ensureInteger($value));
179    }
180
181    /**
182     * @return string cache control method to use for session pages
183     */
184    public function getCacheControl()
185    {
186        return session_cache_limiter();
187    }
188
189    /**
190     * @param string cache control method to use for session pages. Valid values
191     *               include none/nocache/private/private_no_expire/public
192     */
193    public function setCacheControl($value)
194    {
195        session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public')));
196    }
197
198    /**
199     * @return string content type, default is text/html
200     */
201    public function setContentType($type)
202    {
203        $this->_contentType = $type;
204    }
205
206    /**
207     * @return string current content type
208     */
209    public function getContentType()
210    {
211        return $this->_contentType;
212    }
213
214    /**
215     * @return string output charset.
216     */
217    public function getCharset()
218    {
219        return $this->_charset;
220    }
221
222    /**
223     * @param string output charset.
224     */
225    public function setCharset($charset)
226    {
227        $this->_charset = $charset;
228    }
229
230    /**
231     * @return boolean whether to enable output buffer
232     */
233    public function getBufferOutput()
234    {
235        return $this->_bufferOutput;
236    }
237
238    /**
239     * @param boolean whether to enable output buffer
240     * @throws TInvalidOperationException if session is started already
241     */
242    public function setBufferOutput($value)
243    {
244        if($this->_initialized)
245            throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable');
246        else
247            $this->_bufferOutput=TPropertyValue::ensureBoolean($value);
248    }
249
250    /**
251     * @return integer HTTP status code, defaults to 200
252     */
253    public function getStatusCode()
254    {
255        return $this->_status;
256    }
257
258    /**
259     * Set the HTTP status code for the response.
260     * The code and its reason will be sent to client using the currently requested http protocol version (see {@link THttpRequest::getHttpProtocolVersion})
261     * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1
262     *
263     * @param integer HTTP status code
264     * @param string HTTP status reason, defaults to standard HTTP reasons
265     */
266    public function setStatusCode($status, $reason=null)
267    {
268        $status=TPropertyValue::ensureInteger($status);
269        if(isset(self::$HTTP_STATUS_CODES[$status])) {
270            $this->_reason=self::$HTTP_STATUS_CODES[$status];
271        }else{
272            if($reason===null || $reason==='') {
273                throw new TInvalidDataValueException("response_status_reason_missing");
274            }
275            $reason=TPropertyValue::ensureString($reason);
276            if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) {
277                throw new TInvalidDataValueException("response_status_reason_barchars");
278            }
279            $this->_reason=$reason;
280        }
281        $this->_status=$status;
282    }
283
284    /**
285     * @param string HTTP status reason
286     */
287    public function getStatusReason() {
288        return $this->_reason;
289    }
290
291    /**
292     * @return THttpCookieCollection list of output cookies
293     */
294    public function getCookies()
295    {
296        if($this->_cookies===null)
297            $this->_cookies=new THttpCookieCollection($this);
298        return $this->_cookies;
299    }
300
301    /**
302     * Outputs a string.
303     * It may not be sent back to user immediately if output buffer is enabled.
304     * @param string string to be output
305     */
306    public function write($str)
307    {
308        echo $str;
309    }
310
311    /**
312     * Sends a file back to user.
313     * Make sure not to output anything else after calling this method.
314     * @param string file name
315     * @param string content to be set. If null, the content will be read from the server file pointed to by $fileName.
316     * @param string mime type of the content.
317     * @param array list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain').
318     * @throws TInvalidDataValueException if the file cannot be found
319     */
320    public function writeFile($fileName,$content=null,$mimeType=null,$headers=null)
321    {
322        static $defaultMimeTypes=array(
323            'css'=>'text/css',
324            'gif'=>'image/gif',
325            'jpg'=>'image/jpeg',
326            'jpeg'=>'image/jpeg',
327            'htm'=>'text/html',
328            'html'=>'text/html',
329            'js'=>'javascript/js',
330            'pdf'=>'application/pdf',
331            'xls'=>'application/vnd.ms-excel',
332        );
333
334        if($mimeType===null)
335        {
336            $mimeType='text/plain';
337            if(function_exists('mime_content_type'))
338                $mimeType=mime_content_type($fileName);
339            else if(($ext=strrchr($fileName,'.'))!==false)
340            {
341                $ext=substr($ext,1);
342                if(isset($defaultMimeTypes[$ext]))
343                    $mimeType=$defaultMimeTypes[$ext];
344            }
345        }
346        $fn=basename($fileName);
347        $this->sendHttpHeader();
348        if(is_array($headers))
349        {
350            foreach($headers as $h)
351                header($h);
352        }
353        else
354        {
355            header('Pragma: public');
356            header('Expires: 0');
357            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
358        }
359        header("Content-type: $mimeType");
360        header('Content-Length: '.($content===null?filesize($fileName):strlen($content)));
361        header("Content-Disposition: attachment; filename=\"$fn\"");
362        header('Content-Transfer-Encoding: binary');
363        if($content===null)
364            readfile($fileName);
365        else
366            echo $content;
367    }
368
369    /**
370     * Redirects the browser to the specified URL.
371     * The current application will be terminated after this method is invoked.
372     * @param string URL to be redirected to. If the URL is a relative one, the base URL of
373     * the current request will be inserted at the beginning.
374     */
375    public function redirect($url)
376    {
377        if($this->getHasAdapter())
378            $this->_adapter->httpRedirect($url);
379        else
380            $this->httpRedirect($url);
381    }
382
383    /**
384     * Redirect the browser to another URL and exists the current application.
385     * This method is used internally. Please use {@link redirect} instead.
386     * @param string URL to be redirected to. If the URL is a relative one, the base URL of
387     * the current request will be inserted at the beginning.
388     */
389    public function httpRedirect($url)
390    {
391        if(!$this->getApplication()->getRequestCompleted())
392            $this->getApplication()->onEndRequest();
393        if($url[0]==='/')
394            $url=$this->getRequest()->getBaseUrl().$url;
395        header('Location: '.str_replace('&amp;','&',$url));
396        exit();
397    }
398
399    /**
400     * Reloads the current page.
401     * The effect of this method call is the same as user pressing the
402     * refresh button on his browser (without post data).
403     **/
404    public function reload()
405    {
406        $this->redirect($this->getRequest()->getRequestUri());
407    }
408
409    /**
410     * Flush the response contents and headers.
411     */
412    public function flush()
413    {
414        if($this->getHasAdapter())
415            $this->_adapter->flushContent();
416        else
417            $this->flushContent();
418    }
419
420    /**
421     * Outputs the buffered content, sends content-type and charset header.
422     * This method is used internally. Please use {@link flush} instead.
423     */
424    public function flushContent()
425    {
426        Prado::trace("Flushing output",'System.Web.THttpResponse');
427        $this->sendHttpHeader();
428        $this->sendContentTypeHeader();
429        if($this->_bufferOutput)
430            ob_flush();
431    }
432   
433    /**
434     * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK)
435     */
436    protected function sendHttpHeader ()
437    {
438        if (($version=$this->getRequest()->getHttpProtocolVersion())==='')
439            header (' ', true, $this->_status);
440        else
441            header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status);
442    }
443
444    /**
445     * Sends content type header if charset is not empty.
446     */
447    protected function sendContentTypeHeader()
448    {
449        $charset=$this->getCharset();
450        if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null)
451            $charset=$globalization->getCharset();
452        if($charset!=='')
453        {
454            $contentType=$this->_contentType===null?'text/html':$this->_contentType;
455            $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset);
456        }
457        else if($this->_contentType!==null)
458            $this->appendHeader('Content-Type: '.$this->_contentType.';charset=UTF-8');
459    }
460
461    /**
462     * Returns the content in the output buffer.
463     * The buffer will NOT be cleared after calling this method.
464     * Use {@link clear()} is you want to clear the buffer.
465     * @return string output that is in the buffer.
466     */
467    public function getContents()
468    {
469        Prado::trace("Retrieving output",'System.Web.THttpResponse');
470        return $this->_bufferOutput?ob_get_contents():'';
471    }
472
473    /**
474     * Clears any existing buffered content.
475     */
476    public function clear()
477    {
478        if($this->_bufferOutput)
479            ob_clean();
480        Prado::trace("Clearing output",'System.Web.THttpResponse');
481    }
482
483    /**
484     * Sends a header.
485     * @param string header
486     */
487    public function appendHeader($value)
488    {
489        Prado::trace("Sending header '$value'",'System.Web.THttpResponse');
490        header($value);
491    }
492
493    /**
494     * Writes a log message into error log.
495     * This method is simple wrapper of PHP function error_log.
496     * @param string The error message that should be logged
497     * @param integer where the error should go
498     * @param string The destination. Its meaning depends on the message parameter as described above
499     * @param string 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.
500     * @see http://us2.php.net/manual/en/function.error-log.php
501     */
502    public function appendLog($message,$messageType=0,$destination='',$extraHeaders='')
503    {
504        error_log($message,$messageType,$destination,$extraHeaders);
505    }
506
507    /**
508     * Sends a cookie.
509     * Do not call this method directly. Operate with the result of {@link getCookies} instead.
510     * @param THttpCookie cook to be sent
511     */
512    public function addCookie($cookie)
513    {
514        $request=$this->getRequest();
515        if($request->getEnableCookieValidation())
516        {
517            $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue());
518            setcookie($cookie->getName(),$value,$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
519        }
520        else
521            setcookie($cookie->getName(),$cookie->getValue(),$cookie->getExpire(),$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
522    }
523
524    /**
525     * Deletes a cookie.
526     * Do not call this method directly. Operate with the result of {@link getCookies} instead.
527     * @param THttpCookie cook to be deleted
528     */
529    public function removeCookie($cookie)
530    {
531        setcookie($cookie->getName(),null,0,$cookie->getPath(),$cookie->getDomain(),$cookie->getSecure());
532    }
533
534    /**
535     * @return string the type of HTML writer to be used, defaults to THtmlWriter
536     */
537    public function getHtmlWriterType()
538    {
539        return $this->_htmlWriterType;
540    }
541
542    /**
543     * @param string the type of HTML writer to be used, may be the class name or the namespace
544     */
545    public function setHtmlWriterType($value)
546    {
547        $this->_htmlWriterType=$value;
548    }
549
550    /**
551     * Creates a new instance of HTML writer.
552     * If the type of the HTML writer is not supplied, {@link getHtmlWriterType HtmlWriterType} will be assumed.
553     * @param string type of the HTML writer to be created. If null, {@link getHtmlWriterType HtmlWriterType} will be assumed.
554     */
555    public function createHtmlWriter($type=null)
556    {
557        if($type===null)
558            $type=$this->getHtmlWriterType();
559        if($this->getHasAdapter())
560            return $this->_adapter->createNewHtmlWriter($type, $this);
561        else
562             return $this->createNewHtmlWriter($type, $this);
563    }
564
565    /**
566     * Create a new html writer instance.
567     * This method is used internally. Please use {@link createHtmlWriter} instead.
568     * @param string type of HTML writer to be created.
569     * @param ITextWriter text writer holding the contents.
570     */
571    public function createNewHtmlWriter($type, $writer)
572    {
573        return Prado::createComponent($type, $writer);
574    }
575}
576
577?>
Note: See TracBrowser for help on using the browser.