source: sourcecode/system/core/Output.php @ 1

Last change on this file since 1 was 1, checked in by dungnv, 11 years ago
File size: 12.6 KB
Line 
1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package             CodeIgniter
8 * @author              ExpressionEngine Dev Team
9 * @copyright   Copyright (c) 2008 - 2011, EllisLab, Inc.
10 * @license             http://codeigniter.com/user_guide/license.html
11 * @link                http://codeigniter.com
12 * @since               Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Output Class
20 *
21 * Responsible for sending final output to browser
22 *
23 * @package             CodeIgniter
24 * @subpackage  Libraries
25 * @category    Output
26 * @author              ExpressionEngine Dev Team
27 * @link                http://codeigniter.com/user_guide/libraries/output.html
28 */
29class CI_Output {
30
31        /**
32         * Current output string
33         *
34         * @var string
35         * @access      protected
36         */
37        protected $final_output;
38        /**
39         * Cache expiration time
40         *
41         * @var int
42         * @access      protected
43         */
44        protected $cache_expiration     = 0;
45        /**
46         * List of server headers
47         *
48         * @var array
49         * @access      protected
50         */
51        protected $headers                      = array();
52        /**
53         * List of mime types
54         *
55         * @var array
56         * @access      protected
57         */
58        protected $mime_types           = array();
59        /**
60         * Determines wether profiler is enabled
61         *
62         * @var book
63         * @access      protected
64         */
65        protected $enable_profiler      = FALSE;
66        /**
67         * Determines if output compression is enabled
68         *
69         * @var bool
70         * @access      protected
71         */
72        protected $_zlib_oc                     = FALSE;
73        /**
74         * List of profiler sections
75         *
76         * @var array
77         * @access      protected
78         */
79        protected $_profiler_sections = array();
80        /**
81         * Whether or not to parse variables like {elapsed_time} and {memory_usage}
82         *
83         * @var bool
84         * @access      protected
85         */
86        protected $parse_exec_vars      = TRUE;
87
88        /**
89         * Constructor
90         *
91         */
92        function __construct()
93        {
94                $this->_zlib_oc = @ini_get('zlib.output_compression');
95
96                // Get mime types for later
97                if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
98                {
99                    include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
100                }
101                else
102                {
103                        include APPPATH.'config/mimes.php';
104                }
105
106
107                $this->mime_types = $mimes;
108
109                log_message('debug', "Output Class Initialized");
110        }
111
112        // --------------------------------------------------------------------
113
114        /**
115         * Get Output
116         *
117         * Returns the current output string
118         *
119         * @access      public
120         * @return      string
121         */
122        function get_output()
123        {
124                return $this->final_output;
125        }
126
127        // --------------------------------------------------------------------
128
129        /**
130         * Set Output
131         *
132         * Sets the output string
133         *
134         * @access      public
135         * @param       string
136         * @return      void
137         */
138        function set_output($output)
139        {
140                $this->final_output = $output;
141
142                return $this;
143        }
144
145        // --------------------------------------------------------------------
146
147        /**
148         * Append Output
149         *
150         * Appends data onto the output string
151         *
152         * @access      public
153         * @param       string
154         * @return      void
155         */
156        function append_output($output)
157        {
158                if ($this->final_output == '')
159                {
160                        $this->final_output = $output;
161                }
162                else
163                {
164                        $this->final_output .= $output;
165                }
166
167                return $this;
168        }
169
170        // --------------------------------------------------------------------
171
172        /**
173         * Set Header
174         *
175         * Lets you set a server header which will be outputted with the final display.
176         *
177         * Note:  If a file is cached, headers will not be sent.  We need to figure out
178         * how to permit header data to be saved with the cache data...
179         *
180         * @access      public
181         * @param       string
182         * @param       bool
183         * @return      void
184         */
185        function set_header($header, $replace = TRUE)
186        {
187                // 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.
191
192                if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
193                {
194                        return;
195                }
196
197                $this->headers[] = array($header, $replace);
198
199                return $this;
200        }
201
202        // --------------------------------------------------------------------
203
204        /**
205         * Set Content Type Header
206         *
207         * @access      public
208         * @param       string  extension of the file we're outputting
209         * @return      void
210         */
211        function set_content_type($mime_type)
212        {
213                if (strpos($mime_type, '/') === FALSE)
214                {
215                        $extension = ltrim($mime_type, '.');
216
217                        // Is this extension supported?
218                        if (isset($this->mime_types[$extension]))
219                        {
220                                $mime_type =& $this->mime_types[$extension];
221
222                                if (is_array($mime_type))
223                                {
224                                        $mime_type = current($mime_type);
225                                }
226                        }
227                }
228
229                $header = 'Content-Type: '.$mime_type;
230
231                $this->headers[] = array($header, TRUE);
232
233                return $this;
234        }
235
236        // --------------------------------------------------------------------
237
238        /**
239         * Set HTTP Status Header
240         * moved to Common procedural functions in 1.7.2
241         *
242         * @access      public
243         * @param       int             the status code
244         * @param       string
245         * @return      void
246         */
247        function set_status_header($code = 200, $text = '')
248        {
249                set_status_header($code, $text);
250
251                return $this;
252        }
253
254        // --------------------------------------------------------------------
255
256        /**
257         * Enable/disable Profiler
258         *
259         * @access      public
260         * @param       bool
261         * @return      void
262         */
263        function enable_profiler($val = TRUE)
264        {
265                $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
266
267                return $this;
268        }
269
270        // --------------------------------------------------------------------
271
272        /**
273         * Set Profiler Sections
274         *
275         * Allows override of default / config settings for Profiler section display
276         *
277         * @access      public
278         * @param       array
279         * @return      void
280         */
281        function set_profiler_sections($sections)
282        {
283                foreach ($sections as $section => $enable)
284                {
285                        $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE;
286                }
287
288                return $this;
289        }
290
291        // --------------------------------------------------------------------
292
293        /**
294         * Set Cache
295         *
296         * @access      public
297         * @param       integer
298         * @return      void
299         */
300        function cache($time)
301        {
302                $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
303
304                return $this;
305        }
306
307        // --------------------------------------------------------------------
308
309        /**
310         * Display Output
311         *
312         * All "view" data is automatically put into this variable by the controller class:
313         *
314         * $this->final_output
315         *
316         * This function sends the finalized output data to the browser along
317         * with any server headers and profile data.  It also stops the
318         * benchmark timer so the page rendering speed and memory usage can be shown.
319         *
320         * @access      public
321         * @param       string
322         * @return      mixed
323         */
324        function _display($output = '')
325        {
326                // Note:  We use globals because we can't use $CI =& get_instance()
327                // since this function is sometimes called by the caching mechanism,
328                // which happens before the CI super object is available.
329                global $BM, $CFG;
330
331                // Grab the super object if we can.
332                if (class_exists('CI_Controller'))
333                {
334                        $CI =& get_instance();
335                }
336
337                // --------------------------------------------------------------------
338
339                // Set the output data
340                if ($output == '')
341                {
342                        $output =& $this->final_output;
343                }
344
345                // --------------------------------------------------------------------
346
347                // Do we need to write a cache file?  Only if the controller does not have its
348                // own _output() method and we are not dealing with a cache file, which we
349                // can determine by the existence of the $CI object above
350                if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
351                {
352                        $this->_write_cache($output);
353                }
354
355                // --------------------------------------------------------------------
356
357                // Parse out the elapsed time and memory usage,
358                // then swap the pseudo-variables with the data
359
360                $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
361
362                if ($this->parse_exec_vars === TRUE)
363                {
364                        $memory  = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
365
366                        $output = str_replace('{elapsed_time}', $elapsed, $output);
367                        $output = str_replace('{memory_usage}', $memory, $output);
368                }
369
370                // --------------------------------------------------------------------
371
372                // Is compression requested?
373                if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
374                {
375                        if (extension_loaded('zlib'))
376                        {
377                                if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
378                                {
379                                        ob_start('ob_gzhandler');
380                                }
381                        }
382                }
383
384                // --------------------------------------------------------------------
385
386                // Are there any server headers to send?
387                if (count($this->headers) > 0)
388                {
389                        foreach ($this->headers as $header)
390                        {
391                                @header($header[0], $header[1]);
392                        }
393                }
394
395                // --------------------------------------------------------------------
396
397                // Does the $CI object exist?
398                // If not we know we are dealing with a cache file so we'll
399                // simply echo out the data and exit.
400                if ( ! isset($CI))
401                {
402                        echo $output;
403                        log_message('debug', "Final output sent to browser");
404                        log_message('debug', "Total execution time: ".$elapsed);
405                        return TRUE;
406                }
407
408                // --------------------------------------------------------------------
409
410                // Do we need to generate profile data?
411                // If so, load the Profile class and run it.
412                if ($this->enable_profiler == TRUE)
413                {
414                        $CI->load->library('profiler');
415
416                        if ( ! empty($this->_profiler_sections))
417                        {
418                                $CI->profiler->set_sections($this->_profiler_sections);
419                        }
420
421                        // If the output data contains closing </body> and </html> tags
422                        // we will remove them and add them back after we insert the profile data
423                        if (preg_match("|</body>.*?</html>|is", $output))
424                        {
425                                $output  = preg_replace("|</body>.*?</html>|is", '', $output);
426                                $output .= $CI->profiler->run();
427                                $output .= '</body></html>';
428                        }
429                        else
430                        {
431                                $output .= $CI->profiler->run();
432                        }
433                }
434
435                // --------------------------------------------------------------------
436
437                // Does the controller contain a function named _output()?
438                // If so send the output there.  Otherwise, echo it.
439                if (method_exists($CI, '_output'))
440                {
441                        $CI->_output($output);
442                }
443                else
444                {
445                        echo $output;  // Send it to the browser!
446                }
447
448                log_message('debug', "Final output sent to browser");
449                log_message('debug', "Total execution time: ".$elapsed);
450        }
451
452        // --------------------------------------------------------------------
453
454        /**
455         * Write a Cache File
456         *
457         * @access      public
458         * @param       string
459         * @return      void
460         */
461        function _write_cache($output)
462        {
463                $CI =& get_instance();
464                $path = $CI->config->item('cache_path');
465
466                $cache_path = ($path == '') ? APPPATH.'cache/' : $path;
467
468                if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
469                {
470                        log_message('error', "Unable to write cache file: ".$cache_path);
471                        return;
472                }
473
474                $uri =  $CI->config->item('base_url').
475                                $CI->config->item('index_page').
476                                $CI->uri->uri_string();
477
478                $cache_path .= md5($uri);
479
480                if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
481                {
482                        log_message('error', "Unable to write cache file: ".$cache_path);
483                        return;
484                }
485
486                $expire = time() + ($this->cache_expiration * 60);
487
488                if (flock($fp, LOCK_EX))
489                {
490                        fwrite($fp, $expire.'TS--->'.$output);
491                        flock($fp, LOCK_UN);
492                }
493                else
494                {
495                        log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
496                        return;
497                }
498                fclose($fp);
499                @chmod($cache_path, FILE_WRITE_MODE);
500
501                log_message('debug', "Cache file written: ".$cache_path);
502        }
503
504        // --------------------------------------------------------------------
505
506        /**
507         * Update/serve a cached file
508         *
509         * @access      public
510         * @param       object  config class
511         * @param       object  uri class
512         * @return      void
513         */
514        function _display_cache(&$CFG, &$URI)
515        {
516                $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');
517
518                // Build the file path.  The file name is an MD5 hash of the full URI
519                $uri =  $CFG->item('base_url').
520                                $CFG->item('index_page').
521                                $URI->uri_string;
522
523                $filepath = $cache_path.md5($uri);
524
525                if ( ! @file_exists($filepath))
526                {
527                        return FALSE;
528                }
529
530                if ( ! $fp = @fopen($filepath, FOPEN_READ))
531                {
532                        return FALSE;
533                }
534
535                flock($fp, LOCK_SH);
536
537                $cache = '';
538                if (filesize($filepath) > 0)
539                {
540                        $cache = fread($fp, filesize($filepath));
541                }
542
543                flock($fp, LOCK_UN);
544                fclose($fp);
545
546                // Strip out the embedded timestamp
547                if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
548                {
549                        return FALSE;
550                }
551
552                // Has the file expired? If so we'll delete it.
553                if (time() >= trim(str_replace('TS--->', '', $match['1'])))
554                {
555                        if (is_really_writable($cache_path))
556                        {
557                                @unlink($filepath);
558                                log_message('debug', "Cache file has expired. File deleted");
559                                return FALSE;
560                        }
561                }
562
563                // Display the cache
564                $this->_display(str_replace($match['0'], '', $cache));
565                log_message('debug', "Cache file is current. Sending it to browser.");
566                return TRUE;
567        }
568
569
570}
571// END Output Class
572
573/* End of file Output.php */
574/* Location: ./system/core/Output.php */
Note: See TracBrowser for help on using the repository browser.