Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: THttpResponse.php 2541 2008-10-21 15:05:13Z qiang.xue $
10
 * @package System.Web
11
 */
12
 
13
/**
14
 * Includes the THttpResponse adapter.
15
 */
16
Prado::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: THttpResponse.php 2541 2008-10-21 15:05:13Z qiang.xue $
64
 * @package System.Web
65
 * @since 3.0
66
 */
67
class 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