blob: 8a0f14e02db81211e7c2e82427eb1b9951d31cc6 [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';
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 Andreev6ef498b2012-06-05 22:01:58 +0300113 $this->mimes =& get_mimes();
Greg Akerd96f8822011-12-27 16:23:47 -0600114
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200115 log_message('debug', 'Output Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000116 }
Barry Mienydd671972010-10-04 16:33:58 +0200117
Derek Allard2067d1a2008-11-13 22:59:24 +0000118 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200119
Derek Allard2067d1a2008-11-13 22:59:24 +0000120 /**
121 * Get Output
122 *
123 * Returns the current output string
124 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000125 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200126 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200127 public function get_output()
Derek Allard2067d1a2008-11-13 22:59:24 +0000128 {
129 return $this->final_output;
130 }
Barry Mienydd671972010-10-04 16:33:58 +0200131
Derek Allard2067d1a2008-11-13 22:59:24 +0000132 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200133
Derek Allard2067d1a2008-11-13 22:59:24 +0000134 /**
135 * Set Output
136 *
137 * Sets the output string
138 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000139 * @param string
140 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200141 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200142 public function set_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000143 {
144 $this->final_output = $output;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000145 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000146 }
147
148 // --------------------------------------------------------------------
149
150 /**
151 * Append Output
152 *
153 * Appends data onto the output string
154 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000155 * @param string
156 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200157 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200158 public function append_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000159 {
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300160 if ($this->final_output == '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000161 {
162 $this->final_output = $output;
163 }
164 else
165 {
166 $this->final_output .= $output;
167 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000168
169 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000170 }
171
172 // --------------------------------------------------------------------
173
174 /**
175 * Set Header
176 *
177 * Lets you set a server header which will be outputted with the final display.
178 *
Andrey Andreev92ebfb62012-05-17 12:49:24 +0300179 * Note: If a file is cached, headers will not be sent. We need to figure out
Derek Allard2067d1a2008-11-13 22:59:24 +0000180 * how to permit header data to be saved with the cache data...
181 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000182 * @param string
David Behler07b53422011-08-15 00:25:06 +0200183 * @param bool
Derek Allard2067d1a2008-11-13 22:59:24 +0000184 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200185 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200186 public function set_header($header, $replace = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200188 // If zlib.output_compression is enabled it will compress the output,
189 // but it will not modify the content-length header to compensate for
190 // the reduction, causing the browser to hang waiting for more data.
191 // We'll just skip content-length in those cases.
Alex Bilbieed944a32012-06-02 11:07:47 +0100192 if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200193 {
194 return;
195 }
Barry Mienydd671972010-10-04 16:33:58 +0200196
Derek Allard2067d1a2008-11-13 22:59:24 +0000197 $this->headers[] = array($header, $replace);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000198 return $this;
199 }
200
201 // --------------------------------------------------------------------
202
203 /**
204 * Set Content Type Header
205 *
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000206 * @param string extension of the file we're outputting
207 * @return void
208 */
Andrey Andreev47b67332012-06-06 15:58:05 +0300209 public function set_content_type($mime_type, $charset = NULL)
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000210 {
211 if (strpos($mime_type, '/') === FALSE)
212 {
213 $extension = ltrim($mime_type, '.');
214
215 // Is this extension supported?
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300216 if (isset($this->mimes[$extension]))
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000217 {
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300218 $mime_type =& $this->mimes[$extension];
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000219
220 if (is_array($mime_type))
221 {
222 $mime_type = current($mime_type);
223 }
224 }
225 }
Thor (atiredmachine)242115c2012-01-24 23:08:11 -0800226
227 $this->mime_type = $mime_type;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000228
Andrey Andreev47b67332012-06-06 15:58:05 +0300229 if (empty($charset))
230 {
231 $charset = config_item('charset');
232 }
233
234 $header = 'Content-Type: '.$mime_type
235 .(empty($charset) ? NULL : '; charset='.strtolower($charset));
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000236
237 $this->headers[] = array($header, TRUE);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000238 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300240
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700241 // --------------------------------------------------------------------
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300242
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700243 /**
244 * Get Current Content Type Header
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700245 *
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300246 * @return string 'text/html', if not already set
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700247 */
Songpol Sripaoeiam614db072012-04-03 01:29:28 +0700248 public function get_content_type()
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700249 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300250 for ($i = 0, $c = count($this->headers); $i < $c; $i++)
Songpol Sripao-eiam38c0a722012-04-01 20:10:35 +0700251 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300252 if (preg_match('/^Content-Type:\s(.+)$/', $this->headers[$i][0], $matches))
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700253 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300254 return $matches[1];
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700255 }
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700256 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300257
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700258 return 'text/html';
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700259 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000260
261 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200262
Derek Allard2067d1a2008-11-13 22:59:24 +0000263 /**
264 * Set HTTP Status Header
Derek Jones817163a2009-07-11 17:05:58 +0000265 * moved to Common procedural functions in 1.7.2
Barry Mienydd671972010-10-04 16:33:58 +0200266 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300267 * @param int the status code
Barry Mienydd671972010-10-04 16:33:58 +0200268 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000269 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200270 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200271 public function set_status_header($code = 200, $text = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000272 {
Derek Jones817163a2009-07-11 17:05:58 +0000273 set_status_header($code, $text);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000274 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000275 }
Barry Mienydd671972010-10-04 16:33:58 +0200276
Derek Allard2067d1a2008-11-13 22:59:24 +0000277 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200278
Derek Allard2067d1a2008-11-13 22:59:24 +0000279 /**
280 * Enable/disable Profiler
281 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000282 * @param bool
283 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200284 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200285 public function enable_profiler($val = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000286 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300287 $this->enable_profiler = is_bool($val) ? $val : TRUE;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000288 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000289 }
Barry Mienydd671972010-10-04 16:33:58 +0200290
Derek Allard2067d1a2008-11-13 22:59:24 +0000291 // --------------------------------------------------------------------
Derek Jonesee71c802010-03-10 10:05:05 -0600292
293 /**
294 * Set Profiler Sections
295 *
296 * Allows override of default / config settings for Profiler section display
297 *
Derek Jonesee71c802010-03-10 10:05:05 -0600298 * @param array
299 * @return void
300 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200301 public function set_profiler_sections($sections)
Derek Jonesee71c802010-03-10 10:05:05 -0600302 {
303 foreach ($sections as $section => $enable)
304 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300305 $this->_profiler_sections[$section] = ($enable !== FALSE);
Derek Jonesee71c802010-03-10 10:05:05 -0600306 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000307
308 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600309 }
310
311 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200312
Derek Allard2067d1a2008-11-13 22:59:24 +0000313 /**
314 * Set Cache
315 *
Andrey Andreev7b53d042012-03-26 23:02:32 +0300316 * @param int
Derek Allard2067d1a2008-11-13 22:59:24 +0000317 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200318 */
Michiel Vugteveen0609d582012-01-08 13:26:17 +0100319 public function cache($time)
Derek Allard2067d1a2008-11-13 22:59:24 +0000320 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300321 $this->cache_expiration = is_numeric($time) ? $time : 0;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000322 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000323 }
Barry Mienydd671972010-10-04 16:33:58 +0200324
Derek Allard2067d1a2008-11-13 22:59:24 +0000325 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200326
Derek Allard2067d1a2008-11-13 22:59:24 +0000327 /**
328 * Display Output
329 *
330 * All "view" data is automatically put into this variable by the controller class:
331 *
332 * $this->final_output
333 *
334 * This function sends the finalized output data to the browser along
Andrey Andreev7b53d042012-03-26 23:02:32 +0300335 * with any server headers and profile data. It also stops the
Derek Allard2067d1a2008-11-13 22:59:24 +0000336 * benchmark timer so the page rendering speed and memory usage can be shown.
337 *
David Behler07b53422011-08-15 00:25:06 +0200338 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000339 * @return mixed
Barry Mienydd671972010-10-04 16:33:58 +0200340 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200341 public function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200342 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500343 // Note: We use globals because we can't use $CI =& get_instance()
Derek Allard2067d1a2008-11-13 22:59:24 +0000344 // since this function is sometimes called by the caching mechanism,
345 // which happens before the CI super object is available.
346 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500347
348 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600349 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500350 {
351 $CI =& get_instance();
352 }
353
Derek Allard2067d1a2008-11-13 22:59:24 +0000354 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200355
Derek Allard2067d1a2008-11-13 22:59:24 +0000356 // Set the output data
Alex Bilbieed944a32012-06-02 11:07:47 +0100357 if ($output === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000358 {
359 $output =& $this->final_output;
360 }
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800361
362 // --------------------------------------------------------------------
363
364 // Is minify requested?
365 if ($CFG->item('minify_output') === TRUE)
366 {
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800367 $output = $this->minify($output, $this->mime_type);
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800368 }
369
Barry Mienydd671972010-10-04 16:33:58 +0200370
Derek Allard2067d1a2008-11-13 22:59:24 +0000371 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200372
Derek Jones37f4b9c2011-07-01 17:56:50 -0500373 // Do we need to write a cache file? Only if the controller does not have its
Derek Jonesd7633492010-09-28 13:14:57 -0500374 // own _output() method and we are not dealing with a cache file, which we
375 // can determine by the existence of the $CI object above
376 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000377 {
378 $this->_write_cache($output);
379 }
Barry Mienydd671972010-10-04 16:33:58 +0200380
Derek Allard2067d1a2008-11-13 22:59:24 +0000381 // --------------------------------------------------------------------
382
383 // Parse out the elapsed time and memory usage,
384 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200385
386 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600387
388 if ($this->parse_exec_vars === TRUE)
389 {
Andrey Andreevc839d282012-06-07 14:35:27 +0300390 $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
Barry Mienydd671972010-10-04 16:33:58 +0200391
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200392 $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
Derek Jones46520492010-03-02 13:53:25 -0600393 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000394
395 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200396
Derek Allard2067d1a2008-11-13 22:59:24 +0000397 // Is compression requested?
Alex Bilbieed944a32012-06-02 11:07:47 +0100398 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200399 && extension_loaded('zlib')
400 && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000401 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200402 ob_start('ob_gzhandler');
Derek Allard2067d1a2008-11-13 22:59:24 +0000403 }
404
405 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200406
Derek Allard2067d1a2008-11-13 22:59:24 +0000407 // Are there any server headers to send?
408 if (count($this->headers) > 0)
409 {
410 foreach ($this->headers as $header)
411 {
412 @header($header[0], $header[1]);
413 }
Barry Mienydd671972010-10-04 16:33:58 +0200414 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000415
416 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200417
Derek Jonesd7633492010-09-28 13:14:57 -0500418 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000419 // If not we know we are dealing with a cache file so we'll
420 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500421 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000422 {
423 echo $output;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200424 log_message('debug', 'Final output sent to browser');
425 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000426 return TRUE;
427 }
Barry Mienydd671972010-10-04 16:33:58 +0200428
Derek Allard2067d1a2008-11-13 22:59:24 +0000429 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200430
Derek Allard2067d1a2008-11-13 22:59:24 +0000431 // Do we need to generate profile data?
432 // If so, load the Profile class and run it.
Alex Bilbieed944a32012-06-02 11:07:47 +0100433 if ($this->enable_profiler === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000434 {
Barry Mienydd671972010-10-04 16:33:58 +0200435 $CI->load->library('profiler');
Derek Jonesee71c802010-03-10 10:05:05 -0600436 if ( ! empty($this->_profiler_sections))
437 {
438 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200439 }
Derek Jonesee71c802010-03-10 10:05:05 -0600440
Derek Allard2067d1a2008-11-13 22:59:24 +0000441 // If the output data contains closing </body> and </html> tags
442 // we will remove them and add them back after we insert the profile data
Andrey Andreevcba20b12012-01-09 10:16:41 +0200443 $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
444 if ($count > 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000445 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000446 $output .= '</body></html>';
447 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000448 }
Barry Mienydd671972010-10-04 16:33:58 +0200449
Derek Allard2067d1a2008-11-13 22:59:24 +0000450 // Does the controller contain a function named _output()?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500451 // If so send the output there. Otherwise, echo it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000452 if (method_exists($CI, '_output'))
453 {
454 $CI->_output($output);
455 }
456 else
457 {
Andrey Andreevedc87552012-01-09 09:35:10 +0200458 echo $output; // Send it to the browser!
Derek Allard2067d1a2008-11-13 22:59:24 +0000459 }
Barry Mienydd671972010-10-04 16:33:58 +0200460
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200461 log_message('debug', 'Final output sent to browser');
462 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000463 }
Barry Mienydd671972010-10-04 16:33:58 +0200464
Derek Allard2067d1a2008-11-13 22:59:24 +0000465 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200466
Derek Allard2067d1a2008-11-13 22:59:24 +0000467 /**
468 * Write a Cache File
469 *
David Behler07b53422011-08-15 00:25:06 +0200470 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000471 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200472 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200473 public function _write_cache($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000474 {
Barry Mienydd671972010-10-04 16:33:58 +0200475 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000476 $path = $CI->config->item('cache_path');
Alex Bilbieed944a32012-06-02 11:07:47 +0100477 $cache_path = ($path === '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200478
Derek Allard2067d1a2008-11-13 22:59:24 +0000479 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
480 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200481 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000482 return;
483 }
Barry Mienydd671972010-10-04 16:33:58 +0200484
Derek Allard2067d1a2008-11-13 22:59:24 +0000485 $uri = $CI->config->item('base_url').
486 $CI->config->item('index_page').
487 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200488
Derek Allard2067d1a2008-11-13 22:59:24 +0000489 $cache_path .= md5($uri);
490
491 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
492 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200493 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000494 return;
495 }
Barry Mienydd671972010-10-04 16:33:58 +0200496
Derek Allard2067d1a2008-11-13 22:59:24 +0000497 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200498
Derek Allard2067d1a2008-11-13 22:59:24 +0000499 if (flock($fp, LOCK_EX))
500 {
501 fwrite($fp, $expire.'TS--->'.$output);
502 flock($fp, LOCK_UN);
503 }
504 else
505 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200506 log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000507 return;
508 }
509 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000510 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000511
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200512 log_message('debug', 'Cache file written: '.$cache_path);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800513
514 // Send HTTP cache-control headers to browser to match file cache settings.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800515 $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000516 }
517
518 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200519
Derek Allard2067d1a2008-11-13 22:59:24 +0000520 /**
521 * Update/serve a cached file
522 *
David Behler07b53422011-08-15 00:25:06 +0200523 * @param object config class
524 * @param object uri class
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300525 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200526 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200527 public function _display_cache(&$CFG, &$URI)
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 {
Alex Bilbieed944a32012-06-02 11:07:47 +0100529 $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200530
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200531 // Build the file path. The file name is an MD5 hash of the full URI
532 $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
Derek Allard2067d1a2008-11-13 22:59:24 +0000533 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200534
Andrey Andreevc90d6512012-01-08 04:35:02 +0200535 if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allard2067d1a2008-11-13 22:59:24 +0000536 {
537 return FALSE;
538 }
Barry Mienydd671972010-10-04 16:33:58 +0200539
Derek Allard2067d1a2008-11-13 22:59:24 +0000540 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200541
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200542 $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
Barry Mienydd671972010-10-04 16:33:58 +0200543
Derek Allard2067d1a2008-11-13 22:59:24 +0000544 flock($fp, LOCK_UN);
545 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200546
547 // Strip out the embedded timestamp
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200548 if ( ! preg_match('/(\d+TS--->)/', $cache, $match))
Derek Allard2067d1a2008-11-13 22:59:24 +0000549 {
550 return FALSE;
551 }
Barry Mienydd671972010-10-04 16:33:58 +0200552
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800553 $last_modified = filemtime($cache_path);
554 $expire = trim(str_replace('TS--->', '', $match[1]));
555
556 // Has the file expired?
557 if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
Derek Jonesd99e6032010-03-19 19:57:33 -0500558 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800559 // If so we'll delete it.
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200560 @unlink($filepath);
561 log_message('debug', 'Cache file has expired. File deleted.');
562 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000563 }
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800564 else
565 {
566 // Or else send the HTTP cache control headers.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800567 $this->set_cache_header($last_modified, $expire);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800568 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000569
570 // Display the cache
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200571 $this->_display(str_replace($match[0], '', $cache));
572 log_message('debug', 'Cache file is current. Sending it to browser.');
Derek Allard2067d1a2008-11-13 22:59:24 +0000573 return TRUE;
574 }
575
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800576
577 // --------------------------------------------------------------------
578 /**
579 * Set the HTTP headers to match the server-side file cache settings
580 * in order to reduce bandwidth.
581 *
582 * @param int timestamp of when the page was last modified
583 * @param int timestamp of when should the requested page expire from cache
584 * @return void
585 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800586 public function set_cache_header($last_modified, $expiration)
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800587 {
588 $max_age = $expiration - $_SERVER['REQUEST_TIME'];
589
590 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])))
591 {
592 $this->set_status_header(304);
593 exit;
594 }
595 else
596 {
597 header('Pragma: public');
598 header('Cache-Control: max-age=' . $max_age . ', public');
599 header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
600 header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
601 }
602 }
603
604
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800605
606
607 // --------------------------------------------------------------------
608 /**
609 * Reduce excessive size of HTML content.
610 *
611 * @param string
612 * @param string
613 * @return string
614 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800615 public function minify($output, $type='text/html')
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800616 {
617 switch ($type)
618 {
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800619 case 'text/html':
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800620
621 $size_before = strlen($output);
622
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800623 // Find all the <pre>,<code>,<textarea>, and <javascript> tags
624 // We'll want to return them to this unprocessed state later.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800625 preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
626 preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
627 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800628 preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800629
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800630 // Minify the CSS in all the <style> tags.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800631 preg_match_all('{<style.+</style>}msU', $output, $style_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800632 foreach ($style_clean[0] as $s)
633 {
Thor1b8d0ef2012-01-28 01:48:04 -0800634 $output = str_replace($s, $this->minify($s, 'text/css'), $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800635 }
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800636
637 // Minify the javascript in <script> tags.
638 foreach ($javascript_clean[0] as $s)
639 {
Thor1b8d0ef2012-01-28 01:48:04 -0800640 $javascript_mini[] = $this->minify($s, 'text/javascript');
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800641 }
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800642
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800643 // Replace multiple spaces with a single space.
Thor1b8d0ef2012-01-28 01:48:04 -0800644 $output = preg_replace('!\s{2,}!', ' ', $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800645
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800646 // Remove comments (non-MSIE conditionals)
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800647 $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800648
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800649 // Remove spaces around block-level elements.
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800650 $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 -0800651
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800652 // Replace mangled <pre> etc. tags with unprocessed ones.
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800653
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800654 if ( ! empty($pres_clean))
655 {
656 preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
657 $output = str_replace($pres_messed[0], $pres_clean[0], $output);
658 }
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800659
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800660 if ( ! empty($codes_clean))
661 {
662 preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
663 $output = str_replace($codes_messed[0], $codes_clean[0], $output);
664 }
665
666 if ( ! empty($codes_clean))
667 {
668 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
669 $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
670 }
671
672 if (isset($javascript_mini))
673 {
674 preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
675 $output = str_replace($javascript_messed[0], $javascript_mini, $output);
676 }
677
678 $size_removed = $size_before - strlen($output);
679 $savings_percent = round(($size_removed / $size_before * 100));
680
681 log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800682
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800683 break;
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800684
685
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800686 case 'text/css':
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800687
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800688 //Remove CSS comments
689 $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output);
690
691 // Remove spaces around curly brackets, colons,
692 // semi-colons, parenthesis, commas
693 $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output);
694
695 break;
696
697
698 case 'text/javascript':
699
Thor1b8d0ef2012-01-28 01:48:04 -0800700 // Currently leaves JavaScript untouched.
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800701 break;
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800702 }
703
704 return $output;
705 }
706
707
Derek Allard2067d1a2008-11-13 22:59:24 +0000708}
Derek Allard2067d1a2008-11-13 22:59:24 +0000709
710/* End of file Output.php */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200711/* Location: ./system/core/Output.php */