blob: 052367ed6199d4f6952714d8d721cc82475d43a4 [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 */
Andrey Andreev6ef498b2012-06-05 22:01:58 +030067 public $mimes = 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';
Andrey Andreev0140ddd2012-06-16 01:12:56 +030075
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080076 /**
Alex Bilbief512b732012-06-16 11:15:19 +010077 * Determines whether profiler is enabled
David Behler07b53422011-08-15 00:25:06 +020078 *
79 * @var book
David Behler07b53422011-08-15 00:25:06 +020080 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040081 public $enable_profiler = FALSE;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030082
David Behler07b53422011-08-15 00:25:06 +020083 /**
84 * Determines if output compression is enabled
85 *
86 * @var bool
David Behler07b53422011-08-15 00:25:06 +020087 */
Andrey Andreev0140ddd2012-06-16 01:12:56 +030088 protected $_zlib_oc = FALSE;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030089
David Behler07b53422011-08-15 00:25:06 +020090 /**
91 * List of profiler sections
92 *
93 * @var array
David Behler07b53422011-08-15 00:25:06 +020094 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040095 protected $_profiler_sections = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030096
David Behler07b53422011-08-15 00:25:06 +020097 /**
98 * Whether or not to parse variables like {elapsed_time} and {memory_usage}
99 *
100 * @var bool
David Behler07b53422011-08-15 00:25:06 +0200101 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -0400102 public $parse_exec_vars = TRUE;
Derek Jonesee71c802010-03-10 10:05:05 -0600103
Timothy Warrenad475052012-04-19 13:21:06 -0400104 /**
105 * Set up Output class
Andrey Andreev92ebfb62012-05-17 12:49:24 +0300106 *
107 * @return void
Timothy Warrenad475052012-04-19 13:21:06 -0400108 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200109 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +0000110 {
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300111 $this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
Barry Mienydd671972010-10-04 16:33:58 +0200112
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000113 // Get mime types for later
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300114 $this->mimes =& get_mimes();
Greg Akerd96f8822011-12-27 16:23:47 -0600115
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200116 log_message('debug', 'Output Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000117 }
Barry Mienydd671972010-10-04 16:33:58 +0200118
Derek Allard2067d1a2008-11-13 22:59:24 +0000119 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200120
Derek Allard2067d1a2008-11-13 22:59:24 +0000121 /**
122 * Get Output
123 *
124 * Returns the current output string
125 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000126 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200127 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200128 public function get_output()
Derek Allard2067d1a2008-11-13 22:59:24 +0000129 {
130 return $this->final_output;
131 }
Barry Mienydd671972010-10-04 16:33:58 +0200132
Derek Allard2067d1a2008-11-13 22:59:24 +0000133 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200134
Derek Allard2067d1a2008-11-13 22:59:24 +0000135 /**
136 * Set Output
137 *
138 * Sets the output string
139 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000140 * @param string
141 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200142 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200143 public function set_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000144 {
145 $this->final_output = $output;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000146 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000147 }
148
149 // --------------------------------------------------------------------
150
151 /**
152 * Append Output
153 *
154 * Appends data onto the output string
155 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000156 * @param string
157 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200158 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200159 public function append_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000160 {
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300161 if ($this->final_output == '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000162 {
163 $this->final_output = $output;
164 }
165 else
166 {
167 $this->final_output .= $output;
168 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000169
170 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000171 }
172
173 // --------------------------------------------------------------------
174
175 /**
176 * Set Header
177 *
178 * Lets you set a server header which will be outputted with the final display.
179 *
Andrey Andreev92ebfb62012-05-17 12:49:24 +0300180 * Note: If a file is cached, headers will not be sent. We need to figure out
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 * how to permit header data to be saved with the cache data...
182 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000183 * @param string
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300184 * @param bool
Derek Allard2067d1a2008-11-13 22:59:24 +0000185 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200186 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200187 public function set_header($header, $replace = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000188 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200189 // If zlib.output_compression is enabled it will compress the output,
190 // but it will not modify the content-length header to compensate for
191 // the reduction, causing the browser to hang waiting for more data.
192 // We'll just skip content-length in those cases.
Alex Bilbieed944a32012-06-02 11:07:47 +0100193 if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200194 {
195 return;
196 }
Barry Mienydd671972010-10-04 16:33:58 +0200197
Derek Allard2067d1a2008-11-13 22:59:24 +0000198 $this->headers[] = array($header, $replace);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000199 return $this;
200 }
201
202 // --------------------------------------------------------------------
203
204 /**
205 * Set Content Type Header
206 *
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000207 * @param string extension of the file we're outputting
208 * @return void
209 */
Andrey Andreev47b67332012-06-06 15:58:05 +0300210 public function set_content_type($mime_type, $charset = NULL)
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000211 {
212 if (strpos($mime_type, '/') === FALSE)
213 {
214 $extension = ltrim($mime_type, '.');
215
216 // Is this extension supported?
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300217 if (isset($this->mimes[$extension]))
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000218 {
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300219 $mime_type =& $this->mimes[$extension];
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000220
221 if (is_array($mime_type))
222 {
223 $mime_type = current($mime_type);
224 }
225 }
226 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300227
Thor (atiredmachine)242115c2012-01-24 23:08:11 -0800228 $this->mime_type = $mime_type;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000229
Andrey Andreev47b67332012-06-06 15:58:05 +0300230 if (empty($charset))
231 {
232 $charset = config_item('charset');
233 }
234
235 $header = 'Content-Type: '.$mime_type
236 .(empty($charset) ? NULL : '; charset='.strtolower($charset));
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000237
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 {
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300304 if (isset($sections['query_toggle_count']))
305 {
306 $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
307 unset($sections['query_toggle_count']);
308 }
309
Derek Jonesee71c802010-03-10 10:05:05 -0600310 foreach ($sections as $section => $enable)
311 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300312 $this->_profiler_sections[$section] = ($enable !== FALSE);
Derek Jonesee71c802010-03-10 10:05:05 -0600313 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000314
315 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600316 }
317
318 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200319
Derek Allard2067d1a2008-11-13 22:59:24 +0000320 /**
321 * Set Cache
322 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300323 * @param int
Derek Allard2067d1a2008-11-13 22:59:24 +0000324 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200325 */
Michiel Vugteveen0609d582012-01-08 13:26:17 +0100326 public function cache($time)
Derek Allard2067d1a2008-11-13 22:59:24 +0000327 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300328 $this->cache_expiration = is_numeric($time) ? $time : 0;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000329 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000330 }
Barry Mienydd671972010-10-04 16:33:58 +0200331
Derek Allard2067d1a2008-11-13 22:59:24 +0000332 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200333
Derek Allard2067d1a2008-11-13 22:59:24 +0000334 /**
335 * Display Output
336 *
337 * All "view" data is automatically put into this variable by the controller class:
338 *
339 * $this->final_output
340 *
341 * This function sends the finalized output data to the browser along
Andrey Andreev7b53d042012-03-26 23:02:32 +0300342 * with any server headers and profile data. It also stops the
Derek Allard2067d1a2008-11-13 22:59:24 +0000343 * benchmark timer so the page rendering speed and memory usage can be shown.
344 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300345 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000346 * @return mixed
Barry Mienydd671972010-10-04 16:33:58 +0200347 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200348 public function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200349 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500350 // Note: We use globals because we can't use $CI =& get_instance()
Derek Allard2067d1a2008-11-13 22:59:24 +0000351 // since this function is sometimes called by the caching mechanism,
352 // which happens before the CI super object is available.
353 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500354
355 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600356 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500357 {
358 $CI =& get_instance();
359 }
360
Derek Allard2067d1a2008-11-13 22:59:24 +0000361 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200362
Derek Allard2067d1a2008-11-13 22:59:24 +0000363 // Set the output data
Alex Bilbieed944a32012-06-02 11:07:47 +0100364 if ($output === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000365 {
366 $output =& $this->final_output;
367 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300368
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800369 // --------------------------------------------------------------------
370
371 // Is minify requested?
372 if ($CFG->item('minify_output') === TRUE)
373 {
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800374 $output = $this->minify($output, $this->mime_type);
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800375 }
376
Barry Mienydd671972010-10-04 16:33:58 +0200377
Derek Allard2067d1a2008-11-13 22:59:24 +0000378 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200379
Derek Jones37f4b9c2011-07-01 17:56:50 -0500380 // Do we need to write a cache file? Only if the controller does not have its
Derek Jonesd7633492010-09-28 13:14:57 -0500381 // own _output() method and we are not dealing with a cache file, which we
382 // can determine by the existence of the $CI object above
383 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000384 {
385 $this->_write_cache($output);
386 }
Barry Mienydd671972010-10-04 16:33:58 +0200387
Derek Allard2067d1a2008-11-13 22:59:24 +0000388 // --------------------------------------------------------------------
389
390 // Parse out the elapsed time and memory usage,
391 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200392
393 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600394
395 if ($this->parse_exec_vars === TRUE)
396 {
Andrey Andreevc839d282012-06-07 14:35:27 +0300397 $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
Barry Mienydd671972010-10-04 16:33:58 +0200398
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200399 $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
Derek Jones46520492010-03-02 13:53:25 -0600400 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000401
402 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200403
Derek Allard2067d1a2008-11-13 22:59:24 +0000404 // Is compression requested?
Alex Bilbieed944a32012-06-02 11:07:47 +0100405 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200406 && extension_loaded('zlib')
407 && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000408 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200409 ob_start('ob_gzhandler');
Derek Allard2067d1a2008-11-13 22:59:24 +0000410 }
411
412 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200413
Derek Allard2067d1a2008-11-13 22:59:24 +0000414 // Are there any server headers to send?
415 if (count($this->headers) > 0)
416 {
417 foreach ($this->headers as $header)
418 {
419 @header($header[0], $header[1]);
420 }
Barry Mienydd671972010-10-04 16:33:58 +0200421 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000422
423 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200424
Derek Jonesd7633492010-09-28 13:14:57 -0500425 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000426 // If not we know we are dealing with a cache file so we'll
427 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500428 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000429 {
430 echo $output;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200431 log_message('debug', 'Final output sent to browser');
432 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000433 return TRUE;
434 }
Barry Mienydd671972010-10-04 16:33:58 +0200435
Derek Allard2067d1a2008-11-13 22:59:24 +0000436 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200437
Derek Allard2067d1a2008-11-13 22:59:24 +0000438 // Do we need to generate profile data?
439 // If so, load the Profile class and run it.
Alex Bilbieed944a32012-06-02 11:07:47 +0100440 if ($this->enable_profiler === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000441 {
Barry Mienydd671972010-10-04 16:33:58 +0200442 $CI->load->library('profiler');
Derek Jonesee71c802010-03-10 10:05:05 -0600443 if ( ! empty($this->_profiler_sections))
444 {
445 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200446 }
Derek Jonesee71c802010-03-10 10:05:05 -0600447
Derek Allard2067d1a2008-11-13 22:59:24 +0000448 // If the output data contains closing </body> and </html> tags
449 // we will remove them and add them back after we insert the profile data
Andrey Andreevcba20b12012-01-09 10:16:41 +0200450 $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
451 if ($count > 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000452 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000453 $output .= '</body></html>';
454 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000455 }
Barry Mienydd671972010-10-04 16:33:58 +0200456
Derek Allard2067d1a2008-11-13 22:59:24 +0000457 // Does the controller contain a function named _output()?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500458 // If so send the output there. Otherwise, echo it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000459 if (method_exists($CI, '_output'))
460 {
461 $CI->_output($output);
462 }
463 else
464 {
Andrey Andreevedc87552012-01-09 09:35:10 +0200465 echo $output; // Send it to the browser!
Derek Allard2067d1a2008-11-13 22:59:24 +0000466 }
Barry Mienydd671972010-10-04 16:33:58 +0200467
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200468 log_message('debug', 'Final output sent to browser');
469 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000470 }
Barry Mienydd671972010-10-04 16:33:58 +0200471
Derek Allard2067d1a2008-11-13 22:59:24 +0000472 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200473
Derek Allard2067d1a2008-11-13 22:59:24 +0000474 /**
475 * Write a Cache File
476 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300477 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000478 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200479 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200480 public function _write_cache($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000481 {
Barry Mienydd671972010-10-04 16:33:58 +0200482 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000483 $path = $CI->config->item('cache_path');
Alex Bilbieed944a32012-06-02 11:07:47 +0100484 $cache_path = ($path === '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200485
Derek Allard2067d1a2008-11-13 22:59:24 +0000486 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
487 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200488 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000489 return;
490 }
Barry Mienydd671972010-10-04 16:33:58 +0200491
Derek Allard2067d1a2008-11-13 22:59:24 +0000492 $uri = $CI->config->item('base_url').
493 $CI->config->item('index_page').
494 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200495
Derek Allard2067d1a2008-11-13 22:59:24 +0000496 $cache_path .= md5($uri);
497
498 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
499 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200500 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000501 return;
502 }
Barry Mienydd671972010-10-04 16:33:58 +0200503
Derek Allard2067d1a2008-11-13 22:59:24 +0000504 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200505
Derek Allard2067d1a2008-11-13 22:59:24 +0000506 if (flock($fp, LOCK_EX))
507 {
508 fwrite($fp, $expire.'TS--->'.$output);
509 flock($fp, LOCK_UN);
510 }
511 else
512 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200513 log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000514 return;
515 }
516 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000517 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000518
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200519 log_message('debug', 'Cache file written: '.$cache_path);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300520
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800521 // Send HTTP cache-control headers to browser to match file cache settings.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800522 $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 }
524
525 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200526
Derek Allard2067d1a2008-11-13 22:59:24 +0000527 /**
528 * Update/serve a cached file
529 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300530 * @param object config class
531 * @param object uri class
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300532 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200533 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200534 public function _display_cache(&$CFG, &$URI)
Derek Allard2067d1a2008-11-13 22:59:24 +0000535 {
Alex Bilbieed944a32012-06-02 11:07:47 +0100536 $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200537
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200538 // Build the file path. The file name is an MD5 hash of the full URI
539 $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
Derek Allard2067d1a2008-11-13 22:59:24 +0000540 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200541
Andrey Andreevc90d6512012-01-08 04:35:02 +0200542 if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allard2067d1a2008-11-13 22:59:24 +0000543 {
544 return FALSE;
545 }
Barry Mienydd671972010-10-04 16:33:58 +0200546
Derek Allard2067d1a2008-11-13 22:59:24 +0000547 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200548
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200549 $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
Barry Mienydd671972010-10-04 16:33:58 +0200550
Derek Allard2067d1a2008-11-13 22:59:24 +0000551 flock($fp, LOCK_UN);
552 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200553
554 // Strip out the embedded timestamp
vlakoff239e0e52012-09-16 19:16:33 +0200555 if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))
Derek Allard2067d1a2008-11-13 22:59:24 +0000556 {
557 return FALSE;
558 }
Barry Mienydd671972010-10-04 16:33:58 +0200559
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800560 $last_modified = filemtime($cache_path);
vlakoff239e0e52012-09-16 19:16:33 +0200561 $expire = $match[1];
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800562
563 // Has the file expired?
564 if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
Derek Jonesd99e6032010-03-19 19:57:33 -0500565 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800566 // If so we'll delete it.
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200567 @unlink($filepath);
568 log_message('debug', 'Cache file has expired. File deleted.');
569 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000570 }
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800571 else
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300572 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800573 // Or else send the HTTP cache control headers.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800574 $this->set_cache_header($last_modified, $expire);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800575 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000576
577 // Display the cache
vlakoff1288fe32012-07-23 16:22:06 +0200578 $this->_display(substr($cache, strlen($match[0])));
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200579 log_message('debug', 'Cache file is current. Sending it to browser.');
Derek Allard2067d1a2008-11-13 22:59:24 +0000580 return TRUE;
581 }
582
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800583 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300584
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800585 /**
586 * Set the HTTP headers to match the server-side file cache settings
587 * in order to reduce bandwidth.
588 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300589 * @param int timestamp of when the page was last modified
590 * @param int timestamp of when should the requested page expire from cache
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800591 * @return void
592 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800593 public function set_cache_header($last_modified, $expiration)
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300594 {
595 $max_age = $expiration - $_SERVER['REQUEST_TIME'];
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800596
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300597 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800598 {
599 $this->set_status_header(304);
600 exit;
601 }
602 else
603 {
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300604 header('Pragma: public');
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800605 header('Cache-Control: max-age=' . $max_age . ', public');
606 header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
607 header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
608 }
609 }
610
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800611 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300612
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800613 /**
614 * Reduce excessive size of HTML content.
615 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300616 * @param string
617 * @param string
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800618 * @return string
619 */
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300620 public function minify($output, $type = 'text/html')
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800621 {
622 switch ($type)
623 {
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800624 case 'text/html':
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800625
626 $size_before = strlen($output);
627
Phil Sturgeon7a744a82012-06-23 17:21:00 +0100628 if ($size_before === 0)
629 {
630 return '';
631 }
632
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800633 // Find all the <pre>,<code>,<textarea>, and <javascript> tags
634 // We'll want to return them to this unprocessed state later.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800635 preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
636 preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
637 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800638 preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800639
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800640 // Minify the CSS in all the <style> tags.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800641 preg_match_all('{<style.+</style>}msU', $output, $style_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800642 foreach ($style_clean[0] as $s)
643 {
Thor1b8d0ef2012-01-28 01:48:04 -0800644 $output = str_replace($s, $this->minify($s, 'text/css'), $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800645 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300646
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800647 // Minify the javascript in <script> tags.
648 foreach ($javascript_clean[0] as $s)
649 {
Thor1b8d0ef2012-01-28 01:48:04 -0800650 $javascript_mini[] = $this->minify($s, 'text/javascript');
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800651 }
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800652
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800653 // Replace multiple spaces with a single space.
Thor1b8d0ef2012-01-28 01:48:04 -0800654 $output = preg_replace('!\s{2,}!', ' ', $output);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300655
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800656 // Remove comments (non-MSIE conditionals)
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800657 $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800658
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800659 // Remove spaces around block-level elements.
Phil Sturgeon3b6af432012-06-21 09:21:17 -0500660 $output = preg_replace('/\s*(<\/?(html|head|title|meta|script|link|style|body|h[1-6]|div|p|br)[^>]*>)\s*/is', '$1', $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800661
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800662 // Replace mangled <pre> etc. tags with unprocessed ones.
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300663
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800664 if ( ! empty($pres_clean))
665 {
666 preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
667 $output = str_replace($pres_messed[0], $pres_clean[0], $output);
668 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300669
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800670 if ( ! empty($codes_clean))
671 {
672 preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
673 $output = str_replace($codes_messed[0], $codes_clean[0], $output);
674 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300675
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800676 if ( ! empty($codes_clean))
677 {
678 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
679 $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
680 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300681
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800682 if (isset($javascript_mini))
683 {
684 preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
685 $output = str_replace($javascript_messed[0], $javascript_mini, $output);
686 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300687
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800688 $size_removed = $size_before - strlen($output);
689 $savings_percent = round(($size_removed / $size_before * 100));
690
691 log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800692
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800693 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300694
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800695 case 'text/css':
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300696
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800697 //Remove CSS comments
698 $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300699
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800700 // Remove spaces around curly brackets, colons,
701 // semi-colons, parenthesis, commas
702 $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output);
703
704 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300705
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800706 case 'text/javascript':
707
Thor1b8d0ef2012-01-28 01:48:04 -0800708 // Currently leaves JavaScript untouched.
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800709 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300710
711 default: break;
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800712 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300713
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800714 return $output;
715 }
716
Derek Allard2067d1a2008-11-13 22:59:24 +0000717}
Derek Allard2067d1a2008-11-13 22:59:24 +0000718
719/* End of file Output.php */
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300720/* Location: ./system/core/Output.php */