blob: b5484ac8129a6c7929d2b758d92c563957b4ebcd [file] [log] [blame]
Andrey Andreev1f5fbb62012-01-07 20:53:29 +02001<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
3 * CodeIgniter
4 *
Phil Sturgeon07c1ac82012-03-09 17:03:37 +00005 * An open source application development framework for PHP 5.2.4 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
Andrey Andreev1f5fbb62012-01-07 20:53:29 +02008 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05009 * Licensed under the Open Software License version 3.0
Andrey Andreev1f5fbb62012-01-07 20:53:29 +020010 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -050011 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Derek Allard2067d1a2008-11-13 22:59:24 +000019 * @package CodeIgniter
Derek Jonesf4a4bd82011-10-20 12:18:42 -050020 * @author EllisLab Dev Team
Greg Aker0defe5d2012-01-01 18:46:41 -060021 * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
Derek Jonesf4a4bd82011-10-20 12:18:42 -050022 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Derek Allard2067d1a2008-11-13 22:59:24 +000023 * @link http://codeigniter.com
24 * @since Version 1.0
25 * @filesource
26 */
27
Derek Allard2067d1a2008-11-13 22:59:24 +000028/**
29 * Output Class
30 *
31 * Responsible for sending final output to browser
32 *
33 * @package CodeIgniter
34 * @subpackage Libraries
35 * @category Output
Derek Jonesf4a4bd82011-10-20 12:18:42 -050036 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000037 * @link http://codeigniter.com/user_guide/libraries/output.html
38 */
39class CI_Output {
40
David Behler07b53422011-08-15 00:25:06 +020041 /**
42 * Current output string
43 *
44 * @var string
David Behler07b53422011-08-15 00:25:06 +020045 */
Andrey Andreev0f221172012-04-13 14:52:16 +030046 public $final_output;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030047
David Behler07b53422011-08-15 00:25:06 +020048 /**
49 * Cache expiration time
50 *
51 * @var int
David Behler07b53422011-08-15 00:25:06 +020052 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040053 public $cache_expiration = 0;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030054
David Behler07b53422011-08-15 00:25:06 +020055 /**
56 * List of server headers
57 *
58 * @var array
David Behler07b53422011-08-15 00:25:06 +020059 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040060 public $headers = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030061
David Behler07b53422011-08-15 00:25:06 +020062 /**
63 * List of mime types
64 *
65 * @var array
David Behler07b53422011-08-15 00:25:06 +020066 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040067 public $mime_types = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030068
David Behler07b53422011-08-15 00:25:06 +020069 /**
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080070 * Mime-type for the current page
71 *
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -080072 * @var string
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080073 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -080074 protected $mime_type = 'text/html';
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080075 /**
David Behler07b53422011-08-15 00:25:06 +020076 * Determines wether profiler is enabled
77 *
78 * @var book
David Behler07b53422011-08-15 00:25:06 +020079 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040080 public $enable_profiler = FALSE;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030081
David Behler07b53422011-08-15 00:25:06 +020082 /**
83 * Determines if output compression is enabled
84 *
85 * @var bool
David Behler07b53422011-08-15 00:25:06 +020086 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040087 protected $_zlib_oc = FALSE;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030088
David Behler07b53422011-08-15 00:25:06 +020089 /**
90 * List of profiler sections
91 *
92 * @var array
David Behler07b53422011-08-15 00:25:06 +020093 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040094 protected $_profiler_sections = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030095
David Behler07b53422011-08-15 00:25:06 +020096 /**
97 * Whether or not to parse variables like {elapsed_time} and {memory_usage}
98 *
99 * @var bool
David Behler07b53422011-08-15 00:25:06 +0200100 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -0400101 public $parse_exec_vars = TRUE;
Derek Jonesee71c802010-03-10 10:05:05 -0600102
Timothy Warrenad475052012-04-19 13:21:06 -0400103 /**
104 * Set up Output class
Andrey Andreev92ebfb62012-05-17 12:49:24 +0300105 *
106 * @return void
Timothy Warrenad475052012-04-19 13:21:06 -0400107 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200108 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +0000109 {
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300110 $this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
Barry Mienydd671972010-10-04 16:33:58 +0200111
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000112 // Get mime types for later
Andrey Andreev7b53d042012-03-26 23:02:32 +0300113 if (defined('ENVIRONMENT') && file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
Greg Akerd96f8822011-12-27 16:23:47 -0600114 {
Phil Sturgeon39b1c112012-06-04 16:51:14 -0500115 $this->mime_types = include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
Greg Akerd96f8822011-12-27 16:23:47 -0600116 }
117 else
118 {
Phil Sturgeon39b1c112012-06-04 16:51:14 -0500119 $this->mime_types = include APPPATH.'config/mimes.php';
Greg Akerd96f8822011-12-27 16:23:47 -0600120 }
121
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200122 log_message('debug', 'Output Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000123 }
Barry Mienydd671972010-10-04 16:33:58 +0200124
Derek Allard2067d1a2008-11-13 22:59:24 +0000125 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200126
Derek Allard2067d1a2008-11-13 22:59:24 +0000127 /**
128 * Get Output
129 *
130 * Returns the current output string
131 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000132 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200133 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200134 public function get_output()
Derek Allard2067d1a2008-11-13 22:59:24 +0000135 {
136 return $this->final_output;
137 }
Barry Mienydd671972010-10-04 16:33:58 +0200138
Derek Allard2067d1a2008-11-13 22:59:24 +0000139 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200140
Derek Allard2067d1a2008-11-13 22:59:24 +0000141 /**
142 * Set Output
143 *
144 * Sets the output string
145 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000146 * @param string
147 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200148 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200149 public function set_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000150 {
151 $this->final_output = $output;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000152 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000153 }
154
155 // --------------------------------------------------------------------
156
157 /**
158 * Append Output
159 *
160 * Appends data onto the output string
161 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000162 * @param string
163 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200164 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200165 public function append_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000166 {
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300167 if ($this->final_output == '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000168 {
169 $this->final_output = $output;
170 }
171 else
172 {
173 $this->final_output .= $output;
174 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000175
176 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000177 }
178
179 // --------------------------------------------------------------------
180
181 /**
182 * Set Header
183 *
184 * Lets you set a server header which will be outputted with the final display.
185 *
Andrey Andreev92ebfb62012-05-17 12:49:24 +0300186 * Note: If a file is cached, headers will not be sent. We need to figure out
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 * how to permit header data to be saved with the cache data...
188 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000189 * @param string
David Behler07b53422011-08-15 00:25:06 +0200190 * @param bool
Derek Allard2067d1a2008-11-13 22:59:24 +0000191 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200192 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200193 public function set_header($header, $replace = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000194 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200195 // If zlib.output_compression is enabled it will compress the output,
196 // but it will not modify the content-length header to compensate for
197 // the reduction, causing the browser to hang waiting for more data.
198 // We'll just skip content-length in those cases.
Alex Bilbieed944a32012-06-02 11:07:47 +0100199 if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200200 {
201 return;
202 }
Barry Mienydd671972010-10-04 16:33:58 +0200203
Derek Allard2067d1a2008-11-13 22:59:24 +0000204 $this->headers[] = array($header, $replace);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000205 return $this;
206 }
207
208 // --------------------------------------------------------------------
209
210 /**
211 * Set Content Type Header
212 *
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000213 * @param string extension of the file we're outputting
214 * @return void
215 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200216 public function set_content_type($mime_type)
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000217 {
218 if (strpos($mime_type, '/') === FALSE)
219 {
220 $extension = ltrim($mime_type, '.');
221
222 // Is this extension supported?
223 if (isset($this->mime_types[$extension]))
224 {
225 $mime_type =& $this->mime_types[$extension];
226
227 if (is_array($mime_type))
228 {
229 $mime_type = current($mime_type);
230 }
231 }
232 }
Thor (atiredmachine)242115c2012-01-24 23:08:11 -0800233
234 $this->mime_type = $mime_type;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000235
236 $header = 'Content-Type: '.$mime_type;
237
238 $this->headers[] = array($header, TRUE);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000239 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000240 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300241
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700242 // --------------------------------------------------------------------
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300243
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700244 /**
245 * Get Current Content Type Header
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700246 *
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300247 * @return string 'text/html', if not already set
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700248 */
Songpol Sripaoeiam614db072012-04-03 01:29:28 +0700249 public function get_content_type()
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700250 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300251 for ($i = 0, $c = count($this->headers); $i < $c; $i++)
Songpol Sripao-eiam38c0a722012-04-01 20:10:35 +0700252 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300253 if (preg_match('/^Content-Type:\s(.+)$/', $this->headers[$i][0], $matches))
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700254 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300255 return $matches[1];
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700256 }
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700257 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300258
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700259 return 'text/html';
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700260 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000261
262 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200263
Derek Allard2067d1a2008-11-13 22:59:24 +0000264 /**
265 * Set HTTP Status Header
Derek Jones817163a2009-07-11 17:05:58 +0000266 * moved to Common procedural functions in 1.7.2
Barry Mienydd671972010-10-04 16:33:58 +0200267 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300268 * @param int the status code
Barry Mienydd671972010-10-04 16:33:58 +0200269 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000270 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200271 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200272 public function set_status_header($code = 200, $text = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000273 {
Derek Jones817163a2009-07-11 17:05:58 +0000274 set_status_header($code, $text);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000275 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000276 }
Barry Mienydd671972010-10-04 16:33:58 +0200277
Derek Allard2067d1a2008-11-13 22:59:24 +0000278 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200279
Derek Allard2067d1a2008-11-13 22:59:24 +0000280 /**
281 * Enable/disable Profiler
282 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000283 * @param bool
284 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200285 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200286 public function enable_profiler($val = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000287 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300288 $this->enable_profiler = is_bool($val) ? $val : TRUE;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000289 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000290 }
Barry Mienydd671972010-10-04 16:33:58 +0200291
Derek Allard2067d1a2008-11-13 22:59:24 +0000292 // --------------------------------------------------------------------
Derek Jonesee71c802010-03-10 10:05:05 -0600293
294 /**
295 * Set Profiler Sections
296 *
297 * Allows override of default / config settings for Profiler section display
298 *
Derek Jonesee71c802010-03-10 10:05:05 -0600299 * @param array
300 * @return void
301 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200302 public function set_profiler_sections($sections)
Derek Jonesee71c802010-03-10 10:05:05 -0600303 {
304 foreach ($sections as $section => $enable)
305 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300306 $this->_profiler_sections[$section] = ($enable !== FALSE);
Derek Jonesee71c802010-03-10 10:05:05 -0600307 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000308
309 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600310 }
311
312 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200313
Derek Allard2067d1a2008-11-13 22:59:24 +0000314 /**
315 * Set Cache
316 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300317 * @param int
Derek Allard2067d1a2008-11-13 22:59:24 +0000318 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200319 */
Michiel Vugteveen0609d582012-01-08 13:26:17 +0100320 public function cache($time)
Derek Allard2067d1a2008-11-13 22:59:24 +0000321 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300322 $this->cache_expiration = is_numeric($time) ? $time : 0;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000323 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000324 }
Barry Mienydd671972010-10-04 16:33:58 +0200325
Derek Allard2067d1a2008-11-13 22:59:24 +0000326 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200327
Derek Allard2067d1a2008-11-13 22:59:24 +0000328 /**
329 * Display Output
330 *
331 * All "view" data is automatically put into this variable by the controller class:
332 *
333 * $this->final_output
334 *
335 * This function sends the finalized output data to the browser along
Andrey Andreev7b53d042012-03-26 23:02:32 +0300336 * with any server headers and profile data. It also stops the
Derek Allard2067d1a2008-11-13 22:59:24 +0000337 * benchmark timer so the page rendering speed and memory usage can be shown.
338 *
David Behler07b53422011-08-15 00:25:06 +0200339 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000340 * @return mixed
Barry Mienydd671972010-10-04 16:33:58 +0200341 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200342 public function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200343 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500344 // Note: We use globals because we can't use $CI =& get_instance()
Derek Allard2067d1a2008-11-13 22:59:24 +0000345 // since this function is sometimes called by the caching mechanism,
346 // which happens before the CI super object is available.
347 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500348
349 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600350 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500351 {
352 $CI =& get_instance();
353 }
354
Derek Allard2067d1a2008-11-13 22:59:24 +0000355 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200356
Derek Allard2067d1a2008-11-13 22:59:24 +0000357 // Set the output data
Alex Bilbieed944a32012-06-02 11:07:47 +0100358 if ($output === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000359 {
360 $output =& $this->final_output;
361 }
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800362
363 // --------------------------------------------------------------------
364
365 // Is minify requested?
366 if ($CFG->item('minify_output') === TRUE)
367 {
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800368 $output = $this->minify($output, $this->mime_type);
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800369 }
370
Barry Mienydd671972010-10-04 16:33:58 +0200371
Derek Allard2067d1a2008-11-13 22:59:24 +0000372 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200373
Derek Jones37f4b9c2011-07-01 17:56:50 -0500374 // Do we need to write a cache file? Only if the controller does not have its
Derek Jonesd7633492010-09-28 13:14:57 -0500375 // own _output() method and we are not dealing with a cache file, which we
376 // can determine by the existence of the $CI object above
377 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000378 {
379 $this->_write_cache($output);
380 }
Barry Mienydd671972010-10-04 16:33:58 +0200381
Derek Allard2067d1a2008-11-13 22:59:24 +0000382 // --------------------------------------------------------------------
383
384 // Parse out the elapsed time and memory usage,
385 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200386
387 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600388
389 if ($this->parse_exec_vars === TRUE)
390 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300391 $memory = function_exists('memory_get_usage') ? round(memory_get_usage()/1024/1024, 2).'MB' : '0';
Barry Mienydd671972010-10-04 16:33:58 +0200392
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200393 $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
Derek Jones46520492010-03-02 13:53:25 -0600394 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000395
396 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200397
Derek Allard2067d1a2008-11-13 22:59:24 +0000398 // Is compression requested?
Alex Bilbieed944a32012-06-02 11:07:47 +0100399 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200400 && extension_loaded('zlib')
401 && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000402 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200403 ob_start('ob_gzhandler');
Derek Allard2067d1a2008-11-13 22:59:24 +0000404 }
405
406 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200407
Derek Allard2067d1a2008-11-13 22:59:24 +0000408 // Are there any server headers to send?
409 if (count($this->headers) > 0)
410 {
411 foreach ($this->headers as $header)
412 {
413 @header($header[0], $header[1]);
414 }
Barry Mienydd671972010-10-04 16:33:58 +0200415 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000416
417 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200418
Derek Jonesd7633492010-09-28 13:14:57 -0500419 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000420 // If not we know we are dealing with a cache file so we'll
421 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500422 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000423 {
424 echo $output;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200425 log_message('debug', 'Final output sent to browser');
426 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000427 return TRUE;
428 }
Barry Mienydd671972010-10-04 16:33:58 +0200429
Derek Allard2067d1a2008-11-13 22:59:24 +0000430 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200431
Derek Allard2067d1a2008-11-13 22:59:24 +0000432 // Do we need to generate profile data?
433 // If so, load the Profile class and run it.
Alex Bilbieed944a32012-06-02 11:07:47 +0100434 if ($this->enable_profiler === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000435 {
Barry Mienydd671972010-10-04 16:33:58 +0200436 $CI->load->library('profiler');
Derek Jonesee71c802010-03-10 10:05:05 -0600437 if ( ! empty($this->_profiler_sections))
438 {
439 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200440 }
Derek Jonesee71c802010-03-10 10:05:05 -0600441
Derek Allard2067d1a2008-11-13 22:59:24 +0000442 // If the output data contains closing </body> and </html> tags
443 // we will remove them and add them back after we insert the profile data
Andrey Andreevcba20b12012-01-09 10:16:41 +0200444 $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
445 if ($count > 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000446 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000447 $output .= '</body></html>';
448 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 }
Barry Mienydd671972010-10-04 16:33:58 +0200450
Derek Allard2067d1a2008-11-13 22:59:24 +0000451 // Does the controller contain a function named _output()?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500452 // If so send the output there. Otherwise, echo it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000453 if (method_exists($CI, '_output'))
454 {
455 $CI->_output($output);
456 }
457 else
458 {
Andrey Andreevedc87552012-01-09 09:35:10 +0200459 echo $output; // Send it to the browser!
Derek Allard2067d1a2008-11-13 22:59:24 +0000460 }
Barry Mienydd671972010-10-04 16:33:58 +0200461
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200462 log_message('debug', 'Final output sent to browser');
463 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000464 }
Barry Mienydd671972010-10-04 16:33:58 +0200465
Derek Allard2067d1a2008-11-13 22:59:24 +0000466 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200467
Derek Allard2067d1a2008-11-13 22:59:24 +0000468 /**
469 * Write a Cache File
470 *
David Behler07b53422011-08-15 00:25:06 +0200471 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000472 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200473 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200474 public function _write_cache($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000475 {
Barry Mienydd671972010-10-04 16:33:58 +0200476 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000477 $path = $CI->config->item('cache_path');
Alex Bilbieed944a32012-06-02 11:07:47 +0100478 $cache_path = ($path === '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200479
Derek Allard2067d1a2008-11-13 22:59:24 +0000480 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
481 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200482 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000483 return;
484 }
Barry Mienydd671972010-10-04 16:33:58 +0200485
Derek Allard2067d1a2008-11-13 22:59:24 +0000486 $uri = $CI->config->item('base_url').
487 $CI->config->item('index_page').
488 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200489
Derek Allard2067d1a2008-11-13 22:59:24 +0000490 $cache_path .= md5($uri);
491
492 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
493 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200494 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000495 return;
496 }
Barry Mienydd671972010-10-04 16:33:58 +0200497
Derek Allard2067d1a2008-11-13 22:59:24 +0000498 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200499
Derek Allard2067d1a2008-11-13 22:59:24 +0000500 if (flock($fp, LOCK_EX))
501 {
502 fwrite($fp, $expire.'TS--->'.$output);
503 flock($fp, LOCK_UN);
504 }
505 else
506 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200507 log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000508 return;
509 }
510 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000511 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000512
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200513 log_message('debug', 'Cache file written: '.$cache_path);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800514
515 // Send HTTP cache-control headers to browser to match file cache settings.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800516 $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000517 }
518
519 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200520
Derek Allard2067d1a2008-11-13 22:59:24 +0000521 /**
522 * Update/serve a cached file
523 *
David Behler07b53422011-08-15 00:25:06 +0200524 * @param object config class
525 * @param object uri class
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300526 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200527 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200528 public function _display_cache(&$CFG, &$URI)
Derek Allard2067d1a2008-11-13 22:59:24 +0000529 {
Alex Bilbieed944a32012-06-02 11:07:47 +0100530 $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200531
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200532 // Build the file path. The file name is an MD5 hash of the full URI
533 $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
Derek Allard2067d1a2008-11-13 22:59:24 +0000534 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200535
Andrey Andreevc90d6512012-01-08 04:35:02 +0200536 if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allard2067d1a2008-11-13 22:59:24 +0000537 {
538 return FALSE;
539 }
Barry Mienydd671972010-10-04 16:33:58 +0200540
Derek Allard2067d1a2008-11-13 22:59:24 +0000541 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200542
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200543 $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
Barry Mienydd671972010-10-04 16:33:58 +0200544
Derek Allard2067d1a2008-11-13 22:59:24 +0000545 flock($fp, LOCK_UN);
546 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200547
548 // Strip out the embedded timestamp
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200549 if ( ! preg_match('/(\d+TS--->)/', $cache, $match))
Derek Allard2067d1a2008-11-13 22:59:24 +0000550 {
551 return FALSE;
552 }
Barry Mienydd671972010-10-04 16:33:58 +0200553
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800554 $last_modified = filemtime($cache_path);
555 $expire = trim(str_replace('TS--->', '', $match[1]));
556
557 // Has the file expired?
558 if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
Derek Jonesd99e6032010-03-19 19:57:33 -0500559 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800560 // If so we'll delete it.
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200561 @unlink($filepath);
562 log_message('debug', 'Cache file has expired. File deleted.');
563 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000564 }
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800565 else
566 {
567 // Or else send the HTTP cache control headers.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800568 $this->set_cache_header($last_modified, $expire);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800569 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000570
571 // Display the cache
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200572 $this->_display(str_replace($match[0], '', $cache));
573 log_message('debug', 'Cache file is current. Sending it to browser.');
Derek Allard2067d1a2008-11-13 22:59:24 +0000574 return TRUE;
575 }
576
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800577
578 // --------------------------------------------------------------------
579 /**
580 * Set the HTTP headers to match the server-side file cache settings
581 * in order to reduce bandwidth.
582 *
583 * @param int timestamp of when the page was last modified
584 * @param int timestamp of when should the requested page expire from cache
585 * @return void
586 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800587 public function set_cache_header($last_modified, $expiration)
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800588 {
589 $max_age = $expiration - $_SERVER['REQUEST_TIME'];
590
591 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])))
592 {
593 $this->set_status_header(304);
594 exit;
595 }
596 else
597 {
598 header('Pragma: public');
599 header('Cache-Control: max-age=' . $max_age . ', public');
600 header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
601 header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
602 }
603 }
604
605
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800606
607
608 // --------------------------------------------------------------------
609 /**
610 * Reduce excessive size of HTML content.
611 *
612 * @param string
613 * @param string
614 * @return string
615 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800616 public function minify($output, $type='text/html')
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800617 {
618 switch ($type)
619 {
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800620 case 'text/html':
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800621
622 $size_before = strlen($output);
623
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800624 // Find all the <pre>,<code>,<textarea>, and <javascript> tags
625 // We'll want to return them to this unprocessed state later.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800626 preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
627 preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
628 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800629 preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800630
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800631 // Minify the CSS in all the <style> tags.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800632 preg_match_all('{<style.+</style>}msU', $output, $style_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800633 foreach ($style_clean[0] as $s)
634 {
Thor1b8d0ef2012-01-28 01:48:04 -0800635 $output = str_replace($s, $this->minify($s, 'text/css'), $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800636 }
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800637
638 // Minify the javascript in <script> tags.
639 foreach ($javascript_clean[0] as $s)
640 {
Thor1b8d0ef2012-01-28 01:48:04 -0800641 $javascript_mini[] = $this->minify($s, 'text/javascript');
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800642 }
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800643
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800644 // Replace multiple spaces with a single space.
Thor1b8d0ef2012-01-28 01:48:04 -0800645 $output = preg_replace('!\s{2,}!', ' ', $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800646
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800647 // Remove comments (non-MSIE conditionals)
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800648 $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800649
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800650 // Remove spaces around block-level elements.
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800651 $output = preg_replace('{\s*(</?(html|head|title|meta|script|link|style|body|h[1-6]|div|p|br).*>)\s+}msU', '$1', $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800652
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800653 // Replace mangled <pre> etc. tags with unprocessed ones.
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800654
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800655 if ( ! empty($pres_clean))
656 {
657 preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
658 $output = str_replace($pres_messed[0], $pres_clean[0], $output);
659 }
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800660
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800661 if ( ! empty($codes_clean))
662 {
663 preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
664 $output = str_replace($codes_messed[0], $codes_clean[0], $output);
665 }
666
667 if ( ! empty($codes_clean))
668 {
669 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
670 $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
671 }
672
673 if (isset($javascript_mini))
674 {
675 preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
676 $output = str_replace($javascript_messed[0], $javascript_mini, $output);
677 }
678
679 $size_removed = $size_before - strlen($output);
680 $savings_percent = round(($size_removed / $size_before * 100));
681
682 log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800683
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800684 break;
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800685
686
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800687 case 'text/css':
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800688
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800689 //Remove CSS comments
690 $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output);
691
692 // Remove spaces around curly brackets, colons,
693 // semi-colons, parenthesis, commas
694 $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output);
695
696 break;
697
698
699 case 'text/javascript':
700
Thor1b8d0ef2012-01-28 01:48:04 -0800701 // Currently leaves JavaScript untouched.
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800702 break;
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800703 }
704
705 return $output;
706 }
707
708
Derek Allard2067d1a2008-11-13 22:59:24 +0000709}
Derek Allard2067d1a2008-11-13 22:59:24 +0000710
711/* End of file Output.php */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200712/* Location: ./system/core/Output.php */