blob: b6f69fd8830b09526aed1e50ddd53594bfaf84de [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
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 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020027defined('BASEPATH') OR exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +000028
Derek Allard2067d1a2008-11-13 22:59:24 +000029/**
30 * Output Class
31 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030032 * Responsible for sending final output to the browser.
Derek Allard2067d1a2008-11-13 22:59:24 +000033 *
34 * @package CodeIgniter
35 * @subpackage Libraries
36 * @category Output
Derek Jonesf4a4bd82011-10-20 12:18:42 -050037 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000038 * @link http://codeigniter.com/user_guide/libraries/output.html
39 */
40class CI_Output {
41
David Behler07b53422011-08-15 00:25:06 +020042 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030043 * Final output string
David Behler07b53422011-08-15 00:25:06 +020044 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030045 * @var string
David Behler07b53422011-08-15 00:25:06 +020046 */
Andrey Andreev0f221172012-04-13 14:52:16 +030047 public $final_output;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030048
David Behler07b53422011-08-15 00:25:06 +020049 /**
50 * Cache expiration time
51 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030052 * @var int
David Behler07b53422011-08-15 00:25:06 +020053 */
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030054 public $cache_expiration = 0;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030055
David Behler07b53422011-08-15 00:25:06 +020056 /**
57 * List of server headers
58 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030059 * @var array
David Behler07b53422011-08-15 00:25:06 +020060 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040061 public $headers = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030062
David Behler07b53422011-08-15 00:25:06 +020063 /**
64 * List of mime types
65 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030066 * @var array
David Behler07b53422011-08-15 00:25:06 +020067 */
Andrey Andreev6ef498b2012-06-05 22:01:58 +030068 public $mimes = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030069
David Behler07b53422011-08-15 00:25:06 +020070 /**
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080071 * Mime-type for the current page
72 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030073 * @var string
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080074 */
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030075 protected $mime_type = 'text/html';
Andrey Andreev0140ddd2012-06-16 01:12:56 +030076
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080077 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030078 * Enable Profiler flag
David Behler07b53422011-08-15 00:25:06 +020079 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030080 * @var bool
David Behler07b53422011-08-15 00:25:06 +020081 */
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030082 public $enable_profiler = FALSE;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030083
David Behler07b53422011-08-15 00:25:06 +020084 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030085 * zLib output compression flag
David Behler07b53422011-08-15 00:25:06 +020086 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030087 * @var bool
David Behler07b53422011-08-15 00:25:06 +020088 */
Andrey Andreev0140ddd2012-06-16 01:12:56 +030089 protected $_zlib_oc = FALSE;
Andrey Andreev92ebfb62012-05-17 12:49:24 +030090
David Behler07b53422011-08-15 00:25:06 +020091 /**
92 * List of profiler sections
93 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030094 * @var array
David Behler07b53422011-08-15 00:25:06 +020095 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -040096 protected $_profiler_sections = array();
Andrey Andreev92ebfb62012-05-17 12:49:24 +030097
David Behler07b53422011-08-15 00:25:06 +020098 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +030099 * Parse markers flag
David Behler07b53422011-08-15 00:25:06 +0200100 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300101 * Whether or not to parse variables like {elapsed_time} and {memory_usage}.
102 *
103 * @var bool
David Behler07b53422011-08-15 00:25:06 +0200104 */
Timothy Warren48a7fbb2012-04-23 11:58:16 -0400105 public $parse_exec_vars = TRUE;
Derek Jonesee71c802010-03-10 10:05:05 -0600106
Timothy Warrenad475052012-04-19 13:21:06 -0400107 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300108 * Class constructor
109 *
110 * Determines whether zLib output compression will be used.
Andrey Andreev92ebfb62012-05-17 12:49:24 +0300111 *
112 * @return void
Timothy Warrenad475052012-04-19 13:21:06 -0400113 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200114 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +0000115 {
Andrey Andreev9ba661b2012-06-04 14:44:34 +0300116 $this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
Barry Mienydd671972010-10-04 16:33:58 +0200117
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000118 // Get mime types for later
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300119 $this->mimes =& get_mimes();
Greg Akerd96f8822011-12-27 16:23:47 -0600120
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200121 log_message('debug', 'Output Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000122 }
Barry Mienydd671972010-10-04 16:33:58 +0200123
Derek Allard2067d1a2008-11-13 22:59:24 +0000124 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200125
Derek Allard2067d1a2008-11-13 22:59:24 +0000126 /**
127 * Get Output
128 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300129 * Returns the current output string.
Derek Allard2067d1a2008-11-13 22:59:24 +0000130 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000131 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200132 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200133 public function get_output()
Derek Allard2067d1a2008-11-13 22:59:24 +0000134 {
135 return $this->final_output;
136 }
Barry Mienydd671972010-10-04 16:33:58 +0200137
Derek Allard2067d1a2008-11-13 22:59:24 +0000138 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200139
Derek Allard2067d1a2008-11-13 22:59:24 +0000140 /**
141 * Set Output
142 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300143 * Sets the output string.
Derek Allard2067d1a2008-11-13 22:59:24 +0000144 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300145 * @param string $output Output data
Andrew Podner4296a652012-12-17 07:51:15 -0500146 * @return CI_Output
Barry Mienydd671972010-10-04 16:33:58 +0200147 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200148 public function set_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000149 {
150 $this->final_output = $output;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000151 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000152 }
153
154 // --------------------------------------------------------------------
155
156 /**
157 * Append Output
158 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300159 * Appends data onto the output string.
Derek Allard2067d1a2008-11-13 22:59:24 +0000160 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300161 * @param string $output Data to append
Andrew Podner4296a652012-12-17 07:51:15 -0500162 * @return CI_Output
Barry Mienydd671972010-10-04 16:33:58 +0200163 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200164 public function append_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000165 {
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300166 if (empty($this->final_output))
Derek Allard2067d1a2008-11-13 22:59:24 +0000167 {
168 $this->final_output = $output;
169 }
170 else
171 {
172 $this->final_output .= $output;
173 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000174
175 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000176 }
177
178 // --------------------------------------------------------------------
179
180 /**
181 * Set Header
182 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300183 * Lets you set a server header which will be sent with the final output.
Derek Allard2067d1a2008-11-13 22:59:24 +0000184 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300185 * Note: If a file is cached, headers will not be sent.
186 * @todo We need to figure out how to permit headers to be cached.
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300188 * @param string $header Header
189 * @param bool $replace Whether to replace the old header value, if already set
Andrew Podner4296a652012-12-17 07:51:15 -0500190 * @return CI_Output
Barry Mienydd671972010-10-04 16:33:58 +0200191 */
Andrey Andreev44dc50e2012-10-28 13:30:21 +0200192 public function set_header($header, $replace = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000193 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200194 // If zlib.output_compression is enabled it will compress the output,
195 // but it will not modify the content-length header to compensate for
196 // the reduction, causing the browser to hang waiting for more data.
197 // We'll just skip content-length in those cases.
Alex Bilbieed944a32012-06-02 11:07:47 +0100198 if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) === 0)
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200199 {
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300200 return $this;
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200201 }
Barry Mienydd671972010-10-04 16:33:58 +0200202
Derek Allard2067d1a2008-11-13 22:59:24 +0000203 $this->headers[] = array($header, $replace);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000204 return $this;
205 }
206
207 // --------------------------------------------------------------------
208
209 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300210 * Set Content-Type Header
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000211 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300212 * @param string $mime_type Extension of the file we're outputting
213 * @param string $charset Character set (default: NULL)
Andrew Podner4296a652012-12-17 07:51:15 -0500214 * @return CI_Output
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000215 */
Andrey Andreev47b67332012-06-06 15:58:05 +0300216 public function set_content_type($mime_type, $charset = NULL)
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000217 {
218 if (strpos($mime_type, '/') === FALSE)
219 {
220 $extension = ltrim($mime_type, '.');
221
222 // Is this extension supported?
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300223 if (isset($this->mimes[$extension]))
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000224 {
Andrey Andreev6ef498b2012-06-05 22:01:58 +0300225 $mime_type =& $this->mimes[$extension];
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000226
227 if (is_array($mime_type))
228 {
229 $mime_type = current($mime_type);
230 }
231 }
232 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300233
Thor (atiredmachine)242115c2012-01-24 23:08:11 -0800234 $this->mime_type = $mime_type;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000235
Andrey Andreev47b67332012-06-06 15:58:05 +0300236 if (empty($charset))
237 {
238 $charset = config_item('charset');
239 }
240
241 $header = 'Content-Type: '.$mime_type
242 .(empty($charset) ? NULL : '; charset='.strtolower($charset));
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000243
244 $this->headers[] = array($header, TRUE);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000245 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000246 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300247
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700248 // --------------------------------------------------------------------
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300249
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700250 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300251 * Get Current Content-Type Header
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700252 *
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300253 * @return string 'text/html', if not already set
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700254 */
Songpol Sripaoeiam614db072012-04-03 01:29:28 +0700255 public function get_content_type()
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700256 {
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300257 for ($i = 0, $c = count($this->headers); $i < $c; $i++)
Songpol Sripao-eiam38c0a722012-04-01 20:10:35 +0700258 {
Andrey Andreevcc4b0032012-11-29 17:21:43 +0200259 if (sscanf($this->headers[$i][0], 'Content-Type: %[^;]', $content_type) === 1)
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700260 {
Andrey Andreev7a7ad782012-11-12 17:21:01 +0200261 return $content_type;
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700262 }
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700263 }
Andrey Andreev00adf1d2012-04-03 12:30:50 +0300264
Songpol Sripaoeiamb9667012012-04-01 17:13:44 +0700265 return 'text/html';
Songpol Sripaoeiam52fe7bb2012-04-01 11:43:20 +0700266 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000267
268 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200269
Derek Allard2067d1a2008-11-13 22:59:24 +0000270 /**
Andrey Andreevcc4b0032012-11-29 17:21:43 +0200271 * Get Header
272 *
273 * @param string $header_name
274 * @return string
275 */
276 public function get_header($header)
277 {
278 // Combine headers already sent with our batched headers
279 $headers = array_merge(
280 // We only need [x][0] from our multi-dimensional array
281 array_map('array_shift', $this->headers),
282 headers_list()
283 );
284
285 if (empty($headers) OR empty($header))
286 {
287 return NULL;
288 }
289
290 for ($i = 0, $c = count($headers); $i < $c; $i++)
291 {
292 if (strncasecmp($header, $headers[$i], $l = strlen($header)) === 0)
293 {
294 return trim(substr($headers[$i], $l+1));
295 }
296 }
297
298 return NULL;
299 }
300
301 // --------------------------------------------------------------------
302
303 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000304 * Set HTTP Status Header
Barry Mienydd671972010-10-04 16:33:58 +0200305 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300306 * As of version 1.7.2, this is an alias for common function
307 * set_status_header().
308 *
309 * @param int $code Status code (default: 200)
310 * @param string $text Optional message
Andrew Podner4296a652012-12-17 07:51:15 -0500311 * @return CI_Output
Barry Mienydd671972010-10-04 16:33:58 +0200312 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200313 public function set_status_header($code = 200, $text = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000314 {
Derek Jones817163a2009-07-11 17:05:58 +0000315 set_status_header($code, $text);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000316 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000317 }
Barry Mienydd671972010-10-04 16:33:58 +0200318
Derek Allard2067d1a2008-11-13 22:59:24 +0000319 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200320
Derek Allard2067d1a2008-11-13 22:59:24 +0000321 /**
322 * Enable/disable Profiler
323 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300324 * @param bool $val TRUE to enable or FALSE to disable
Andrew Podner4296a652012-12-17 07:51:15 -0500325 * @return CI_Output
Barry Mienydd671972010-10-04 16:33:58 +0200326 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200327 public function enable_profiler($val = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000328 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300329 $this->enable_profiler = is_bool($val) ? $val : TRUE;
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 // --------------------------------------------------------------------
Derek Jonesee71c802010-03-10 10:05:05 -0600334
335 /**
336 * Set Profiler Sections
337 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300338 * Allows override of default/config settings for
339 * Profiler section display.
Derek Jonesee71c802010-03-10 10:05:05 -0600340 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300341 * @param array $sections Profiler sections
Andrew Podner4296a652012-12-17 07:51:15 -0500342 * @return CI_Output
Derek Jonesee71c802010-03-10 10:05:05 -0600343 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200344 public function set_profiler_sections($sections)
Derek Jonesee71c802010-03-10 10:05:05 -0600345 {
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300346 if (isset($sections['query_toggle_count']))
347 {
348 $this->_profiler_sections['query_toggle_count'] = (int) $sections['query_toggle_count'];
349 unset($sections['query_toggle_count']);
350 }
351
Derek Jonesee71c802010-03-10 10:05:05 -0600352 foreach ($sections as $section => $enable)
353 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300354 $this->_profiler_sections[$section] = ($enable !== FALSE);
Derek Jonesee71c802010-03-10 10:05:05 -0600355 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000356
357 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600358 }
359
360 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200361
Derek Allard2067d1a2008-11-13 22:59:24 +0000362 /**
363 * Set Cache
364 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300365 * @param int $time Cache expiration time in seconds
Andrew Podner4296a652012-12-17 07:51:15 -0500366 * @return CI_Output
Barry Mienydd671972010-10-04 16:33:58 +0200367 */
Michiel Vugteveen0609d582012-01-08 13:26:17 +0100368 public function cache($time)
Derek Allard2067d1a2008-11-13 22:59:24 +0000369 {
Andrey Andreev7b53d042012-03-26 23:02:32 +0300370 $this->cache_expiration = is_numeric($time) ? $time : 0;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000371 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000372 }
Barry Mienydd671972010-10-04 16:33:58 +0200373
Derek Allard2067d1a2008-11-13 22:59:24 +0000374 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200375
Derek Allard2067d1a2008-11-13 22:59:24 +0000376 /**
377 * Display Output
378 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300379 * Processes sends the sends finalized output data to the browser along
380 * with any server headers and profile data. It also stops benchmark
381 * timers so the page rendering speed and memory usage can be shown.
Derek Allard2067d1a2008-11-13 22:59:24 +0000382 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300383 * Note: All "view" data is automatically put into $this->final_output
384 * by controller class.
Derek Allard2067d1a2008-11-13 22:59:24 +0000385 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300386 * @uses CI_Output::$final_output
387 * @param string $output Output data override
388 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200389 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200390 public function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200391 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500392 // Note: We use globals because we can't use $CI =& get_instance()
Derek Allard2067d1a2008-11-13 22:59:24 +0000393 // since this function is sometimes called by the caching mechanism,
394 // which happens before the CI super object is available.
395 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500396
397 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600398 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500399 {
400 $CI =& get_instance();
401 }
402
Derek Allard2067d1a2008-11-13 22:59:24 +0000403 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200404
Derek Allard2067d1a2008-11-13 22:59:24 +0000405 // Set the output data
Alex Bilbieed944a32012-06-02 11:07:47 +0100406 if ($output === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000407 {
408 $output =& $this->final_output;
409 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300410
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800411 // --------------------------------------------------------------------
412
413 // Is minify requested?
414 if ($CFG->item('minify_output') === TRUE)
415 {
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800416 $output = $this->minify($output, $this->mime_type);
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800417 }
418
Derek Allard2067d1a2008-11-13 22:59:24 +0000419 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200420
vkeranova8349bc2012-10-27 18:07:59 +0300421 // Do we need to write a cache file? Only if the controller does not have its
Derek Jonesd7633492010-09-28 13:14:57 -0500422 // own _output() method and we are not dealing with a cache file, which we
423 // can determine by the existence of the $CI object above
424 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000425 {
426 $this->_write_cache($output);
427 }
Barry Mienydd671972010-10-04 16:33:58 +0200428
Derek Allard2067d1a2008-11-13 22:59:24 +0000429 // --------------------------------------------------------------------
430
431 // Parse out the elapsed time and memory usage,
432 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200433
434 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600435
436 if ($this->parse_exec_vars === TRUE)
437 {
Andrey Andreevc839d282012-06-07 14:35:27 +0300438 $memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
Barry Mienydd671972010-10-04 16:33:58 +0200439
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200440 $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
Derek Jones46520492010-03-02 13:53:25 -0600441 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000442
443 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200444
Derek Allard2067d1a2008-11-13 22:59:24 +0000445 // Is compression requested?
Alex Bilbieed944a32012-06-02 11:07:47 +0100446 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200447 && extension_loaded('zlib')
448 && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200450 ob_start('ob_gzhandler');
Derek Allard2067d1a2008-11-13 22:59:24 +0000451 }
452
453 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200454
Derek Allard2067d1a2008-11-13 22:59:24 +0000455 // Are there any server headers to send?
456 if (count($this->headers) > 0)
457 {
458 foreach ($this->headers as $header)
459 {
460 @header($header[0], $header[1]);
461 }
Barry Mienydd671972010-10-04 16:33:58 +0200462 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000463
464 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200465
Derek Jonesd7633492010-09-28 13:14:57 -0500466 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000467 // If not we know we are dealing with a cache file so we'll
468 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500469 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000470 {
471 echo $output;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200472 log_message('debug', 'Final output sent to browser');
473 log_message('debug', 'Total execution time: '.$elapsed);
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300474 return;
Derek Allard2067d1a2008-11-13 22:59:24 +0000475 }
Barry Mienydd671972010-10-04 16:33:58 +0200476
Derek Allard2067d1a2008-11-13 22:59:24 +0000477 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200478
Derek Allard2067d1a2008-11-13 22:59:24 +0000479 // Do we need to generate profile data?
480 // If so, load the Profile class and run it.
Alex Bilbieed944a32012-06-02 11:07:47 +0100481 if ($this->enable_profiler === TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000482 {
Barry Mienydd671972010-10-04 16:33:58 +0200483 $CI->load->library('profiler');
Derek Jonesee71c802010-03-10 10:05:05 -0600484 if ( ! empty($this->_profiler_sections))
485 {
486 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200487 }
Derek Jonesee71c802010-03-10 10:05:05 -0600488
Derek Allard2067d1a2008-11-13 22:59:24 +0000489 // If the output data contains closing </body> and </html> tags
490 // we will remove them and add them back after we insert the profile data
Andrey Andreevcba20b12012-01-09 10:16:41 +0200491 $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
492 if ($count > 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000493 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000494 $output .= '</body></html>';
495 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000496 }
Barry Mienydd671972010-10-04 16:33:58 +0200497
Derek Allard2067d1a2008-11-13 22:59:24 +0000498 // Does the controller contain a function named _output()?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500499 // If so send the output there. Otherwise, echo it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000500 if (method_exists($CI, '_output'))
501 {
502 $CI->_output($output);
503 }
504 else
505 {
Andrey Andreevedc87552012-01-09 09:35:10 +0200506 echo $output; // Send it to the browser!
Derek Allard2067d1a2008-11-13 22:59:24 +0000507 }
Barry Mienydd671972010-10-04 16:33:58 +0200508
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200509 log_message('debug', 'Final output sent to browser');
510 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000511 }
Barry Mienydd671972010-10-04 16:33:58 +0200512
Derek Allard2067d1a2008-11-13 22:59:24 +0000513 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200514
Derek Allard2067d1a2008-11-13 22:59:24 +0000515 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300516 * Write Cache
Derek Allard2067d1a2008-11-13 22:59:24 +0000517 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300518 * @param string $output Output data to cache
Derek Allard2067d1a2008-11-13 22:59:24 +0000519 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200520 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200521 public function _write_cache($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000522 {
Barry Mienydd671972010-10-04 16:33:58 +0200523 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000524 $path = $CI->config->item('cache_path');
Alex Bilbieed944a32012-06-02 11:07:47 +0100525 $cache_path = ($path === '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200526
Derek Allard2067d1a2008-11-13 22:59:24 +0000527 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
528 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200529 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000530 return;
531 }
Barry Mienydd671972010-10-04 16:33:58 +0200532
Derek Allard2067d1a2008-11-13 22:59:24 +0000533 $uri = $CI->config->item('base_url').
534 $CI->config->item('index_page').
535 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200536
Derek Allard2067d1a2008-11-13 22:59:24 +0000537 $cache_path .= md5($uri);
538
539 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
540 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200541 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000542 return;
543 }
Barry Mienydd671972010-10-04 16:33:58 +0200544
Derek Allard2067d1a2008-11-13 22:59:24 +0000545 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200546
Derek Allard2067d1a2008-11-13 22:59:24 +0000547 if (flock($fp, LOCK_EX))
548 {
549 fwrite($fp, $expire.'TS--->'.$output);
550 flock($fp, LOCK_UN);
551 }
552 else
553 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200554 log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000555 return;
556 }
557 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000558 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000559
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200560 log_message('debug', 'Cache file written: '.$cache_path);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300561
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800562 // Send HTTP cache-control headers to browser to match file cache settings.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800563 $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000564 }
565
566 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200567
Derek Allard2067d1a2008-11-13 22:59:24 +0000568 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300569 * Update/serve cached output
Derek Allard2067d1a2008-11-13 22:59:24 +0000570 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300571 * @uses CI_Config
572 * @uses CI_URI
573 *
574 * @param object &$CFG CI_Config class instance
575 * @param object &$URI CI_URI class instance
576 * @return bool TRUE on success or FALSE on failure
Barry Mienydd671972010-10-04 16:33:58 +0200577 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200578 public function _display_cache(&$CFG, &$URI)
Derek Allard2067d1a2008-11-13 22:59:24 +0000579 {
Alex Bilbieed944a32012-06-02 11:07:47 +0100580 $cache_path = ($CFG->item('cache_path') === '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200581
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200582 // Build the file path. The file name is an MD5 hash of the full URI
583 $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
Derek Allard2067d1a2008-11-13 22:59:24 +0000584 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200585
Andrey Andreevc90d6512012-01-08 04:35:02 +0200586 if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allard2067d1a2008-11-13 22:59:24 +0000587 {
588 return FALSE;
589 }
Barry Mienydd671972010-10-04 16:33:58 +0200590
Derek Allard2067d1a2008-11-13 22:59:24 +0000591 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200592
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200593 $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
Barry Mienydd671972010-10-04 16:33:58 +0200594
Derek Allard2067d1a2008-11-13 22:59:24 +0000595 flock($fp, LOCK_UN);
596 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200597
598 // Strip out the embedded timestamp
vlakoff239e0e52012-09-16 19:16:33 +0200599 if ( ! preg_match('/^(\d+)TS--->/', $cache, $match))
Derek Allard2067d1a2008-11-13 22:59:24 +0000600 {
601 return FALSE;
602 }
Barry Mienydd671972010-10-04 16:33:58 +0200603
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800604 $last_modified = filemtime($cache_path);
vlakoff239e0e52012-09-16 19:16:33 +0200605 $expire = $match[1];
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800606
607 // Has the file expired?
608 if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
Derek Jonesd99e6032010-03-19 19:57:33 -0500609 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800610 // If so we'll delete it.
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200611 @unlink($filepath);
612 log_message('debug', 'Cache file has expired. File deleted.');
613 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000614 }
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800615 else
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300616 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800617 // Or else send the HTTP cache control headers.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800618 $this->set_cache_header($last_modified, $expire);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800619 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000620
621 // Display the cache
vlakoff1288fe32012-07-23 16:22:06 +0200622 $this->_display(substr($cache, strlen($match[0])));
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200623 log_message('debug', 'Cache file is current. Sending it to browser.');
Derek Allard2067d1a2008-11-13 22:59:24 +0000624 return TRUE;
625 }
626
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800627 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300628
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800629 /**
Andrey Andreevb37d2bc2012-11-30 02:19:35 +0200630 * Delete cache
631 *
632 * @param string $uri URI string
633 * @return bool
634 */
635 public function delete_cache($uri = '')
636 {
637 $CI =& get_instance();
638 $cache_path = $CI->config->item('cache_path');
639 if ($cache_path === '')
640 {
641 $cache_path = APPPATH.'cache/';
642 }
643
644 if ( ! is_dir($cache_path))
645 {
646 log_message('error', 'Unable to find cache path: '.$cache_path);
647 return FALSE;
648 }
649
650 if (empty($uri))
651 {
652 $uri = $CI->uri->uri_string();
653 }
654
655 $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri);
656
657 if ( ! @unlink($cache_path))
658 {
659 log_message('error', 'Unable to delete cache file for '.$uri);
660 return FALSE;
661 }
662
663 return TRUE;
664 }
665
666 // --------------------------------------------------------------------
667
668 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300669 * Set Cache Header
670 *
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800671 * Set the HTTP headers to match the server-side file cache settings
672 * in order to reduce bandwidth.
673 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300674 * @param int $last_modified Timestamp of when the page was last modified
675 * @param int $expiration Timestamp of when should the requested page expire from cache
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800676 * @return void
677 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800678 public function set_cache_header($last_modified, $expiration)
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300679 {
680 $max_age = $expiration - $_SERVER['REQUEST_TIME'];
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800681
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300682 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']))
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800683 {
684 $this->set_status_header(304);
685 exit;
686 }
687 else
688 {
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300689 header('Pragma: public');
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800690 header('Cache-Control: max-age=' . $max_age . ', public');
691 header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
692 header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
693 }
694 }
695
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800696 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300697
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800698 /**
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300699 * Minify
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800700 *
Andrey Andreevb9fe7e92012-10-27 18:45:59 +0300701 * Reduce excessive size of HTML/CSS/JavaScript content.
702 *
703 * @param string $output Output to minify
704 * @param string $type Output content MIME type
705 * @return string Minified output
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800706 */
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300707 public function minify($output, $type = 'text/html')
Andrew Podner79503c52012-12-18 07:47:38 -0500708 {
709 switch ($type)
710 {
711 case 'text/html':
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800712
Andrey Andreev72ed4c32012-12-19 17:07:54 +0200713 if (($size_before = strlen($output)) === 0)
Andrew Podner79503c52012-12-18 07:47:38 -0500714 {
715 return '';
716 }
Phil Sturgeon7a744a82012-06-23 17:21:00 +0100717
Andrew Podner79503c52012-12-18 07:47:38 -0500718 // Find all the <pre>,<code>,<textarea>, and <javascript> tags
719 // We'll want to return them to this unprocessed state later.
720 preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
721 preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
722 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
723 preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800724
Andrew Podner79503c52012-12-18 07:47:38 -0500725 // Minify the CSS in all the <style> tags.
726 preg_match_all('{<style.+</style>}msU', $output, $style_clean);
727 foreach ($style_clean[0] as $s)
728 {
Andrew Podner9dfceda2012-12-18 19:37:22 -0500729 $output = str_replace($s, $this->_minify_script_style($s, TRUE), $output);
Andrew Podner79503c52012-12-18 07:47:38 -0500730 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300731
Andrew Podner79503c52012-12-18 07:47:38 -0500732 // Minify the javascript in <script> tags.
733 foreach ($javascript_clean[0] as $s)
734 {
Andrew Podner9dfceda2012-12-18 19:37:22 -0500735 $javascript_mini[] = $this->_minify_script_style($s, TRUE);
Andrew Podner79503c52012-12-18 07:47:38 -0500736 }
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800737
Andrew Podner79503c52012-12-18 07:47:38 -0500738 // Replace multiple spaces with a single space.
739 $output = preg_replace('!\s{2,}!', ' ', $output);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300740
Andrew Podner79503c52012-12-18 07:47:38 -0500741 // Remove comments (non-MSIE conditionals)
742 $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800743
Andrew Podner79503c52012-12-18 07:47:38 -0500744 // Remove spaces around block-level elements.
745 $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 -0800746
Andrew Podner79503c52012-12-18 07:47:38 -0500747 // Replace mangled <pre> etc. tags with unprocessed ones.
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300748
Andrew Podner79503c52012-12-18 07:47:38 -0500749 if ( ! empty($pres_clean))
750 {
751 preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
752 $output = str_replace($pres_messed[0], $pres_clean[0], $output);
753 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300754
Andrew Podner79503c52012-12-18 07:47:38 -0500755 if ( ! empty($codes_clean))
756 {
757 preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
758 $output = str_replace($codes_messed[0], $codes_clean[0], $output);
759 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300760
Andrew Podner79503c52012-12-18 07:47:38 -0500761 if ( ! empty($codes_clean))
762 {
763 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
764 $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
765 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300766
Andrew Podner79503c52012-12-18 07:47:38 -0500767 if (isset($javascript_mini))
768 {
769 preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
770 $output = str_replace($javascript_messed[0], $javascript_mini, $output);
771 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300772
Andrew Podner79503c52012-12-18 07:47:38 -0500773 $size_removed = $size_before - strlen($output);
774 $savings_percent = round(($size_removed / $size_before * 100));
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800775
Andrew Podner79503c52012-12-18 07:47:38 -0500776 log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800777
Andrew Podner79503c52012-12-18 07:47:38 -0500778 break;
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300779
Andrew Podner79503c52012-12-18 07:47:38 -0500780 case 'text/css':
781 case 'text/javascript':
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300782
Andrew Podner9dfceda2012-12-18 19:37:22 -0500783 $output = $this->_minify_script_style($output);
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300784
Andrew Podner79503c52012-12-18 07:47:38 -0500785 break;
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800786
Andrew Podner79503c52012-12-18 07:47:38 -0500787 default: break;
788 }
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300789
Andrew Podner79503c52012-12-18 07:47:38 -0500790 return $output;
791 }
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800792
Andrew Podner79503c52012-12-18 07:47:38 -0500793 // --------------------------------------------------------------------
Andrey Andreev0140ddd2012-06-16 01:12:56 +0300794
Andrew Podner369b3142012-12-17 22:32:02 -0500795 /**
796 * Minify Style and Script
797 *
798 * Reduce excessive size of CSS/JavaScript content. To remove spaces this
Andrew Podner79503c52012-12-18 07:47:38 -0500799 * script walks the string as an array and determines if the pointer is inside
800 * a string created by single quotes or double quotes. spaces inside those
801 * strings are not stripped. Opening and closing tags are severed from
802 * the string initially and saved without stripping whitespace to preserve
803 * the tags and any associated properties if tags are present
Andrew Podner369b3142012-12-17 22:32:02 -0500804 *
Andrew Podner79b70272012-12-25 12:04:01 -0500805 * Minification logic/workflow is similar to methods used by Douglas Crockford
806 * in JSMIN. http://www.crockford.com/javascript/jsmin.html
807 *
808 * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon
809 * where there should be one will break the Javascript. New lines after a
810 * closing parenthesis are not recognized by the script. For best results
811 * be sure to terminate lines with a semicolon when appropriate.
812 *
813 *
814 * @param string $output Output to minify
Andrey Andreev72ed4c32012-12-19 17:07:54 +0200815 * @param bool $has_tags Specify if the output has style or script tags
Andrew Podner369b3142012-12-17 22:32:02 -0500816 * @return string Minified output
817 */
Andrew Podner9dfceda2012-12-18 19:37:22 -0500818 protected function _minify_script_style($output, $has_tags = FALSE)
Andrew Podner79503c52012-12-18 07:47:38 -0500819 {
820 // We only need this if there are tags in the file
Andrew Podner9dfceda2012-12-18 19:37:22 -0500821 if ($has_tags === TRUE)
Andrew Podner79503c52012-12-18 07:47:38 -0500822 {
823 // Remove opening tag and save for later
Andrew Podner79b70272012-12-25 12:04:01 -0500824 $pos = strpos($output, '>') + 1;
Andrew Podner79503c52012-12-18 07:47:38 -0500825 $open_tag = substr($output, 0, $pos);
826 $output = substr_replace($output, '', 0, $pos);
Andrew Podner369b3142012-12-17 22:32:02 -0500827
Andrew Podner79503c52012-12-18 07:47:38 -0500828 // Remove closing tag and save it for later
829 $end_pos = strlen($output);
830 $pos = strpos($output, '</');
831 $closing_tag = substr($output, $pos, $end_pos);
832 $output = substr_replace($output, '', $pos);
833 }
Andrew Podner369b3142012-12-17 22:32:02 -0500834
Andrew Podner79503c52012-12-18 07:47:38 -0500835 // Remove CSS comments
Andrey Andreev72ed4c32012-12-19 17:07:54 +0200836 $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output);
Andrew Podner369b3142012-12-17 22:32:02 -0500837
Andrew Podner79503c52012-12-18 07:47:38 -0500838 // Remove spaces around curly brackets, colons,
839 // semi-colons, parenthesis, commas
Andrey Andreev72ed4c32012-12-19 17:07:54 +0200840 $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!i', '$1', $output);
Andrew Podner369b3142012-12-17 22:32:02 -0500841
Andrew Podner79b70272012-12-25 12:04:01 -0500842 // Replace tabs with spaces
Andrew Podner464a7b72012-12-25 21:43:55 -0500843 // Replace carriage returns & multiple new lines with single new line
Andrew Podner79b70272012-12-25 12:04:01 -0500844 // and trim any leading or trailing whitespace
Andrew Podner96b95012012-12-28 08:30:43 -0500845 $output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output));
Andrew Podner79b70272012-12-25 12:04:01 -0500846
847 // Remove spaces when safe to do so.
848 $in_string = $in_dstring = $prev = FALSE;
Andrew Podner79503c52012-12-18 07:47:38 -0500849 $array_output = str_split($output);
850 foreach ($array_output as $key => $value)
851 {
Andrew Podner7747f0a2012-12-18 13:13:15 -0500852 if ($in_string === FALSE && $in_dstring === FALSE)
Andrew Podner79503c52012-12-18 07:47:38 -0500853 {
Andrew Podner7747f0a2012-12-18 13:13:15 -0500854 if ($value === ' ')
Andrew Podner79503c52012-12-18 07:47:38 -0500855 {
Andrew Podner79b70272012-12-25 12:04:01 -0500856 // Get the next element in the array for comparisons
857 $next = $array_output[$key + 1];
858
859 // Strip spaces preceded/followed by a non-ASCII character
860 // or not preceded/followed by an alphanumeric
861 // or not preceded/followed \ $ and _
Andrew Podnerae8c2d92012-12-26 08:05:44 -0500862 if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev))
863 && ( ! ctype_alnum($next) OR ! ctype_alnum($prev))
Andrew Podner464a7b72012-12-25 21:43:55 -0500864 && ( ! in_array($next, array('\\', '_', '$')) && ! in_array($prev, array('\\', '_', '$'))))
Andrew Podner79b70272012-12-25 12:04:01 -0500865 {
866 unset($array_output[$key]);
867 }
868 }
869 else
870 {
871 // Save this value as previous for the next iteration
872 // if it is not a blank space
873 $prev = $value;
Andrew Podner79503c52012-12-18 07:47:38 -0500874 }
875 }
Andrew Podner369b3142012-12-17 22:32:02 -0500876
Andrew Podner7747f0a2012-12-18 13:13:15 -0500877 if ($value === "'")
Andrew Podner79503c52012-12-18 07:47:38 -0500878 {
Andrew Podner7747f0a2012-12-18 13:13:15 -0500879 $in_string = ! $in_string;
Andrew Podner79503c52012-12-18 07:47:38 -0500880 }
Andrey Andreev72ed4c32012-12-19 17:07:54 +0200881 elseif ($value === '"')
Andrew Podner79503c52012-12-18 07:47:38 -0500882 {
Andrew Podner7747f0a2012-12-18 13:13:15 -0500883 $in_dstring = ! $in_dstring;
Andrew Podner79503c52012-12-18 07:47:38 -0500884 }
885 }
Andrew Podner369b3142012-12-17 22:32:02 -0500886
Andrew Podner79b70272012-12-25 12:04:01 -0500887 // Put the string back together after spaces have been stripped
888 $output = implode($array_output);
889
890 // Remove new line characters unless previous or next character is
891 // printable or Non-ASCII
892 preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE);
Andrew Podner464a7b72012-12-25 21:43:55 -0500893 $removed_lf = 0;
Andrew Podner79b70272012-12-25 12:04:01 -0500894 foreach ($lf as $feed_position)
895 {
Andrew Podner464a7b72012-12-25 21:43:55 -0500896 foreach ($feed_position as $position)
Andrew Podner79b70272012-12-25 12:04:01 -0500897 {
Andrew Podner464a7b72012-12-25 21:43:55 -0500898 $next_char = substr($output, $position[1] - $removed_lf + 1, 1);
899 $prev_char = substr($output, $position[1] - $removed_lf - 1, 1);
900 if ( ! ctype_print($next_char) && ! ctype_print($prev_char)
Andrew Podner96b95012012-12-28 08:30:43 -0500901 && ! preg_match('/^[\x20-\x7f]*$/D', $next_char)
902 && ! preg_match('/^[\x20-\x7f]*$/D', $prev_char))
Andrew Podner79b70272012-12-25 12:04:01 -0500903 {
Andrew Podner96b95012012-12-28 08:30:43 -0500904 $output = substr_replace($output, '', $position[1] - $removed_lf++, 1);
Andrew Podner79b70272012-12-25 12:04:01 -0500905 }
906 }
907 }
Andrew Podner369b3142012-12-17 22:32:02 -0500908
Andrew Podner79503c52012-12-18 07:47:38 -0500909 // Put the opening and closing tags back if applicable
Andrey Andreev72ed4c32012-12-19 17:07:54 +0200910 return isset($open_tag)
911 ? $open_tag.$output.$closing_tag
912 : $output;
Andrew Podner79503c52012-12-18 07:47:38 -0500913 }
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800914
Derek Allard2067d1a2008-11-13 22:59:24 +0000915}
Derek Allard2067d1a2008-11-13 22:59:24 +0000916
917/* End of file Output.php */
Andrey Andreevd8dba5d2012-12-17 15:42:01 +0200918/* Location: ./system/core/Output.php */