Output.php

Go to the documentation of this file.
00001 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
00002 /**
00003  * CodeIgniter
00004  *
00005  * An open source application development framework for PHP 4.3.2 or newer
00006  *
00007  * @package             CodeIgniter
00008  * @author              ExpressionEngine Dev Team
00009  * @copyright   Copyright (c) 2008, EllisLab, Inc.
00010  * @license             http://codeigniter.com/user_guide/license.html
00011  * @link                http://codeigniter.com
00012  * @since               Version 1.0
00013  * @filesource
00014  */
00015 
00016 // ------------------------------------------------------------------------
00017 
00018 /**
00019  * Output Class
00020  *
00021  * Responsible for sending final output to browser
00022  *
00023  * @package             CodeIgniter
00024  * @subpackage  Libraries
00025  * @category    Output
00026  * @author              ExpressionEngine Dev Team
00027  * @link                http://codeigniter.com/user_guide/libraries/output.html
00028  */
00029 class CI_Output {
00030 
00031         var $final_output;
00032         var $cache_expiration   = 0;
00033         var $headers                    = array();
00034         var $enable_profiler    = FALSE;
00035 
00036 
00037         function CI_Output()
00038         {
00039                 log_message('debug', "Output Class Initialized");
00040         }
00041         
00042         // --------------------------------------------------------------------
00043         
00044         /**
00045          * Get Output
00046          *
00047          * Returns the current output string
00048          *
00049          * @access      public
00050          * @return      string
00051          */     
00052         function get_output()
00053         {
00054                 return $this->final_output;
00055         }
00056         
00057         // --------------------------------------------------------------------
00058         
00059         /**
00060          * Set Output
00061          *
00062          * Sets the output string
00063          *
00064          * @access      public
00065          * @param       string
00066          * @return      void
00067          */     
00068         function set_output($output)
00069         {
00070                 $this->final_output = $output;
00071         }
00072 
00073         // --------------------------------------------------------------------
00074 
00075         /**
00076          * Append Output
00077          *
00078          * Appends data onto the output string
00079          *
00080          * @access      public
00081          * @param       string
00082          * @return      void
00083          */     
00084         function append_output($output)
00085         {
00086                 if ($this->final_output == '')
00087                 {
00088                         $this->final_output = $output;
00089                 }
00090                 else
00091                 {
00092                         $this->final_output .= $output;
00093                 }
00094         }
00095 
00096         // --------------------------------------------------------------------
00097 
00098         /**
00099          * Set Header
00100          *
00101          * Lets you set a server header which will be outputted with the final display.
00102          *
00103          * Note:  If a file is cached, headers will not be sent.  We need to figure out
00104          * how to permit header data to be saved with the cache data...
00105          *
00106          * @access      public
00107          * @param       string
00108          * @return      void
00109          */     
00110         function set_header($header, $replace = TRUE)
00111         {
00112                 $this->headers[] = array($header, $replace);
00113         }
00114 
00115         // --------------------------------------------------------------------
00116         
00117         /**
00118          * Set HTTP Status Header
00119          *
00120          * @access      public
00121          * @param       int     the status code
00122          * @param       string  
00123          * @return      void
00124          */     
00125         function set_status_header($code = '200', $text = '')
00126         {
00127                 $stati = array(
00128                                                         '200'   => 'OK',
00129                                                         '201'   => 'Created',
00130                                                         '202'   => 'Accepted',
00131                                                         '203'   => 'Non-Authoritative Information',
00132                                                         '204'   => 'No Content',
00133                                                         '205'   => 'Reset Content',
00134                                                         '206'   => 'Partial Content',
00135                                                         
00136                                                         '300'   => 'Multiple Choices',
00137                                                         '301'   => 'Moved Permanently',
00138                                                         '302'   => 'Found',
00139                                                         '304'   => 'Not Modified',
00140                                                         '305'   => 'Use Proxy',
00141                                                         '307'   => 'Temporary Redirect',
00142                                                         
00143                                                         '400'   => 'Bad Request',
00144                                                         '401'   => 'Unauthorized',
00145                                                         '403'   => 'Forbidden',
00146                                                         '404'   => 'Not Found',
00147                                                         '405'   => 'Method Not Allowed',
00148                                                         '406'   => 'Not Acceptable',
00149                                                         '407'   => 'Proxy Authentication Required',
00150                                                         '408'   => 'Request Timeout',
00151                                                         '409'   => 'Conflict',
00152                                                         '410'   => 'Gone',
00153                                                         '411'   => 'Length Required',
00154                                                         '412'   => 'Precondition Failed',
00155                                                         '413'   => 'Request Entity Too Large',
00156                                                         '414'   => 'Request-URI Too Long',
00157                                                         '415'   => 'Unsupported Media Type',
00158                                                         '416'   => 'Requested Range Not Satisfiable',
00159                                                         '417'   => 'Expectation Failed',
00160                 
00161                                                         '500'   => 'Internal Server Error',
00162                                                         '501'   => 'Not Implemented',
00163                                                         '502'   => 'Bad Gateway',
00164                                                         '503'   => 'Service Unavailable',
00165                                                         '504'   => 'Gateway Timeout',
00166                                                         '505'   => 'HTTP Version Not Supported'
00167                                                 );
00168 
00169                 if ($code == '' OR ! is_numeric($code))
00170                 {
00171                         show_error('Status codes must be numeric');
00172                 }
00173 
00174                 if (isset($stati[$code]) AND $text == '')
00175                 {                               
00176                         $text = $stati[$code];
00177                 }
00178                 
00179                 if ($text == '')
00180                 {
00181                         show_error('No status text available.  Please check your status code number or supply your own message text.');
00182                 }
00183                 
00184                 $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
00185         
00186                 if (substr(php_sapi_name(), 0, 3) == 'cgi')
00187                 {
00188                         header("Status: {$code} {$text}", TRUE);
00189                 }
00190                 elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0')
00191                 {
00192                         header($server_protocol." {$code} {$text}", TRUE, $code);
00193                 }
00194                 else
00195                 {
00196                         header("HTTP/1.1 {$code} {$text}", TRUE, $code);
00197                 }
00198         }
00199         
00200         // --------------------------------------------------------------------
00201         
00202         /**
00203          * Enable/disable Profiler
00204          *
00205          * @access      public
00206          * @param       bool
00207          * @return      void
00208          */     
00209         function enable_profiler($val = TRUE)
00210         {
00211                 $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
00212         }
00213         
00214         // --------------------------------------------------------------------
00215         
00216         /**
00217          * Set Cache
00218          *
00219          * @access      public
00220          * @param       integer
00221          * @return      void
00222          */     
00223         function cache($time)
00224         {
00225                 $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
00226         }
00227         
00228         // --------------------------------------------------------------------
00229         
00230         /**
00231          * Display Output
00232          *
00233          * All "view" data is automatically put into this variable by the controller class:
00234          *
00235          * $this->final_output
00236          *
00237          * This function sends the finalized output data to the browser along
00238          * with any server headers and profile data.  It also stops the
00239          * benchmark timer so the page rendering speed and memory usage can be shown.
00240          *
00241          * @access      public
00242          * @return      mixed
00243          */             
00244         function _display($output = '')
00245         {       
00246                 // Note:  We use globals because we can't use $CI =& get_instance()
00247                 // since this function is sometimes called by the caching mechanism,
00248                 // which happens before the CI super object is available.
00249                 global $BM, $CFG;
00250                 
00251                 // --------------------------------------------------------------------
00252                 
00253                 // Set the output data
00254                 if ($output == '')
00255                 {
00256                         $output =& $this->final_output;
00257                 }
00258                 
00259                 // --------------------------------------------------------------------
00260                 
00261                 // Do we need to write a cache file?
00262                 if ($this->cache_expiration > 0)
00263                 {
00264                         $this->_write_cache($output);
00265                 }
00266                 
00267                 // --------------------------------------------------------------------
00268 
00269                 // Parse out the elapsed time and memory usage,
00270                 // then swap the pseudo-variables with the data
00271 
00272                 $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');         
00273                 $output = str_replace('{elapsed_time}', $elapsed, $output);
00274                 
00275                 $memory  = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
00276                 $output = str_replace('{memory_usage}', $memory, $output);              
00277 
00278                 // --------------------------------------------------------------------
00279                 
00280                 // Is compression requested?
00281                 if ($CFG->item('compress_output') === TRUE)
00282                 {
00283                         if (extension_loaded('zlib'))
00284                         {
00285                                 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
00286                                 {
00287                                         ob_start('ob_gzhandler');
00288                                 }
00289                         }
00290                 }
00291 
00292                 // --------------------------------------------------------------------
00293                 
00294                 // Are there any server headers to send?
00295                 if (count($this->headers) > 0)
00296                 {
00297                         foreach ($this->headers as $header)
00298                         {
00299                                 @header($header[0], $header[1]);
00300                         }
00301                 }               
00302 
00303                 // --------------------------------------------------------------------
00304                 
00305                 // Does the get_instance() function exist?
00306                 // If not we know we are dealing with a cache file so we'll
00307                 // simply echo out the data and exit.
00308                 if ( ! function_exists('get_instance'))
00309                 {
00310                         echo $output;
00311                         log_message('debug', "Final output sent to browser");
00312                         log_message('debug', "Total execution time: ".$elapsed);
00313                         return TRUE;
00314                 }
00315         
00316                 // --------------------------------------------------------------------
00317 
00318                 // Grab the super object.  We'll need it in a moment...
00319                 $CI =& get_instance();
00320                 
00321                 // Do we need to generate profile data?
00322                 // If so, load the Profile class and run it.
00323                 if ($this->enable_profiler == TRUE)
00324                 {
00325                         $CI->load->library('profiler');                         
00326                                                                                 
00327                         // If the output data contains closing </body> and </html> tags
00328                         // we will remove them and add them back after we insert the profile data
00329                         if (preg_match("|</body>.*?</html>|is", $output))
00330                         {
00331                                 $output  = preg_replace("|</body>.*?</html>|is", '', $output);
00332                                 $output .= $CI->profiler->run();
00333                                 $output .= '</body></html>';
00334                         }
00335                         else
00336                         {
00337                                 $output .= $CI->profiler->run();
00338                         }
00339                 }
00340                 
00341                 // --------------------------------------------------------------------
00342 
00343                 // Does the controller contain a function named _output()?
00344                 // If so send the output there.  Otherwise, echo it.
00345                 if (method_exists($CI, '_output'))
00346                 {
00347                         $CI->_output($output);
00348                 }
00349                 else
00350                 {
00351                         echo $output;  // Send it to the browser!
00352                 }
00353                 
00354                 log_message('debug', "Final output sent to browser");
00355                 log_message('debug', "Total execution time: ".$elapsed);                
00356         }
00357         
00358         // --------------------------------------------------------------------
00359         
00360         /**
00361          * Write a Cache File
00362          *
00363          * @access      public
00364          * @return      void
00365          */     
00366         function _write_cache($output)
00367         {
00368                 $CI =& get_instance();  
00369                 $path = $CI->config->item('cache_path');
00370         
00371                 $cache_path = ($path == '') ? BASEPATH.'cache/' : $path;
00372                 
00373                 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
00374                 {
00375                         return;
00376                 }
00377                 
00378                 $uri =  $CI->config->item('base_url').
00379                                 $CI->config->item('index_page').
00380                                 $CI->uri->uri_string();
00381                 
00382                 $cache_path .= md5($uri);
00383 
00384                 if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
00385                 {
00386                         log_message('error', "Unable to write cache file: ".$cache_path);
00387                         return;
00388                 }
00389                 
00390                 $expire = time() + ($this->cache_expiration * 60);
00391                 
00392                 if (flock($fp, LOCK_EX))
00393                 {
00394                         fwrite($fp, $expire.'TS--->'.$output);
00395                         flock($fp, LOCK_UN);
00396                 }
00397                 else
00398                 {
00399                         log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
00400                         return;
00401                 }
00402                 fclose($fp);
00403                 @chmod($cache_path, DIR_WRITE_MODE);
00404 
00405                 log_message('debug', "Cache file written: ".$cache_path);
00406         }
00407 
00408         // --------------------------------------------------------------------
00409         
00410         /**
00411          * Update/serve a cached file
00412          *
00413          * @access      public
00414          * @return      void
00415          */     
00416         function _display_cache(&$CFG, &$URI)
00417         {
00418                 $cache_path = ($CFG->item('cache_path') == '') ? BASEPATH.'cache/' : $CFG->item('cache_path');
00419                         
00420                 if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
00421                 {
00422                         return FALSE;
00423                 }
00424                 
00425                 // Build the file path.  The file name is an MD5 hash of the full URI
00426                 $uri =  $CFG->item('base_url').
00427                                 $CFG->item('index_page').
00428                                 $URI->uri_string;
00429                                 
00430                 $filepath = $cache_path.md5($uri);
00431                 
00432                 if ( ! @file_exists($filepath))
00433                 {
00434                         return FALSE;
00435                 }
00436         
00437                 if ( ! $fp = @fopen($filepath, FOPEN_READ))
00438                 {
00439                         return FALSE;
00440                 }
00441                         
00442                 flock($fp, LOCK_SH);
00443                 
00444                 $cache = '';
00445                 if (filesize($filepath) > 0)
00446                 {
00447                         $cache = fread($fp, filesize($filepath));
00448                 }
00449         
00450                 flock($fp, LOCK_UN);
00451                 fclose($fp);
00452                                         
00453                 // Strip out the embedded timestamp             
00454                 if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
00455                 {
00456                         return FALSE;
00457                 }
00458                 
00459                 // Has the file expired? If so we'll delete it.
00460                 if (time() >= trim(str_replace('TS--->', '', $match['1'])))
00461                 {               
00462                         @unlink($filepath);
00463                         log_message('debug', "Cache file has expired. File deleted");
00464                         return FALSE;
00465                 }
00466 
00467                 // Display the cache
00468                 $this->_display(str_replace($match['0'], '', $cache));
00469                 log_message('debug', "Cache file is current. Sending it to browser.");          
00470                 return TRUE;
00471         }
00472 
00473 
00474 }
00475 // END Output Class
00476 
00477 /* End of file Output.php */
00478 /* Location: ./system/libraries/Output.php */