blob: aa0e05dc4ae96bd679ad1a26967383695de5ece7 [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 *
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300207 * @param string $mime_type extension of the file we're outputting
208 * @param string $charset = NULL
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000209 * @return void
210 */
Andrey Andreev47b67332012-06-06 15:58:05 +0300211 public function set_content_type($mime_type, $charset = NULL)
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000212 {
213 if (strpos($mime_type, '/') === FALSE)
214 {
215 $extension = ltrim($mime_type, '.');
216
217 // Is this extension supported?
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300218 if (isset($this->mimes[$extension]))
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000219 {
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300220 $mime_type =& $this->mimes[$extension];
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000221
222 if (is_array($mime_type))
223 {
224 $mime_type = current($mime_type);
225 }
226 }
227 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300228
Thor (atiredmachine)242115c2012-01-24 23:08:11 -0800229 $this->mime_type = $mime_type;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000230
Andrey Andreev47b67332012-06-06 15:58:05 +0300231 if (empty($charset))
232 {
233 $charset = config_item('charset');
234 }
235
236 $header = 'Content-Type: '.$mime_type
237 .(empty($charset) ? NULL : '; charset='.strtolower($charset));
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000238
239 $this->headers[] = array($header, TRUE);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000240 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000241 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300242
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700243 // --------------------------------------------------------------------
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300244
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700245 /**
246 * Get Current Content Type Header
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700247 *
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300248 * @return string 'text/html', if not already set
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700249 */
Songpol Sripaoeiam614db072012-04-03 01:29:28 +0700250 public function get_content_type()
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700251 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300252 for ($i = 0, $c = count($this->headers); $i < $c; $i++)
Songpol Sripao-eiam38c0a722012-04-01 20:10:35 +0700253 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300254 if (preg_match('/^Content-Type:\s(.+)$/', $this->headers[$i][0], $matches))
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700255 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300256 return $matches[1];
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700257 }
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700258 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300259
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700260 return 'text/html';
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700261 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000262
263 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200264
Derek Allard2067d1a2008-11-13 22:59:24 +0000265 /**
266 * Set HTTP Status Header
Derek Jones817163a2009-07-11 17:05:58 +0000267 * moved to Common procedural functions in 1.7.2
Barry Mienydd671972010-10-04 16:33:58 +0200268 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300269 * @param int the status code
Barry Mienydd671972010-10-04 16:33:58 +0200270 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000271 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200272 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200273 public function set_status_header($code = 200, $text = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000274 {
Derek Jones817163a2009-07-11 17:05:58 +0000275 set_status_header($code, $text);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000276 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000277 }
Barry Mienydd671972010-10-04 16:33:58 +0200278
Derek Allard2067d1a2008-11-13 22:59:24 +0000279 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200280
Derek Allard2067d1a2008-11-13 22:59:24 +0000281 /**
282 * Enable/disable Profiler
283 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000284 * @param bool
285 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200286 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200287 public function enable_profiler($val = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000288 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300289 $this->enable_profiler = is_bool($val) ? $val : TRUE;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000290 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000291 }
Barry Mienydd671972010-10-04 16:33:58 +0200292
Derek Allard2067d1a2008-11-13 22:59:24 +0000293 // --------------------------------------------------------------------
Derek Jonesee71c802010-03-10 10:05:05 -0600294
295 /**
296 * Set Profiler Sections
297 *
298 * Allows override of default / config settings for Profiler section display
299 *
Derek Jonesee71c802010-03-10 10:05:05 -0600300 * @param array
301 * @return void
302 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200303 public function set_profiler_sections($sections)
Derek Jonesee71c802010-03-10 10:05:05 -0600304 {
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300305 if (isset($sections['query_toggle_count']))
306 {
307 $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
308 unset($sections['query_toggle_count']);
309 }
310
Derek Jonesee71c802010-03-10 10:05:05 -0600311 foreach ($sections as $section => $enable)
312 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300313 $this->_profiler_sections[$section] = ($enable !== FALSE);
Derek Jonesee71c802010-03-10 10:05:05 -0600314 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000315
316 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600317 }
318
319 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200320
Derek Allard2067d1a2008-11-13 22:59:24 +0000321 /**
322 * Set Cache
323 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300324 * @param int
Derek Allard2067d1a2008-11-13 22:59:24 +0000325 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200326 */
Michiel Vugteveen0609d582012-01-08 13:26:17 +0100327 public function cache($time)
Derek Allard2067d1a2008-11-13 22:59:24 +0000328 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300329 $this->cache_expiration = is_numeric($time) ? $time : 0;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000330 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000331 }
Barry Mienydd671972010-10-04 16:33:58 +0200332
Derek Allard2067d1a2008-11-13 22:59:24 +0000333 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200334
Derek Allard2067d1a2008-11-13 22:59:24 +0000335 /**
336 * Display Output
337 *
338 * All "view" data is automatically put into this variable by the controller class:
339 *
340 * $this->final_output
341 *
342 * This function sends the finalized output data to the browser along
Andrey Andreev7b53d042012-03-26 23:02:32 +0300343 * with any server headers and profile data. It also stops the
Derek Allard2067d1a2008-11-13 22:59:24 +0000344 * benchmark timer so the page rendering speed and memory usage can be shown.
345 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300346 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000347 * @return mixed
Barry Mienydd671972010-10-04 16:33:58 +0200348 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200349 public function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200350 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500351 // Note: We use globals because we can't use $CI =& get_instance()
Derek Allard2067d1a2008-11-13 22:59:24 +0000352 // since this function is sometimes called by the caching mechanism,
353 // which happens before the CI super object is available.
354 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500355
356 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600357 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500358 {
359 $CI =& get_instance();
360 }
361
Derek Allard2067d1a2008-11-13 22:59:24 +0000362 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200363
Derek Allard2067d1a2008-11-13 22:59:24 +0000364 // Set the output data
Alex Bilbieed944a32012-06-02 11:07:47 +0100365 if ($output === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000366 {
367 $output =& $this->final_output;
368 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300369
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800370 // --------------------------------------------------------------------
371
372 // Is minify requested?
373 if ($CFG->item('minify_output') === TRUE)
374 {
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800375 $output = $this->minify($output, $this->mime_type);
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800376 }
377
Barry Mienydd671972010-10-04 16:33:58 +0200378
Derek Allard2067d1a2008-11-13 22:59:24 +0000379 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200380
Derek Jones37f4b9c2011-07-01 17:56:50 -0500381 // Do we need to write a cache file? Only if the controller does not have its
Derek Jonesd7633492010-09-28 13:14:57 -0500382 // own _output() method and we are not dealing with a cache file, which we
383 // can determine by the existence of the $CI object above
384 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000385 {
386 $this->_write_cache($output);
387 }
Barry Mienydd671972010-10-04 16:33:58 +0200388
Derek Allard2067d1a2008-11-13 22:59:24 +0000389 // --------------------------------------------------------------------
390
391 // Parse out the elapsed time and memory usage,
392 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200393
394 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600395
396 if ($this->parse_exec_vars === TRUE)
397 {
Andrey Andreevc839d282012-06-07 14:35:27 +0300398 $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
Barry Mienydd671972010-10-04 16:33:58 +0200399
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200400 $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
Derek Jones46520492010-03-02 13:53:25 -0600401 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000402
403 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200404
Derek Allard2067d1a2008-11-13 22:59:24 +0000405 // Is compression requested?
Alex Bilbieed944a32012-06-02 11:07:47 +0100406 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200407 && extension_loaded('zlib')
408 && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000409 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200410 ob_start('ob_gzhandler');
Derek Allard2067d1a2008-11-13 22:59:24 +0000411 }
412
413 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200414
Derek Allard2067d1a2008-11-13 22:59:24 +0000415 // Are there any server headers to send?
416 if (count($this->headers) > 0)
417 {
418 foreach ($this->headers as $header)
419 {
420 @header($header[0], $header[1]);
421 }
Barry Mienydd671972010-10-04 16:33:58 +0200422 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000423
424 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200425
Derek Jonesd7633492010-09-28 13:14:57 -0500426 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000427 // If not we know we are dealing with a cache file so we'll
428 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500429 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000430 {
431 echo $output;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200432 log_message('debug', 'Final output sent to browser');
433 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000434 return TRUE;
435 }
Barry Mienydd671972010-10-04 16:33:58 +0200436
Derek Allard2067d1a2008-11-13 22:59:24 +0000437 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200438
Derek Allard2067d1a2008-11-13 22:59:24 +0000439 // Do we need to generate profile data?
440 // If so, load the Profile class and run it.
Alex Bilbieed944a32012-06-02 11:07:47 +0100441 if ($this->enable_profiler === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000442 {
Barry Mienydd671972010-10-04 16:33:58 +0200443 $CI->load->library('profiler');
Derek Jonesee71c802010-03-10 10:05:05 -0600444 if ( ! empty($this->_profiler_sections))
445 {
446 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200447 }
Derek Jonesee71c802010-03-10 10:05:05 -0600448
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 // If the output data contains closing </body> and </html> tags
450 // we will remove them and add them back after we insert the profile data
Andrey Andreevcba20b12012-01-09 10:16:41 +0200451 $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
452 if ($count > 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000453 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000454 $output .= '</body></html>';
455 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000456 }
Barry Mienydd671972010-10-04 16:33:58 +0200457
Derek Allard2067d1a2008-11-13 22:59:24 +0000458 // Does the controller contain a function named _output()?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500459 // If so send the output there. Otherwise, echo it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000460 if (method_exists($CI, '_output'))
461 {
462 $CI->_output($output);
463 }
464 else
465 {
Andrey Andreevedc87552012-01-09 09:35:10 +0200466 echo $output; // Send it to the browser!
Derek Allard2067d1a2008-11-13 22:59:24 +0000467 }
Barry Mienydd671972010-10-04 16:33:58 +0200468
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200469 log_message('debug', 'Final output sent to browser');
470 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000471 }
Barry Mienydd671972010-10-04 16:33:58 +0200472
Derek Allard2067d1a2008-11-13 22:59:24 +0000473 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200474
Derek Allard2067d1a2008-11-13 22:59:24 +0000475 /**
476 * Write a Cache File
477 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300478 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000479 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200480 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200481 public function _write_cache($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000482 {
Barry Mienydd671972010-10-04 16:33:58 +0200483 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000484 $path = $CI->config->item('cache_path');
Alex Bilbieed944a32012-06-02 11:07:47 +0100485 $cache_path = ($path === '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200486
Derek Allard2067d1a2008-11-13 22:59:24 +0000487 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
488 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200489 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000490 return;
491 }
Barry Mienydd671972010-10-04 16:33:58 +0200492
Derek Allard2067d1a2008-11-13 22:59:24 +0000493 $uri = $CI->config->item('base_url').
494 $CI->config->item('index_page').
495 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200496
Derek Allard2067d1a2008-11-13 22:59:24 +0000497 $cache_path .= md5($uri);
498
499 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
500 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200501 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000502 return;
503 }
Barry Mienydd671972010-10-04 16:33:58 +0200504
Derek Allard2067d1a2008-11-13 22:59:24 +0000505 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200506
Derek Allard2067d1a2008-11-13 22:59:24 +0000507 if (flock($fp, LOCK_EX))
508 {
509 fwrite($fp, $expire.'TS--->'.$output);
510 flock($fp, LOCK_UN);
511 }
512 else
513 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200514 log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000515 return;
516 }
517 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000518 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000519
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200520 log_message('debug', 'Cache file written: '.$cache_path);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300521
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800522 // Send HTTP cache-control headers to browser to match file cache settings.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800523 $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000524 }
525
526 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200527
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 /**
529 * Update/serve a cached file
530 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300531 * @param object config class
532 * @param object uri class
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300533 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200534 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200535 public function _display_cache(&$CFG, &$URI)
Derek Allard2067d1a2008-11-13 22:59:24 +0000536 {
Alex Bilbieed944a32012-06-02 11:07:47 +0100537 $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200538
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200539 // Build the file path. The file name is an MD5 hash of the full URI
540 $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
Derek Allard2067d1a2008-11-13 22:59:24 +0000541 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200542
Andrey Andreevc90d6512012-01-08 04:35:02 +0200543 if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allard2067d1a2008-11-13 22:59:24 +0000544 {
545 return FALSE;
546 }
Barry Mienydd671972010-10-04 16:33:58 +0200547
Derek Allard2067d1a2008-11-13 22:59:24 +0000548 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200549
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200550 $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
Barry Mienydd671972010-10-04 16:33:58 +0200551
Derek Allard2067d1a2008-11-13 22:59:24 +0000552 flock($fp, LOCK_UN);
553 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200554
555 // Strip out the embedded timestamp
vlakoff239e0e52012-09-16 19:16:33 +0200556 if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))
Derek Allard2067d1a2008-11-13 22:59:24 +0000557 {
558 return FALSE;
559 }
Barry Mienydd671972010-10-04 16:33:58 +0200560
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800561 $last_modified = filemtime($cache_path);
vlakoff239e0e52012-09-16 19:16:33 +0200562 $expire = $match[1];
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800563
564 // Has the file expired?
565 if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
Derek Jonesd99e6032010-03-19 19:57:33 -0500566 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800567 // If so we'll delete it.
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200568 @unlink($filepath);
569 log_message('debug', 'Cache file has expired. File deleted.');
570 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000571 }
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800572 else
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300573 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800574 // Or else send the HTTP cache control headers.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800575 $this->set_cache_header($last_modified, $expire);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800576 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000577
578 // Display the cache
vlakoff1288fe32012-07-23 16:22:06 +0200579 $this->_display(substr($cache, strlen($match[0])));
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200580 log_message('debug', 'Cache file is current. Sending it to browser.');
Derek Allard2067d1a2008-11-13 22:59:24 +0000581 return TRUE;
582 }
583
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800584 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300585
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800586 /**
587 * Set the HTTP headers to match the server-side file cache settings
588 * in order to reduce bandwidth.
589 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300590 * @param int timestamp of when the page was last modified
591 * @param int timestamp of when should the requested page expire from cache
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800592 * @return void
593 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800594 public function set_cache_header($last_modified, $expiration)
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300595 {
596 $max_age = $expiration - $_SERVER['REQUEST_TIME'];
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800597
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300598 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800599 {
600 $this->set_status_header(304);
601 exit;
602 }
603 else
604 {
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300605 header('Pragma: public');
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800606 header('Cache-Control: max-age=' . $max_age . ', public');
607 header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
608 header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
609 }
610 }
611
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800612 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300613
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800614 /**
615 * Reduce excessive size of HTML content.
616 *
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300617 * @param string
618 * @param string
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800619 * @return string
620 */
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300621 public function minify($output, $type = 'text/html')
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800622 {
623 switch ($type)
624 {
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800625 case 'text/html':
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800626
627 $size_before = strlen($output);
628
Phil Sturgeon7a744a82012-06-23 17:21:00 +0100629 if ($size_before === 0)
630 {
631 return '';
632 }
633
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800634 // Find all the <pre>,<code>,<textarea>, and <javascript> tags
635 // We'll want to return them to this unprocessed state later.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800636 preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
637 preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
638 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800639 preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800640
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800641 // Minify the CSS in all the <style> tags.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800642 preg_match_all('{<style.+</style>}msU', $output, $style_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800643 foreach ($style_clean[0] as $s)
644 {
Thor1b8d0ef2012-01-28 01:48:04 -0800645 $output = str_replace($s, $this->minify($s, 'text/css'), $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800646 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300647
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800648 // Minify the javascript in <script> tags.
649 foreach ($javascript_clean[0] as $s)
650 {
Thor1b8d0ef2012-01-28 01:48:04 -0800651 $javascript_mini[] = $this->minify($s, 'text/javascript');
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800652 }
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800653
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800654 // Replace multiple spaces with a single space.
Thor1b8d0ef2012-01-28 01:48:04 -0800655 $output = preg_replace('!\s{2,}!', ' ', $output);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300656
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800657 // Remove comments (non-MSIE conditionals)
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800658 $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800659
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800660 // Remove spaces around block-level elements.
Phil Sturgeon3b6af432012-06-21 09:21:17 -0500661 $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 -0800662
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800663 // Replace mangled <pre> etc. tags with unprocessed ones.
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300664
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800665 if ( ! empty($pres_clean))
666 {
667 preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
668 $output = str_replace($pres_messed[0], $pres_clean[0], $output);
669 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300670
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800671 if ( ! empty($codes_clean))
672 {
673 preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
674 $output = str_replace($codes_messed[0], $codes_clean[0], $output);
675 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300676
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800677 if ( ! empty($codes_clean))
678 {
679 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
680 $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
681 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300682
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800683 if (isset($javascript_mini))
684 {
685 preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
686 $output = str_replace($javascript_messed[0], $javascript_mini, $output);
687 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300688
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800689 $size_removed = $size_before - strlen($output);
690 $savings_percent = round(($size_removed / $size_before * 100));
691
692 log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800693
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800694 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300695
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800696 case 'text/css':
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300697
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800698 //Remove CSS comments
699 $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300700
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800701 // Remove spaces around curly brackets, colons,
702 // semi-colons, parenthesis, commas
703 $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output);
704
705 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300706
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800707 case 'text/javascript':
708
Thor1b8d0ef2012-01-28 01:48:04 -0800709 // Currently leaves JavaScript untouched.
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800710 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300711
712 default: break;
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800713 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300714
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800715 return $output;
716 }
717
Derek Allard2067d1a2008-11-13 22:59:24 +0000718}
Derek Allard2067d1a2008-11-13 22:59:24 +0000719
720/* End of file Output.php */
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300721/* Location: ./system/core/Output.php */