blob: 6644b3bff06434b5718e7faf2fa37fb477b1a9ed [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
7 * @package CodeIgniter
8 * @author ExpressionEngine Dev Team
Greg Aker0711dc82011-01-05 10:49:40 -06009 * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
12 * @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
26 * @author ExpressionEngine Dev Team
27 * @link http://codeigniter.com/user_guide/libraries/output.html
28 */
29class CI_Output {
30
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000031 protected $final_output;
32 protected $cache_expiration = 0;
33 protected $headers = array();
34 protected $mime_types = array();
35 protected $enable_profiler = FALSE;
36 protected $parse_exec_vars = TRUE; // whether or not to parse variables like {elapsed_time} and {memory_usage}
Derek Allard2067d1a2008-11-13 22:59:24 +000037
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000038 protected $_zlib_oc = FALSE;
39 protected $_profiler_sections = array();
Derek Jonesee71c802010-03-10 10:05:05 -060040
Greg Akera9263282010-11-10 15:26:43 -060041 function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +000042 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +020043 $this->_zlib_oc = @ini_get('zlib.output_compression');
Barry Mienydd671972010-10-04 16:33:58 +020044
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000045 // Get mime types for later
46 include APPPATH.'config/mimes'.EXT;
47 $this->mime_types = $mimes;
48
Derek Allard2067d1a2008-11-13 22:59:24 +000049 log_message('debug', "Output Class Initialized");
50 }
Barry Mienydd671972010-10-04 16:33:58 +020051
Derek Allard2067d1a2008-11-13 22:59:24 +000052 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +020053
Derek Allard2067d1a2008-11-13 22:59:24 +000054 /**
55 * Get Output
56 *
57 * Returns the current output string
58 *
59 * @access public
60 * @return string
Barry Mienydd671972010-10-04 16:33:58 +020061 */
Derek Allard2067d1a2008-11-13 22:59:24 +000062 function get_output()
63 {
64 return $this->final_output;
65 }
Barry Mienydd671972010-10-04 16:33:58 +020066
Derek Allard2067d1a2008-11-13 22:59:24 +000067 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +020068
Derek Allard2067d1a2008-11-13 22:59:24 +000069 /**
70 * Set Output
71 *
72 * Sets the output string
73 *
74 * @access public
75 * @param string
76 * @return void
Barry Mienydd671972010-10-04 16:33:58 +020077 */
Derek Allard2067d1a2008-11-13 22:59:24 +000078 function set_output($output)
79 {
80 $this->final_output = $output;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000081
82 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +000083 }
84
85 // --------------------------------------------------------------------
86
87 /**
88 * Append Output
89 *
90 * Appends data onto the output string
91 *
92 * @access public
93 * @param string
94 * @return void
Barry Mienydd671972010-10-04 16:33:58 +020095 */
Derek Allard2067d1a2008-11-13 22:59:24 +000096 function append_output($output)
97 {
98 if ($this->final_output == '')
99 {
100 $this->final_output = $output;
101 }
102 else
103 {
104 $this->final_output .= $output;
105 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000106
107 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000108 }
109
110 // --------------------------------------------------------------------
111
112 /**
113 * Set Header
114 *
115 * Lets you set a server header which will be outputted with the final display.
116 *
117 * Note: If a file is cached, headers will not be sent. We need to figure out
118 * how to permit header data to be saved with the cache data...
119 *
120 * @access public
121 * @param string
122 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200123 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000124 function set_header($header, $replace = TRUE)
125 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200126 // If zlib.output_compression is enabled it will compress the output,
127 // but it will not modify the content-length header to compensate for
128 // the reduction, causing the browser to hang waiting for more data.
129 // We'll just skip content-length in those cases.
Barry Mienydd671972010-10-04 16:33:58 +0200130
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200131 if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
132 {
133 return;
134 }
Barry Mienydd671972010-10-04 16:33:58 +0200135
Derek Allard2067d1a2008-11-13 22:59:24 +0000136 $this->headers[] = array($header, $replace);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000137
138 return $this;
139 }
140
141 // --------------------------------------------------------------------
142
143 /**
144 * Set Content Type Header
145 *
146 * @access public
147 * @param string extension of the file we're outputting
148 * @return void
149 */
150 function set_content_type($mime_type)
151 {
152 if (strpos($mime_type, '/') === FALSE)
153 {
154 $extension = ltrim($mime_type, '.');
155
156 // Is this extension supported?
157 if (isset($this->mime_types[$extension]))
158 {
159 $mime_type =& $this->mime_types[$extension];
160
161 if (is_array($mime_type))
162 {
163 $mime_type = current($mime_type);
164 }
165 }
166 }
167
168 $header = 'Content-Type: '.$mime_type;
169
170 $this->headers[] = array($header, TRUE);
171
172 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000173 }
174
175 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200176
Derek Allard2067d1a2008-11-13 22:59:24 +0000177 /**
178 * Set HTTP Status Header
Derek Jones817163a2009-07-11 17:05:58 +0000179 * moved to Common procedural functions in 1.7.2
Barry Mienydd671972010-10-04 16:33:58 +0200180 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 * @access public
Barry Mienydd671972010-10-04 16:33:58 +0200182 * @param int the status code
183 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000184 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200185 */
Derek Jones46520492010-03-02 13:53:25 -0600186 function set_status_header($code = 200, $text = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 {
Derek Jones817163a2009-07-11 17:05:58 +0000188 set_status_header($code, $text);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000189
190 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000191 }
Barry Mienydd671972010-10-04 16:33:58 +0200192
Derek Allard2067d1a2008-11-13 22:59:24 +0000193 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200194
Derek Allard2067d1a2008-11-13 22:59:24 +0000195 /**
196 * Enable/disable Profiler
197 *
198 * @access public
199 * @param bool
200 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200201 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000202 function enable_profiler($val = TRUE)
203 {
204 $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000205
206 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000207 }
Barry Mienydd671972010-10-04 16:33:58 +0200208
Derek Allard2067d1a2008-11-13 22:59:24 +0000209 // --------------------------------------------------------------------
Derek Jonesee71c802010-03-10 10:05:05 -0600210
211 /**
212 * Set Profiler Sections
213 *
214 * Allows override of default / config settings for Profiler section display
215 *
216 * @access public
217 * @param array
218 * @return void
219 */
220 function set_profiler_sections($sections)
221 {
222 foreach ($sections as $section => $enable)
223 {
224 $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE;
225 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000226
227 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600228 }
229
230 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200231
Derek Allard2067d1a2008-11-13 22:59:24 +0000232 /**
233 * Set Cache
234 *
235 * @access public
236 * @param integer
237 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200238 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 function cache($time)
240 {
241 $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000242
243 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000244 }
Barry Mienydd671972010-10-04 16:33:58 +0200245
Derek Allard2067d1a2008-11-13 22:59:24 +0000246 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200247
Derek Allard2067d1a2008-11-13 22:59:24 +0000248 /**
249 * Display Output
250 *
251 * All "view" data is automatically put into this variable by the controller class:
252 *
253 * $this->final_output
254 *
255 * This function sends the finalized output data to the browser along
256 * with any server headers and profile data. It also stops the
257 * benchmark timer so the page rendering speed and memory usage can be shown.
258 *
259 * @access public
260 * @return mixed
Barry Mienydd671972010-10-04 16:33:58 +0200261 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000262 function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200263 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000264 // Note: We use globals because we can't use $CI =& get_instance()
265 // since this function is sometimes called by the caching mechanism,
266 // which happens before the CI super object is available.
267 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500268
269 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600270 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500271 {
272 $CI =& get_instance();
273 }
274
Derek Allard2067d1a2008-11-13 22:59:24 +0000275 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200276
Derek Allard2067d1a2008-11-13 22:59:24 +0000277 // Set the output data
278 if ($output == '')
279 {
280 $output =& $this->final_output;
281 }
Barry Mienydd671972010-10-04 16:33:58 +0200282
Derek Allard2067d1a2008-11-13 22:59:24 +0000283 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200284
Derek Jonesd7633492010-09-28 13:14:57 -0500285 // Do we need to write a cache file? Only if the controller does not have its
286 // own _output() method and we are not dealing with a cache file, which we
287 // can determine by the existence of the $CI object above
288 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000289 {
290 $this->_write_cache($output);
291 }
Barry Mienydd671972010-10-04 16:33:58 +0200292
Derek Allard2067d1a2008-11-13 22:59:24 +0000293 // --------------------------------------------------------------------
294
295 // Parse out the elapsed time and memory usage,
296 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200297
298 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600299
300 if ($this->parse_exec_vars === TRUE)
301 {
302 $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
Barry Mienydd671972010-10-04 16:33:58 +0200303
Derek Jones46520492010-03-02 13:53:25 -0600304 $output = str_replace('{elapsed_time}', $elapsed, $output);
305 $output = str_replace('{memory_usage}', $memory, $output);
306 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000307
308 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200309
Derek Allard2067d1a2008-11-13 22:59:24 +0000310 // Is compression requested?
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200311 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000312 {
313 if (extension_loaded('zlib'))
314 {
315 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
316 {
317 ob_start('ob_gzhandler');
318 }
319 }
320 }
321
322 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200323
Derek Allard2067d1a2008-11-13 22:59:24 +0000324 // Are there any server headers to send?
325 if (count($this->headers) > 0)
326 {
327 foreach ($this->headers as $header)
328 {
329 @header($header[0], $header[1]);
330 }
Barry Mienydd671972010-10-04 16:33:58 +0200331 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000332
333 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200334
Derek Jonesd7633492010-09-28 13:14:57 -0500335 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000336 // If not we know we are dealing with a cache file so we'll
337 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500338 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000339 {
340 echo $output;
341 log_message('debug', "Final output sent to browser");
342 log_message('debug', "Total execution time: ".$elapsed);
343 return TRUE;
344 }
Barry Mienydd671972010-10-04 16:33:58 +0200345
Derek Allard2067d1a2008-11-13 22:59:24 +0000346 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200347
Derek Allard2067d1a2008-11-13 22:59:24 +0000348 // Do we need to generate profile data?
349 // If so, load the Profile class and run it.
350 if ($this->enable_profiler == TRUE)
351 {
Barry Mienydd671972010-10-04 16:33:58 +0200352 $CI->load->library('profiler');
353
Derek Jonesee71c802010-03-10 10:05:05 -0600354 if ( ! empty($this->_profiler_sections))
355 {
356 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200357 }
Derek Jonesee71c802010-03-10 10:05:05 -0600358
Derek Allard2067d1a2008-11-13 22:59:24 +0000359 // If the output data contains closing </body> and </html> tags
360 // we will remove them and add them back after we insert the profile data
361 if (preg_match("|</body>.*?</html>|is", $output))
362 {
363 $output = preg_replace("|</body>.*?</html>|is", '', $output);
364 $output .= $CI->profiler->run();
365 $output .= '</body></html>';
366 }
367 else
368 {
369 $output .= $CI->profiler->run();
370 }
371 }
Barry Mienydd671972010-10-04 16:33:58 +0200372
Derek Allard2067d1a2008-11-13 22:59:24 +0000373 // --------------------------------------------------------------------
374
375 // Does the controller contain a function named _output()?
376 // If so send the output there. Otherwise, echo it.
377 if (method_exists($CI, '_output'))
378 {
379 $CI->_output($output);
380 }
381 else
382 {
383 echo $output; // Send it to the browser!
384 }
Barry Mienydd671972010-10-04 16:33:58 +0200385
Derek Allard2067d1a2008-11-13 22:59:24 +0000386 log_message('debug', "Final output sent to browser");
Barry Mienydd671972010-10-04 16:33:58 +0200387 log_message('debug', "Total execution time: ".$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000388 }
Barry Mienydd671972010-10-04 16:33:58 +0200389
Derek Allard2067d1a2008-11-13 22:59:24 +0000390 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200391
Derek Allard2067d1a2008-11-13 22:59:24 +0000392 /**
393 * Write a Cache File
394 *
395 * @access public
396 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200397 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000398 function _write_cache($output)
399 {
Barry Mienydd671972010-10-04 16:33:58 +0200400 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000401 $path = $CI->config->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200402
Greg Aker2eaa4072010-12-21 11:44:08 -0600403 $cache_path = ($path == '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200404
Derek Allard2067d1a2008-11-13 22:59:24 +0000405 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
406 {
Greg Akera7f5a942010-09-14 17:20:53 -0500407 log_message('error', "Unable to write cache file: ".$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000408 return;
409 }
Barry Mienydd671972010-10-04 16:33:58 +0200410
Derek Allard2067d1a2008-11-13 22:59:24 +0000411 $uri = $CI->config->item('base_url').
412 $CI->config->item('index_page').
413 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200414
Derek Allard2067d1a2008-11-13 22:59:24 +0000415 $cache_path .= md5($uri);
416
417 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
418 {
419 log_message('error', "Unable to write cache file: ".$cache_path);
420 return;
421 }
Barry Mienydd671972010-10-04 16:33:58 +0200422
Derek Allard2067d1a2008-11-13 22:59:24 +0000423 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200424
Derek Allard2067d1a2008-11-13 22:59:24 +0000425 if (flock($fp, LOCK_EX))
426 {
427 fwrite($fp, $expire.'TS--->'.$output);
428 flock($fp, LOCK_UN);
429 }
430 else
431 {
432 log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
433 return;
434 }
435 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000436 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000437
438 log_message('debug', "Cache file written: ".$cache_path);
439 }
440
441 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200442
Derek Allard2067d1a2008-11-13 22:59:24 +0000443 /**
444 * Update/serve a cached file
445 *
446 * @access public
447 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200448 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 function _display_cache(&$CFG, &$URI)
450 {
Greg Aker2eaa4072010-12-21 11:44:08 -0600451 $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200452
Derek Allard2067d1a2008-11-13 22:59:24 +0000453 // Build the file path. The file name is an MD5 hash of the full URI
454 $uri = $CFG->item('base_url').
455 $CFG->item('index_page').
456 $URI->uri_string;
Barry Mienydd671972010-10-04 16:33:58 +0200457
Derek Allard2067d1a2008-11-13 22:59:24 +0000458 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200459
Derek Allard2067d1a2008-11-13 22:59:24 +0000460 if ( ! @file_exists($filepath))
461 {
462 return FALSE;
463 }
Barry Mienydd671972010-10-04 16:33:58 +0200464
Derek Allard2067d1a2008-11-13 22:59:24 +0000465 if ( ! $fp = @fopen($filepath, FOPEN_READ))
466 {
467 return FALSE;
468 }
Barry Mienydd671972010-10-04 16:33:58 +0200469
Derek Allard2067d1a2008-11-13 22:59:24 +0000470 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200471
Derek Allard2067d1a2008-11-13 22:59:24 +0000472 $cache = '';
473 if (filesize($filepath) > 0)
474 {
475 $cache = fread($fp, filesize($filepath));
476 }
Barry Mienydd671972010-10-04 16:33:58 +0200477
Derek Allard2067d1a2008-11-13 22:59:24 +0000478 flock($fp, LOCK_UN);
479 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200480
481 // Strip out the embedded timestamp
Derek Allard2067d1a2008-11-13 22:59:24 +0000482 if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
483 {
484 return FALSE;
485 }
Barry Mienydd671972010-10-04 16:33:58 +0200486
Derek Allard2067d1a2008-11-13 22:59:24 +0000487 // Has the file expired? If so we'll delete it.
488 if (time() >= trim(str_replace('TS--->', '', $match['1'])))
Derek Jonesd99e6032010-03-19 19:57:33 -0500489 {
490 if (is_really_writable($cache_path))
491 {
492 @unlink($filepath);
493 log_message('debug', "Cache file has expired. File deleted");
Barry Mienydd671972010-10-04 16:33:58 +0200494 return FALSE;
Derek Jonesd99e6032010-03-19 19:57:33 -0500495 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000496 }
497
498 // Display the cache
499 $this->_display(str_replace($match['0'], '', $cache));
Barry Mienydd671972010-10-04 16:33:58 +0200500 log_message('debug', "Cache file is current. Sending it to browser.");
Derek Allard2067d1a2008-11-13 22:59:24 +0000501 return TRUE;
502 }
503
504
505}
506// END Output Class
507
508/* End of file Output.php */
Derek Jonesc68dfbf2010-03-02 12:59:23 -0600509/* Location: ./system/core/Output.php */