blob: 98a941af5d7aca9e30a91526bee111b272cc9df4 [file] [log] [blame]
Derek Jones0b59f272008-05-13 04:22:33 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allardd2df9bc2007-04-15 17:41:17 +00002/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 4.3.2 or newer
6 *
7 * @package CodeIgniter
Derek Allard3d879d52008-01-18 19:41:32 +00008 * @author ExpressionEngine Dev Team
Rick Ellise4a7db42008-09-12 23:35:09 +00009 * @copyright Copyright (c) 2008, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allardd2df9bc2007-04-15 17:41:17 +000012 * @since Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Output Class
20 *
21 * Responsible for sending final output to browser
22 *
23 * @package CodeIgniter
24 * @subpackage Libraries
25 * @category Output
Derek Allard3d879d52008-01-18 19:41:32 +000026 * @author ExpressionEngine Dev Team
Derek Jones7a9193a2008-01-21 18:39:20 +000027 * @link http://codeigniter.com/user_guide/libraries/output.html
Derek Allardd2df9bc2007-04-15 17:41:17 +000028 */
29class CI_Output {
30
31 var $final_output;
32 var $cache_expiration = 0;
33 var $headers = array();
34 var $enable_profiler = FALSE;
35
36
37 function CI_Output()
38 {
39 log_message('debug', "Output Class Initialized");
40 }
41
42 // --------------------------------------------------------------------
43
44 /**
45 * Get Output
46 *
47 * Returns the current output string
48 *
49 * @access public
50 * @return string
51 */
52 function get_output()
53 {
54 return $this->final_output;
55 }
56
57 // --------------------------------------------------------------------
58
59 /**
60 * Set Output
61 *
62 * Sets the output string
63 *
64 * @access public
65 * @param string
66 * @return void
67 */
68 function set_output($output)
69 {
70 $this->final_output = $output;
71 }
72
73 // --------------------------------------------------------------------
Derek Allard66f07242008-01-18 22:36:14 +000074
75 /**
76 * Append Output
77 *
78 * Appends data onto the output string
79 *
80 * @access public
81 * @param string
82 * @return void
83 */
84 function append_output($output)
85 {
86 if ($this->final_output == '')
87 {
88 $this->final_output = $output;
89 }
90 else
91 {
92 $this->final_output .= $output;
93 }
94 }
95
96 // --------------------------------------------------------------------
Rick Ellisc3b33be2008-10-01 01:31:36 +000097
Derek Allardd2df9bc2007-04-15 17:41:17 +000098 /**
99 * Set Header
100 *
101 * Lets you set a server header which will be outputted with the final display.
102 *
103 * Note: If a file is cached, headers will not be sent. We need to figure out
104 * how to permit header data to be saved with the cache data...
105 *
106 * @access public
107 * @param string
108 * @return void
109 */
Rick Ellisc3b33be2008-10-01 01:31:36 +0000110 function set_header($header, $replace = TRUE)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000111 {
Rick Ellisc3b33be2008-10-01 01:31:36 +0000112 $this->headers[] = array($header, $replace);
113 }
114
115 // --------------------------------------------------------------------
116
117 /**
118 * Set HTTP Status Header
119 *
120 * @access public
121 * @param int the status code
122 * @param string
123 * @return void
124 */
Rick Ellis0dc925f2008-10-02 01:03:21 +0000125 function set_status_header($code = '200', $text = '')
126 {
127 $stati = array(
128 '200' => 'OK',
129 '201' => 'Created',
130 '202' => 'Accepted',
131 '203' => 'Non-Authoritative Information',
132 '204' => 'No Content',
133 '205' => 'Reset Content',
134 '206' => 'Partial Content',
135
136 '300' => 'Multiple Choices',
137 '301' => 'Moved Permanently',
138 '302' => 'Found',
139 '304' => 'Not Modified',
140 '305' => 'Use Proxy',
141 '307' => 'Temporary Redirect',
142
143 '400' => 'Bad Request',
144 '401' => 'Unauthorized',
145 '403' => 'Forbidden',
146 '404' => 'Not Found',
147 '405' => 'Method Not Allowed',
148 '406' => 'Not Acceptable',
149 '407' => 'Proxy Authentication Required',
150 '408' => 'Request Timeout',
151 '409' => 'Conflict',
152 '410' => 'Gone',
153 '411' => 'Length Required',
154 '412' => 'Precondition Failed',
155 '413' => 'Request Entity Too Large',
156 '414' => 'Request-URI Too Long',
157 '415' => 'Unsupported Media Type',
158 '416' => 'Requested Range Not Satisfiable',
159 '417' => 'Expectation Failed',
160
161 '500' => 'Internal Server Error',
162 '501' => 'Not Implemented',
163 '502' => 'Bad Gateway',
164 '503' => 'Service Unavailable',
165 '504' => 'Gateway Timeout',
166 '505' => 'HTTP Version Not Supported'
167 );
168
169 if ($code == '' OR ! is_numeric($code))
Rick Ellisc3b33be2008-10-01 01:31:36 +0000170 {
Rick Ellis0dc925f2008-10-02 01:03:21 +0000171 show_error('Status codes must be numeric');
Rick Ellisc3b33be2008-10-01 01:31:36 +0000172 }
Rick Ellis0dc925f2008-10-02 01:03:21 +0000173
174 if (isset($stati[$code]) AND $text == '')
175 {
176 $text = $stati[$code];
177 }
178
Rick Ellisc3b33be2008-10-01 01:31:36 +0000179 if ($text == '')
180 {
Rick Ellis0dc925f2008-10-02 01:03:21 +0000181 show_error('No status text available. Please check your status code number or supply your own message text.');
Rick Ellisc3b33be2008-10-01 01:31:36 +0000182 }
Rick Ellis0dc925f2008-10-02 01:03:21 +0000183
184 $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
Rick Ellisc3b33be2008-10-01 01:31:36 +0000185
186 if (substr(php_sapi_name(), 0, 3) == 'cgi')
187 {
Rick Ellis0dc925f2008-10-02 01:03:21 +0000188 header("Status: {$code} {$text}", TRUE);
Rick Ellisc3b33be2008-10-01 01:31:36 +0000189 }
Rick Ellis0dc925f2008-10-02 01:03:21 +0000190 elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0')
Rick Ellisc3b33be2008-10-01 01:31:36 +0000191 {
Rick Ellis0dc925f2008-10-02 01:03:21 +0000192 header($server_protocol." {$code} {$text}", TRUE, $code);
Rick Ellisc3b33be2008-10-01 01:31:36 +0000193 }
194 else
195 {
Rick Ellis0dc925f2008-10-02 01:03:21 +0000196 header("HTTP/1.1 {$code} {$text}", TRUE, $code);
Rick Ellisc3b33be2008-10-01 01:31:36 +0000197 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000198 }
199
200 // --------------------------------------------------------------------
201
202 /**
203 * Enable/disable Profiler
204 *
205 * @access public
206 * @param bool
207 * @return void
208 */
209 function enable_profiler($val = TRUE)
210 {
211 $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
212 }
213
214 // --------------------------------------------------------------------
215
216 /**
217 * Set Cache
218 *
219 * @access public
220 * @param integer
221 * @return void
222 */
223 function cache($time)
224 {
Derek Jones0b59f272008-05-13 04:22:33 +0000225 $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000226 }
227
228 // --------------------------------------------------------------------
229
230 /**
231 * Display Output
232 *
233 * All "view" data is automatically put into this variable by the controller class:
234 *
235 * $this->final_output
236 *
237 * This function sends the finalized output data to the browser along
238 * with any server headers and profile data. It also stops the
239 * benchmark timer so the page rendering speed and memory usage can be shown.
240 *
241 * @access public
242 * @return mixed
243 */
244 function _display($output = '')
245 {
246 // Note: We use globals because we can't use $CI =& get_instance()
247 // since this function is sometimes called by the caching mechanism,
248 // which happens before the CI super object is available.
249 global $BM, $CFG;
250
251 // --------------------------------------------------------------------
252
253 // Set the output data
254 if ($output == '')
255 {
256 $output =& $this->final_output;
257 }
258
259 // --------------------------------------------------------------------
260
261 // Do we need to write a cache file?
262 if ($this->cache_expiration > 0)
263 {
264 $this->_write_cache($output);
265 }
266
267 // --------------------------------------------------------------------
268
269 // Parse out the elapsed time and memory usage,
270 // then swap the pseudo-variables with the data
Rick Ellisc3b33be2008-10-01 01:31:36 +0000271
Derek Allardd2df9bc2007-04-15 17:41:17 +0000272 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
273 $output = str_replace('{elapsed_time}', $elapsed, $output);
274
Derek Jones0b59f272008-05-13 04:22:33 +0000275 $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
Derek Allardd2df9bc2007-04-15 17:41:17 +0000276 $output = str_replace('{memory_usage}', $memory, $output);
277
278 // --------------------------------------------------------------------
279
280 // Is compression requested?
281 if ($CFG->item('compress_output') === TRUE)
282 {
283 if (extension_loaded('zlib'))
284 {
285 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
286 {
287 ob_start('ob_gzhandler');
288 }
289 }
290 }
291
292 // --------------------------------------------------------------------
293
294 // Are there any server headers to send?
295 if (count($this->headers) > 0)
296 {
297 foreach ($this->headers as $header)
298 {
Rick Ellisc3b33be2008-10-01 01:31:36 +0000299 @header($header[0], $header[1]);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000300 }
301 }
302
303 // --------------------------------------------------------------------
304
305 // Does the get_instance() function exist?
306 // If not we know we are dealing with a cache file so we'll
307 // simply echo out the data and exit.
Derek Jones0b59f272008-05-13 04:22:33 +0000308 if ( ! function_exists('get_instance'))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000309 {
310 echo $output;
311 log_message('debug', "Final output sent to browser");
312 log_message('debug', "Total execution time: ".$elapsed);
313 return TRUE;
314 }
315
316 // --------------------------------------------------------------------
317
318 // Grab the super object. We'll need it in a moment...
319 $CI =& get_instance();
320
321 // Do we need to generate profile data?
322 // If so, load the Profile class and run it.
323 if ($this->enable_profiler == TRUE)
324 {
325 $CI->load->library('profiler');
326
327 // If the output data contains closing </body> and </html> tags
328 // we will remove them and add them back after we insert the profile data
329 if (preg_match("|</body>.*?</html>|is", $output))
330 {
331 $output = preg_replace("|</body>.*?</html>|is", '', $output);
332 $output .= $CI->profiler->run();
333 $output .= '</body></html>';
334 }
335 else
336 {
337 $output .= $CI->profiler->run();
338 }
339 }
340
341 // --------------------------------------------------------------------
342
343 // Does the controller contain a function named _output()?
344 // If so send the output there. Otherwise, echo it.
345 if (method_exists($CI, '_output'))
346 {
347 $CI->_output($output);
348 }
349 else
350 {
351 echo $output; // Send it to the browser!
352 }
353
354 log_message('debug', "Final output sent to browser");
355 log_message('debug', "Total execution time: ".$elapsed);
356 }
357
358 // --------------------------------------------------------------------
359
360 /**
361 * Write a Cache File
362 *
363 * @access public
364 * @return void
365 */
366 function _write_cache($output)
367 {
368 $CI =& get_instance();
369 $path = $CI->config->item('cache_path');
370
371 $cache_path = ($path == '') ? BASEPATH.'cache/' : $path;
372
Derek Jones0b59f272008-05-13 04:22:33 +0000373 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000374 {
375 return;
376 }
377
378 $uri = $CI->config->item('base_url').
379 $CI->config->item('index_page').
380 $CI->uri->uri_string();
381
382 $cache_path .= md5($uri);
383
Rick Ellisc3b33be2008-10-01 01:31:36 +0000384 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000385 {
Derek Allardd8856802007-05-24 03:49:37 +0000386 log_message('error', "Unable to write cache file: ".$cache_path);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000387 return;
388 }
389
390 $expire = time() + ($this->cache_expiration * 60);
391
Rick Ellis9f0de792008-10-07 01:09:40 +0000392 if (flock($fp, LOCK_EX))
393 {
394 fwrite($fp, $expire.'TS--->'.$output);
395 flock($fp, LOCK_UN);
396 }
397 else
398 {
399 log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
400 return;
401 }
Derek Allardd2df9bc2007-04-15 17:41:17 +0000402 fclose($fp);
Derek Jones3ad8efe2008-04-04 18:56:04 +0000403 @chmod($cache_path, DIR_WRITE_MODE);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000404
405 log_message('debug', "Cache file written: ".$cache_path);
406 }
407
408 // --------------------------------------------------------------------
409
410 /**
411 * Update/serve a cached file
412 *
413 * @access public
414 * @return void
415 */
Derek Allard57aea152008-06-06 14:17:57 +0000416 function _display_cache(&$CFG, &$URI)
Derek Allardd2df9bc2007-04-15 17:41:17 +0000417 {
Derek Allardd2df9bc2007-04-15 17:41:17 +0000418 $cache_path = ($CFG->item('cache_path') == '') ? BASEPATH.'cache/' : $CFG->item('cache_path');
419
Derek Jones0b59f272008-05-13 04:22:33 +0000420 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000421 {
422 return FALSE;
423 }
424
425 // Build the file path. The file name is an MD5 hash of the full URI
426 $uri = $CFG->item('base_url').
427 $CFG->item('index_page').
Rick Ellis30b40152007-07-20 00:01:13 +0000428 $URI->uri_string;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000429
430 $filepath = $cache_path.md5($uri);
431
Derek Jones0b59f272008-05-13 04:22:33 +0000432 if ( ! @file_exists($filepath))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000433 {
434 return FALSE;
435 }
436
Rick Ellisc3b33be2008-10-01 01:31:36 +0000437 if ( ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000438 {
439 return FALSE;
440 }
441
442 flock($fp, LOCK_SH);
443
444 $cache = '';
445 if (filesize($filepath) > 0)
446 {
447 $cache = fread($fp, filesize($filepath));
448 }
449
450 flock($fp, LOCK_UN);
451 fclose($fp);
452
453 // Strip out the embedded timestamp
Derek Jones0b59f272008-05-13 04:22:33 +0000454 if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
Derek Allardd2df9bc2007-04-15 17:41:17 +0000455 {
456 return FALSE;
457 }
458
459 // Has the file expired? If so we'll delete it.
460 if (time() >= trim(str_replace('TS--->', '', $match['1'])))
461 {
462 @unlink($filepath);
463 log_message('debug', "Cache file has expired. File deleted");
464 return FALSE;
465 }
466
467 // Display the cache
468 $this->_display(str_replace($match['0'], '', $cache));
469 log_message('debug', "Cache file is current. Sending it to browser.");
470 return TRUE;
471 }
472
473
474}
475// END Output Class
Derek Jones0b59f272008-05-13 04:22:33 +0000476
477/* End of file Output.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000478/* Location: ./system/libraries/Output.php */