blob: c4eba30bb50bfc62890d8f04e9eff4e96d09badc [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 *
Greg Aker741de1c2010-11-10 14:52:57 -06005 * An open source application development framework for PHP 5.1.6 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
28// ------------------------------------------------------------------------
29
30/**
31 * Output Class
32 *
33 * Responsible for sending final output to browser
34 *
35 * @package CodeIgniter
36 * @subpackage Libraries
37 * @category Output
Derek Jonesf4a4bd82011-10-20 12:18:42 -050038 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000039 * @link http://codeigniter.com/user_guide/libraries/output.html
40 */
41class CI_Output {
42
David Behler07b53422011-08-15 00:25:06 +020043 /**
44 * Current output string
45 *
46 * @var string
David Behler07b53422011-08-15 00:25:06 +020047 */
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000048 protected $final_output;
David Behler07b53422011-08-15 00:25:06 +020049 /**
50 * Cache expiration time
51 *
52 * @var int
David Behler07b53422011-08-15 00:25:06 +020053 */
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000054 protected $cache_expiration = 0;
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 */
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000060 protected $headers = array();
David Behler07b53422011-08-15 00:25:06 +020061 /**
62 * List of mime types
63 *
64 * @var array
David Behler07b53422011-08-15 00:25:06 +020065 */
66 protected $mime_types = array();
67 /**
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080068 * Mime-type for the current page
69 *
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -080070 * @var string
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080071 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -080072 protected $mime_type = 'text/html';
Thor (atiredmachine)242115c2012-01-24 23:08:11 -080073 /**
David Behler07b53422011-08-15 00:25:06 +020074 * Determines wether profiler is enabled
75 *
76 * @var book
David Behler07b53422011-08-15 00:25:06 +020077 */
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000078 protected $enable_profiler = FALSE;
David Behler07b53422011-08-15 00:25:06 +020079 /**
80 * Determines if output compression is enabled
81 *
82 * @var bool
David Behler07b53422011-08-15 00:25:06 +020083 */
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000084 protected $_zlib_oc = FALSE;
David Behler07b53422011-08-15 00:25:06 +020085 /**
86 * List of profiler sections
87 *
88 * @var array
David Behler07b53422011-08-15 00:25:06 +020089 */
Phil Sturgeon60ed1a32011-03-08 21:43:54 +000090 protected $_profiler_sections = array();
David Behler07b53422011-08-15 00:25:06 +020091 /**
92 * Whether or not to parse variables like {elapsed_time} and {memory_usage}
93 *
94 * @var bool
David Behler07b53422011-08-15 00:25:06 +020095 */
96 protected $parse_exec_vars = TRUE;
Derek Jonesee71c802010-03-10 10:05:05 -060097
Andrey Andreev1f5fbb62012-01-07 20:53:29 +020098 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +000099 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200100 $this->_zlib_oc = @ini_get('zlib.output_compression');
Barry Mienydd671972010-10-04 16:33:58 +0200101
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000102 // Get mime types for later
Greg Akerd96f8822011-12-27 16:23:47 -0600103 if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
104 {
105 include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
106 }
107 else
108 {
109 include APPPATH.'config/mimes.php';
110 }
111
Eric Barnesbb5d4f72011-03-18 13:39:58 -0400112
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000113 $this->mime_types = $mimes;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200114 log_message('debug', 'Output Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000115 }
Barry Mienydd671972010-10-04 16:33:58 +0200116
Derek Allard2067d1a2008-11-13 22:59:24 +0000117 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200118
Derek Allard2067d1a2008-11-13 22:59:24 +0000119 /**
120 * Get Output
121 *
122 * Returns the current output string
123 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000124 * @return string
Barry Mienydd671972010-10-04 16:33:58 +0200125 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200126 public function get_output()
Derek Allard2067d1a2008-11-13 22:59:24 +0000127 {
128 return $this->final_output;
129 }
Barry Mienydd671972010-10-04 16:33:58 +0200130
Derek Allard2067d1a2008-11-13 22:59:24 +0000131 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200132
Derek Allard2067d1a2008-11-13 22:59:24 +0000133 /**
134 * Set Output
135 *
136 * Sets the output string
137 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000138 * @param string
139 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200140 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200141 public function set_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000142 {
143 $this->final_output = $output;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000144 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000145 }
146
147 // --------------------------------------------------------------------
148
149 /**
150 * Append Output
151 *
152 * Appends data onto the output string
153 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000154 * @param string
155 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200156 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200157 public function append_output($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000158 {
159 if ($this->final_output == '')
160 {
161 $this->final_output = $output;
162 }
163 else
164 {
165 $this->final_output .= $output;
166 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000167
168 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000169 }
170
171 // --------------------------------------------------------------------
172
173 /**
174 * Set Header
175 *
176 * Lets you set a server header which will be outputted with the final display.
177 *
Derek Jones37f4b9c2011-07-01 17:56:50 -0500178 * Note: If a file is cached, headers will not be sent. We need to figure out
Derek Allard2067d1a2008-11-13 22:59:24 +0000179 * how to permit header data to be saved with the cache data...
180 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000181 * @param string
David Behler07b53422011-08-15 00:25:06 +0200182 * @param bool
Derek Allard2067d1a2008-11-13 22:59:24 +0000183 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200184 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200185 public function set_header($header, $replace = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000186 {
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200187 // If zlib.output_compression is enabled it will compress the output,
188 // but it will not modify the content-length header to compensate for
189 // the reduction, causing the browser to hang waiting for more data.
190 // We'll just skip content-length in those cases.
Pascal Kriete676e1cd2010-04-09 21:16:16 +0200191 if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
192 {
193 return;
194 }
Barry Mienydd671972010-10-04 16:33:58 +0200195
Derek Allard2067d1a2008-11-13 22:59:24 +0000196 $this->headers[] = array($header, $replace);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000197 return $this;
198 }
199
200 // --------------------------------------------------------------------
201
202 /**
203 * Set Content Type Header
204 *
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000205 * @param string extension of the file we're outputting
206 * @return void
207 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200208 public function set_content_type($mime_type)
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000209 {
210 if (strpos($mime_type, '/') === FALSE)
211 {
212 $extension = ltrim($mime_type, '.');
213
214 // Is this extension supported?
215 if (isset($this->mime_types[$extension]))
216 {
217 $mime_type =& $this->mime_types[$extension];
218
219 if (is_array($mime_type))
220 {
221 $mime_type = current($mime_type);
222 }
223 }
224 }
Thor (atiredmachine)242115c2012-01-24 23:08:11 -0800225
226 $this->mime_type = $mime_type;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000227
228 $header = 'Content-Type: '.$mime_type;
229
230 $this->headers[] = array($header, TRUE);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000231 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000232 }
233
234 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200235
Derek Allard2067d1a2008-11-13 22:59:24 +0000236 /**
237 * Set HTTP Status Header
Derek Jones817163a2009-07-11 17:05:58 +0000238 * moved to Common procedural functions in 1.7.2
Barry Mienydd671972010-10-04 16:33:58 +0200239 *
Barry Mienydd671972010-10-04 16:33:58 +0200240 * @param int the status code
241 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000242 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200243 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200244 public function set_status_header($code = 200, $text = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000245 {
Derek Jones817163a2009-07-11 17:05:58 +0000246 set_status_header($code, $text);
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000247 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000248 }
Barry Mienydd671972010-10-04 16:33:58 +0200249
Derek Allard2067d1a2008-11-13 22:59:24 +0000250 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200251
Derek Allard2067d1a2008-11-13 22:59:24 +0000252 /**
253 * Enable/disable Profiler
254 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000255 * @param bool
256 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200257 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200258 public function enable_profiler($val = TRUE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000259 {
260 $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000261 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000262 }
Barry Mienydd671972010-10-04 16:33:58 +0200263
Derek Allard2067d1a2008-11-13 22:59:24 +0000264 // --------------------------------------------------------------------
Derek Jonesee71c802010-03-10 10:05:05 -0600265
266 /**
267 * Set Profiler Sections
268 *
269 * Allows override of default / config settings for Profiler section display
270 *
Derek Jonesee71c802010-03-10 10:05:05 -0600271 * @param array
272 * @return void
273 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200274 public function set_profiler_sections($sections)
Derek Jonesee71c802010-03-10 10:05:05 -0600275 {
276 foreach ($sections as $section => $enable)
277 {
278 $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE;
279 }
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000280
281 return $this;
Derek Jonesee71c802010-03-10 10:05:05 -0600282 }
283
284 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200285
Derek Allard2067d1a2008-11-13 22:59:24 +0000286 /**
287 * Set Cache
288 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000289 * @param integer
290 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200291 */
Michiel Vugteveen0609d582012-01-08 13:26:17 +0100292 public function cache($time)
Derek Allard2067d1a2008-11-13 22:59:24 +0000293 {
294 $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
Phil Sturgeon60ed1a32011-03-08 21:43:54 +0000295 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000296 }
Barry Mienydd671972010-10-04 16:33:58 +0200297
Derek Allard2067d1a2008-11-13 22:59:24 +0000298 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200299
Derek Allard2067d1a2008-11-13 22:59:24 +0000300 /**
301 * Display Output
302 *
303 * All "view" data is automatically put into this variable by the controller class:
304 *
305 * $this->final_output
306 *
307 * This function sends the finalized output data to the browser along
Derek Jones37f4b9c2011-07-01 17:56:50 -0500308 * with any server headers and profile data. It also stops the
Derek Allard2067d1a2008-11-13 22:59:24 +0000309 * benchmark timer so the page rendering speed and memory usage can be shown.
310 *
David Behler07b53422011-08-15 00:25:06 +0200311 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000312 * @return mixed
Barry Mienydd671972010-10-04 16:33:58 +0200313 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200314 public function _display($output = '')
Barry Mienydd671972010-10-04 16:33:58 +0200315 {
Derek Jones37f4b9c2011-07-01 17:56:50 -0500316 // Note: We use globals because we can't use $CI =& get_instance()
Derek Allard2067d1a2008-11-13 22:59:24 +0000317 // since this function is sometimes called by the caching mechanism,
318 // which happens before the CI super object is available.
319 global $BM, $CFG;
Derek Jonesd7633492010-09-28 13:14:57 -0500320
321 // Grab the super object if we can.
Greg Akercc922102010-11-17 20:25:08 -0600322 if (class_exists('CI_Controller'))
Derek Jonesd7633492010-09-28 13:14:57 -0500323 {
324 $CI =& get_instance();
325 }
326
Derek Allard2067d1a2008-11-13 22:59:24 +0000327 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200328
Derek Allard2067d1a2008-11-13 22:59:24 +0000329 // Set the output data
330 if ($output == '')
331 {
332 $output =& $this->final_output;
333 }
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800334
335 // --------------------------------------------------------------------
336
337 // Is minify requested?
338 if ($CFG->item('minify_output') === TRUE)
339 {
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800340 $output = $this->minify($output, $this->mime_type);
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800341 }
342
Barry Mienydd671972010-10-04 16:33:58 +0200343
Derek Allard2067d1a2008-11-13 22:59:24 +0000344 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200345
Derek Jones37f4b9c2011-07-01 17:56:50 -0500346 // Do we need to write a cache file? Only if the controller does not have its
Derek Jonesd7633492010-09-28 13:14:57 -0500347 // own _output() method and we are not dealing with a cache file, which we
348 // can determine by the existence of the $CI object above
349 if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
Derek Allard2067d1a2008-11-13 22:59:24 +0000350 {
351 $this->_write_cache($output);
352 }
Barry Mienydd671972010-10-04 16:33:58 +0200353
Derek Allard2067d1a2008-11-13 22:59:24 +0000354 // --------------------------------------------------------------------
355
356 // Parse out the elapsed time and memory usage,
357 // then swap the pseudo-variables with the data
Barry Mienydd671972010-10-04 16:33:58 +0200358
359 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
Derek Jones46520492010-03-02 13:53:25 -0600360
361 if ($this->parse_exec_vars === TRUE)
362 {
363 $memory = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
Barry Mienydd671972010-10-04 16:33:58 +0200364
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200365 $output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
Derek Jones46520492010-03-02 13:53:25 -0600366 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000367
368 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200369
Derek Allard2067d1a2008-11-13 22:59:24 +0000370 // Is compression requested?
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200371 if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE
372 && extension_loaded('zlib')
373 && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000374 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200375 ob_start('ob_gzhandler');
Derek Allard2067d1a2008-11-13 22:59:24 +0000376 }
377
378 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200379
Derek Allard2067d1a2008-11-13 22:59:24 +0000380 // Are there any server headers to send?
381 if (count($this->headers) > 0)
382 {
383 foreach ($this->headers as $header)
384 {
385 @header($header[0], $header[1]);
386 }
Barry Mienydd671972010-10-04 16:33:58 +0200387 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000388
389 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200390
Derek Jonesd7633492010-09-28 13:14:57 -0500391 // Does the $CI object exist?
Derek Allard2067d1a2008-11-13 22:59:24 +0000392 // If not we know we are dealing with a cache file so we'll
393 // simply echo out the data and exit.
Derek Jonesd7633492010-09-28 13:14:57 -0500394 if ( ! isset($CI))
Derek Allard2067d1a2008-11-13 22:59:24 +0000395 {
396 echo $output;
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200397 log_message('debug', 'Final output sent to browser');
398 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000399 return TRUE;
400 }
Barry Mienydd671972010-10-04 16:33:58 +0200401
Derek Allard2067d1a2008-11-13 22:59:24 +0000402 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200403
Derek Allard2067d1a2008-11-13 22:59:24 +0000404 // Do we need to generate profile data?
405 // If so, load the Profile class and run it.
406 if ($this->enable_profiler == TRUE)
407 {
Barry Mienydd671972010-10-04 16:33:58 +0200408 $CI->load->library('profiler');
Derek Jonesee71c802010-03-10 10:05:05 -0600409 if ( ! empty($this->_profiler_sections))
410 {
411 $CI->profiler->set_sections($this->_profiler_sections);
Barry Mienydd671972010-10-04 16:33:58 +0200412 }
Derek Jonesee71c802010-03-10 10:05:05 -0600413
Derek Allard2067d1a2008-11-13 22:59:24 +0000414 // If the output data contains closing </body> and </html> tags
415 // we will remove them and add them back after we insert the profile data
Andrey Andreevcba20b12012-01-09 10:16:41 +0200416 $output = preg_replace('|</body>.*?</html>|is', '', $output, -1, $count).$CI->profiler->run();
417 if ($count > 0)
Derek Allard2067d1a2008-11-13 22:59:24 +0000418 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000419 $output .= '</body></html>';
420 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000421 }
Barry Mienydd671972010-10-04 16:33:58 +0200422
Derek Allard2067d1a2008-11-13 22:59:24 +0000423 // Does the controller contain a function named _output()?
Derek Jones37f4b9c2011-07-01 17:56:50 -0500424 // If so send the output there. Otherwise, echo it.
Derek Allard2067d1a2008-11-13 22:59:24 +0000425 if (method_exists($CI, '_output'))
426 {
427 $CI->_output($output);
428 }
429 else
430 {
Andrey Andreevedc87552012-01-09 09:35:10 +0200431 echo $output; // Send it to the browser!
Derek Allard2067d1a2008-11-13 22:59:24 +0000432 }
Barry Mienydd671972010-10-04 16:33:58 +0200433
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200434 log_message('debug', 'Final output sent to browser');
435 log_message('debug', 'Total execution time: '.$elapsed);
Derek Allard2067d1a2008-11-13 22:59:24 +0000436 }
Barry Mienydd671972010-10-04 16:33:58 +0200437
Derek Allard2067d1a2008-11-13 22:59:24 +0000438 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200439
Derek Allard2067d1a2008-11-13 22:59:24 +0000440 /**
441 * Write a Cache File
442 *
David Behler07b53422011-08-15 00:25:06 +0200443 * @param string
Derek Allard2067d1a2008-11-13 22:59:24 +0000444 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200445 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200446 public function _write_cache($output)
Derek Allard2067d1a2008-11-13 22:59:24 +0000447 {
Barry Mienydd671972010-10-04 16:33:58 +0200448 $CI =& get_instance();
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 $path = $CI->config->item('cache_path');
Greg Aker2eaa4072010-12-21 11:44:08 -0600450 $cache_path = ($path == '') ? APPPATH.'cache/' : $path;
Barry Mienydd671972010-10-04 16:33:58 +0200451
Derek Allard2067d1a2008-11-13 22:59:24 +0000452 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
453 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200454 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000455 return;
456 }
Barry Mienydd671972010-10-04 16:33:58 +0200457
Derek Allard2067d1a2008-11-13 22:59:24 +0000458 $uri = $CI->config->item('base_url').
459 $CI->config->item('index_page').
460 $CI->uri->uri_string();
Barry Mienydd671972010-10-04 16:33:58 +0200461
Derek Allard2067d1a2008-11-13 22:59:24 +0000462 $cache_path .= md5($uri);
463
464 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
465 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200466 log_message('error', 'Unable to write cache file: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000467 return;
468 }
Barry Mienydd671972010-10-04 16:33:58 +0200469
Derek Allard2067d1a2008-11-13 22:59:24 +0000470 $expire = time() + ($this->cache_expiration * 60);
Barry Mienydd671972010-10-04 16:33:58 +0200471
Derek Allard2067d1a2008-11-13 22:59:24 +0000472 if (flock($fp, LOCK_EX))
473 {
474 fwrite($fp, $expire.'TS--->'.$output);
475 flock($fp, LOCK_UN);
476 }
477 else
478 {
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200479 log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
Derek Allard2067d1a2008-11-13 22:59:24 +0000480 return;
481 }
482 fclose($fp);
Derek Jones172e1612009-10-13 14:32:48 +0000483 @chmod($cache_path, FILE_WRITE_MODE);
Derek Allard2067d1a2008-11-13 22:59:24 +0000484
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200485 log_message('debug', 'Cache file written: '.$cache_path);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800486
487 // Send HTTP cache-control headers to browser to match file cache settings.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800488 $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
Derek Allard2067d1a2008-11-13 22:59:24 +0000489 }
490
491 // --------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200492
Derek Allard2067d1a2008-11-13 22:59:24 +0000493 /**
494 * Update/serve a cached file
495 *
David Behler07b53422011-08-15 00:25:06 +0200496 * @param object config class
497 * @param object uri class
Derek Allard2067d1a2008-11-13 22:59:24 +0000498 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200499 */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200500 public function _display_cache(&$CFG, &$URI)
Derek Allard2067d1a2008-11-13 22:59:24 +0000501 {
Greg Aker2eaa4072010-12-21 11:44:08 -0600502 $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');
Barry Mienydd671972010-10-04 16:33:58 +0200503
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200504 // Build the file path. The file name is an MD5 hash of the full URI
505 $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string;
Derek Allard2067d1a2008-11-13 22:59:24 +0000506 $filepath = $cache_path.md5($uri);
Barry Mienydd671972010-10-04 16:33:58 +0200507
Andrey Andreevc90d6512012-01-08 04:35:02 +0200508 if ( ! @file_exists($filepath) OR ! $fp = @fopen($filepath, FOPEN_READ))
Derek Allard2067d1a2008-11-13 22:59:24 +0000509 {
510 return FALSE;
511 }
Barry Mienydd671972010-10-04 16:33:58 +0200512
Derek Allard2067d1a2008-11-13 22:59:24 +0000513 flock($fp, LOCK_SH);
Barry Mienydd671972010-10-04 16:33:58 +0200514
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200515 $cache = (filesize($filepath) > 0) ? fread($fp, filesize($filepath)) : '';
Barry Mienydd671972010-10-04 16:33:58 +0200516
Derek Allard2067d1a2008-11-13 22:59:24 +0000517 flock($fp, LOCK_UN);
518 fclose($fp);
Barry Mienydd671972010-10-04 16:33:58 +0200519
520 // Strip out the embedded timestamp
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200521 if ( ! preg_match('/(\d+TS--->)/', $cache, $match))
Derek Allard2067d1a2008-11-13 22:59:24 +0000522 {
523 return FALSE;
524 }
Barry Mienydd671972010-10-04 16:33:58 +0200525
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800526 $last_modified = filemtime($cache_path);
527 $expire = trim(str_replace('TS--->', '', $match[1]));
528
529 // Has the file expired?
530 if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path))
Derek Jonesd99e6032010-03-19 19:57:33 -0500531 {
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800532 // If so we'll delete it.
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200533 @unlink($filepath);
534 log_message('debug', 'Cache file has expired. File deleted.');
535 return FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000536 }
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800537 else
538 {
539 // Or else send the HTTP cache control headers.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800540 $this->set_cache_header($last_modified, $expire);
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800541 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000542
543 // Display the cache
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200544 $this->_display(str_replace($match[0], '', $cache));
545 log_message('debug', 'Cache file is current. Sending it to browser.');
Derek Allard2067d1a2008-11-13 22:59:24 +0000546 return TRUE;
547 }
548
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800549
550 // --------------------------------------------------------------------
551 /**
552 * Set the HTTP headers to match the server-side file cache settings
553 * in order to reduce bandwidth.
554 *
555 * @param int timestamp of when the page was last modified
556 * @param int timestamp of when should the requested page expire from cache
557 * @return void
558 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800559 public function set_cache_header($last_modified, $expiration)
Thor (atiredmachine)c8efb802012-01-24 13:33:39 -0800560 {
561 $max_age = $expiration - $_SERVER['REQUEST_TIME'];
562
563 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($last_modified <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])))
564 {
565 $this->set_status_header(304);
566 exit;
567 }
568 else
569 {
570 header('Pragma: public');
571 header('Cache-Control: max-age=' . $max_age . ', public');
572 header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
573 header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
574 }
575 }
576
577
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800578
579
580 // --------------------------------------------------------------------
581 /**
582 * Reduce excessive size of HTML content.
583 *
584 * @param string
585 * @param string
586 * @return string
587 */
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800588 public function minify($output, $type='text/html')
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800589 {
590 switch ($type)
591 {
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800592 case 'text/html':
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800593
594 $size_before = strlen($output);
595
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800596 // Find all the <pre>,<code>,<textarea>, and <javascript> tags
597 // We'll want to return them to this unprocessed state later.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800598 preg_match_all('{<pre.+</pre>}msU', $output, $pres_clean);
599 preg_match_all('{<code.+</code>}msU', $output, $codes_clean);
600 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_clean);
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800601 preg_match_all('{<script.+</script>}msU', $output, $javascript_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800602
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800603 // Minify the CSS in all the <style> tags.
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800604 preg_match_all('{<style.+</style>}msU', $output, $style_clean);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800605 foreach ($style_clean[0] as $s)
606 {
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800607 $output = str_replace($s, $this->minify($s,'text/css'), $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800608 }
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800609
610 // Minify the javascript in <script> tags.
611 foreach ($javascript_clean[0] as $s)
612 {
613 $javascript_mini[] = $this->minify($s,'text/javascript');
614 }
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800615
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800616 // Replace multiple spaces with a single space.
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800617 $output = preg_replace('!\s{2,}!',' ', $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800618
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800619 // Remove comments (non-MSIE conditionals)
Thor (atiredmachine)5528c0e2012-01-25 01:02:28 -0800620 $output = preg_replace('{\s*<!--[^\[].*-->\s*}msU', '', $output);
Thor (atiredmachine)5de11752012-01-24 22:08:36 -0800621
Thor (atiredmachine)a2ae6e12012-01-24 20:57:48 -0800622 // Remove spaces around block-level elements.
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800623 $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 -0800624
Thor (atiredmachine)d68c8192012-01-24 20:56:21 -0800625 // Replace mangled <pre> etc. tags with unprocessed ones.
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800626
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800627 if ( ! empty($pres_clean))
628 {
629 preg_match_all('{<pre.+</pre>}msU', $output, $pres_messed);
630 $output = str_replace($pres_messed[0], $pres_clean[0], $output);
631 }
Thor (atiredmachine)f59ec6f2012-01-24 22:19:14 -0800632
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800633 if ( ! empty($codes_clean))
634 {
635 preg_match_all('{<code.+</code>}msU', $output, $codes_messed);
636 $output = str_replace($codes_messed[0], $codes_clean[0], $output);
637 }
638
639 if ( ! empty($codes_clean))
640 {
641 preg_match_all('{<textarea.+</textarea>}msU', $output, $textareas_messed);
642 $output = str_replace($textareas_messed[0], $textareas_clean[0], $output);
643 }
644
645 if (isset($javascript_mini))
646 {
647 preg_match_all('{<script.+</script>}msU', $output, $javascript_messed);
648 $output = str_replace($javascript_messed[0], $javascript_mini, $output);
649 }
650
651 $size_removed = $size_before - strlen($output);
652 $savings_percent = round(($size_removed / $size_before * 100));
653
654 log_message('debug', 'Minifier shaved '.($size_removed / 1000).'KB ('.$savings_percent.'%) off final HTML output.');
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800655
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800656 break;
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800657
658
Thor (atiredmachine)1d0661a2012-01-24 21:39:16 -0800659 case 'text/css':
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800660
Thor (atiredmachine)5c078ce2012-01-26 17:18:35 -0800661 //Remove CSS comments
662 $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output);
663
664 // Remove spaces around curly brackets, colons,
665 // semi-colons, parenthesis, commas
666 $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output);
667
668 break;
669
670
671 case 'text/javascript':
672
673 // Replace multiple spaces with a single newline.
674 $output = preg_replace('!\s{2,}!',"\n", $output);
675
676 // Remove excessive newlines.
677 $output = preg_replace('!(;|{|})\n!','$1', $output);
Thor (atiredmachine)79db4cd2012-01-24 20:44:51 -0800678
679 break;
Thor (atiredmachine)63678a22012-01-24 16:56:01 -0800680 }
681
682 return $output;
683 }
684
685
Derek Allard2067d1a2008-11-13 22:59:24 +0000686}
Derek Allard2067d1a2008-11-13 22:59:24 +0000687
688/* End of file Output.php */
Andrey Andreev1f5fbb62012-01-07 20:53:29 +0200689/* Location: ./system/core/Output.php */