zoukankan      html  css  js  c++  java
  • 十)CodeIgniter源码分析之Output.php

      1 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
      2 
      3 // ------------------------------------------------------------------------
      4 
      5 /**
      6  * Output Class
      7  * 
      8  * Output组件其实有很多有用的方法,不过一般情况下,你不会直接去用到它们。
      9  * 这里主要以Output::_display_cache()和Output::_display()为两条主线来探究。
     10  */
     11 class CI_Output {
     12 
     13  /**
     14   * Current output string
     15   */
     16  protected $final_output;
     17 
     18 
     19  /**
     20   * Cache expiration time
     21   */
     22  protected $cache_expiration = 0;
     23 
     24 
     25  /**
     26   * List of server headers
     27   */
     28  protected $headers   = array();
     29 
     30 
     31  /**
     32   * List of mime types
     33   */
     34  protected $mime_types  = array();
     35 
     36 
     37  /**
     38   * Determines wether profiler is enabled
     39   */
     40  protected $enable_profiler = FALSE;
     41 
     42 
     43  /**
     44   * Determines if output compression is enabled
     45   * @access  protected
     46   */
     47  protected $_zlib_oc   = FALSE;
     48 
     49 
     50  /**
     51   * List of profiler sections
     52   */
     53  protected $_profiler_sections = array();
     54 
     55 
     56  /**
     57   * Whether or not to parse variables like {elapsed_time} and {memory_usage}
     58   */
     59  protected $parse_exec_vars = TRUE;
     60 
     61  /**
     62   * Constructor
     63   */
     64  function __construct()
     65  {
     66   $this->_zlib_oc = @ini_get('zlib.output_compression');
     67 
     68   if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php'))
     69   {
     70       include APPPATH.'config/'.ENVIRONMENT.'/mimes.php';
     71   }
     72   else
     73   {
     74    include APPPATH.'config/mimes.php';
     75   }
     76 
     77 
     78   $this->mime_types = $mimes;
     79 
     80   log_message('debug', "Output Class Initialized");
     81  }
     82 
     83  // --------------------------------------------------------------------
     84 
     85  /**
     86   * Get Output
     87   */
     88  function get_output()
     89  {
     90   //返回最终输出。
     91   return $this->final_output;
     92  }
     93 
     94  // --------------------------------------------------------------------
     95 
     96  /**
     97   * Set Output
     98   */
     99  function set_output($output)
    100  {
    101   //设置最终输出。
    102   $this->final_output = $output;
    103 
    104   return $this;
    105  }
    106 
    107  // --------------------------------------------------------------------
    108 
    109  /**
    110   * Append Output
    111   */
    112  //通过此方法给Output::$final_output加上输出内容,最终这些内容会在Output::_display()中被输出。
    113  //此方法在Loader.php中被调用。详见Loader.php中的Loader::view()方法及Loader::_ci_load()方法。
    114  function append_output($output)
    115  {
    116   if ($this->final_output == '')
    117   {
    118    $this->final_output = $output;
    119   }
    120   else
    121   {
    122    $this->final_output .= $output;
    123   }
    124 
    125   return $this;
    126  }
    127 
    128  // --------------------------------------------------------------------
    129 
    130  /**
    131   * Set Header
    132   */
    133  /**
    134   * 接下来的三个方法都是设置页面内容输出前的头信息。
    135   */
    136  function set_header($header, $replace = TRUE)
    137  {
    138   if ($this->_zlib_oc && strncasecmp($header, 'content-length', 14) == 0)
    139   {
    140    return;
    141   }
    142 
    143   $this->headers[] = array($header, $replace);
    144 
    145   return $this;
    146  }
    147 
    148  // --------------------------------------------------------------------
    149 
    150  /**
    151   * Set Content Type Header
    152   */
    153  function set_content_type($mime_type)
    154  {
    155   if (strpos($mime_type, '/') === FALSE)
    156   {
    157    $extension = ltrim($mime_type, '.');
    158 
    159    if (isset($this->mime_types[$extension]))
    160    {
    161     $mime_type =& $this->mime_types[$extension];
    162 
    163     if (is_array($mime_type))
    164     {
    165      $mime_type = current($mime_type);
    166     }
    167    }
    168   }
    169 
    170   $header = 'Content-Type: '.$mime_type;
    171 
    172   $this->headers[] = array($header, TRUE);
    173 
    174   return $this;
    175  }
    176 
    177  // --------------------------------------------------------------------
    178 
    179  /**
    180   * Set HTTP Status Header
    181   */
    182  function set_status_header($code = 200, $text = '')
    183  {
    184   set_status_header($code, $text);
    185 
    186   return $this;
    187  }
    188 
    189  // --------------------------------------------------------------------
    190 
    191  /**
    192   * Enable/disable Profiler
    193   */
    194  //是否开启评测器。
    195  function enable_profiler($val = TRUE)
    196  {
    197   $this->enable_profiler = (is_bool($val)) ? $val : TRUE;
    198 
    199   return $this;
    200  }
    201 
    202  // --------------------------------------------------------------------
    203 
    204  /**
    205   * Set Profiler Sections
    206   */
    207  function set_profiler_sections($sections)
    208  {
    209   foreach ($sections as $section => $enable)
    210   {
    211    $this->_profiler_sections[$section] = ($enable !== FALSE) ? TRUE : FALSE;
    212   }
    213 
    214   return $this;
    215  }
    216 
    217  // --------------------------------------------------------------------
    218 
    219  /**
    220   * Set Cache
    221   */
    222  function cache($time)
    223  {
    224   //设置缓存时长。
    225   $this->cache_expiration = ( ! is_numeric($time)) ? 0 : $time;
    226 
    227   return $this;
    228  }
    229 
    230  // --------------------------------------------------------------------
    231 
    232  /**
    233   * Display Output
    234   */
    235  function _display($output = '')
    236  {
    237   //为什么这里要用global来调用这两个组件呢?不是可以通过$CI这个超级控制器来调用?
    238   //其实就是因为这个_display方法,被调用的方式有两种,其中有可能是在CodeIgniter.php中调用
    239   //Output::_display_cache();的时候间接被调用了,而此时& get_instance()这个方法压根还没
    240   //被定义。详见CodeIgniter.php中代码定义和调用的顺序。
    241   global $BM, $CFG;
    242 
    243   //当然如果可以拿到超级控制器,我们先拿过来。
    244   if (class_exists('CI_Controller'))
    245   {
    246    $CI =& get_instance();
    247   }
    248 
    249   // --------------------------------------------------------------------
    250 
    251   //如果$output为空,其实往往这是非缓存方式调用的时候。我们将使用Output::final_output。(如果是正常流程的输出
    252   //方式,而不是缓存的话,这个属性其实在Loader::view()的时候调用Output::append_output()获得输出内容。)
    253   if ($output == '')
    254   {
    255    $output =& $this->final_output;
    256   }
    257 
    258   // --------------------------------------------------------------------
    259 
    260   //Output::$cache_expiration其实就是缓存时长,就是平时我们在控制器里面$this->output->cache(n)设置的时长
    261   //现实手段就是使这个Output::$cache_expiration有一定的值,然后程序执行到这里时根据此值判断是否要缓存,
    262   //如果要缓存就生成缓存文件。(注意如果是_display_cache间接调用的话,$this->cache_expiraton是一定为0的,因为
    263   //没有经历过在控制器中调用$this->output->cache(n)。)
    264   if ($this->cache_expiration > 0 && isset($CI) && ! method_exists($CI, '_output'))
    265   {
    266    //上面有个判断$CI是否有_output方法,其实是提供一个机会让我们自定义处理输出。
    267    //生成缓存文件。
    268    $this->_write_cache($output);
    269   }
    270 
    271   // --------------------------------------------------------------------
    272 
    273   $elapsed = $BM->elapsed_time('total_execution_time_start', 'total_execution_time_end');
    274 
    275   if ($this->parse_exec_vars === TRUE)
    276   {
    277    //系统的总体运行时间和内存消耗就是在这里替换的。呵呵。上面的Output::$parse_exec_vars就是设置要不要替换。
    278    $memory  = ( ! function_exists('memory_get_usage')) ? '0' : round(memory_get_usage()/1024/1024, 2).'MB';
    279 
    280    $output = str_replace('{elapsed_time}', $elapsed, $output);
    281    $output = str_replace('{memory_usage}', $memory, $output);
    282   }
    283 
    284   // --------------------------------------------------------------------
    285   //压缩传输的处理。
    286   if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc == FALSE)
    287   {
    288    if (extension_loaded('zlib'))
    289    {
    290     if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) AND strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
    291     {
    292      ob_start('ob_gzhandler');
    293     }
    294    }
    295   }
    296 
    297   // --------------------------------------------------------------------
    298   if (count($this->headers) > 0)
    299   {
    300    foreach ($this->headers as $header)
    301    {
    302     @header($header[0], $header[1]);
    303    }
    304   }
    305 
    306   // --------------------------------------------------------------------
    307   //如果没有超级控制器,可以证明当前是在处理一个缓存的输出。不过利用这个方式来判断,真的有点那个。。。
    308   if ( ! isset($CI))
    309   {
    310    echo $output;//输出缓存内容。结束本函数。
    311    log_message('debug', "Final output sent to browser");
    312    log_message('debug', "Total execution time: ".$elapsed);
    313    return TRUE;
    314   }
    315 
    316   // --------------------------------------------------------------------
    317   //这里是一个评测器,如果有开启就调用,会生成一些报告到页面尾部用于辅助我们调试。我用CI的时候其实没有开启过,厄。
    318   if ($this->enable_profiler == TRUE)
    319   {
    320    $CI->load->library('profiler');
    321 
    322    if ( ! empty($this->_profiler_sections))
    323    {
    324     $CI->profiler->set_sections($this->_profiler_sections);
    325    }
    326 
    327    if (preg_match("|</body>.*?</html>|is", $output))
    328    {
    329     $output  = preg_replace("|</body>.*?</html>|is", '', $output);
    330     $output .= $CI->profiler->run();
    331     $output .= '</body></html>';
    332    }
    333    else
    334    {
    335     $output .= $CI->profiler->run();
    336    }
    337   }
    338 
    339   // --------------------------------------------------------------------
    340 
    341   //如果我们有在当前的控制器里面定义了_output这个方法,那么可以利用这个输出做你想做的东西,这个也是很不错的功能。
    342   if (method_exists($CI, '_output'))
    343   {
    344    $CI->_output($output);
    345   }
    346   else
    347   {
    348    //如果没有定义_output,就默认简单输出。
    349    echo $output;  // Send it to the browser!
    350   }
    351 
    352   log_message('debug', "Final output sent to browser");
    353   log_message('debug', "Total execution time: ".$elapsed);
    354  }
    355 
    356  // --------------------------------------------------------------------
    357 
    358  /**
    359   * Write a Cache File
    360   */
    361  /**
    362   * 写入缓存。在Output::_display()中,判断需要缓存页面时,则调用此方法写入缓存。
    363   */
    364  function _write_cache($output)
    365  {
    366   //下面很多操作其实和读缓存的时候差不多。
    367   
    368   $CI =& get_instance();
    369   $path = $CI->config->item('cache_path');
    370 
    371   $cache_path = ($path == '') ? APPPATH.'cache/' : $path;
    372 
    373   if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
    374   {
    375    log_message('error', "Unable to write cache file: ".$cache_path);
    376    return;
    377   }
    378 
    379   $uri = $CI->config->item('base_url').
    380     $CI->config->item('index_page').
    381     $CI->uri->uri_string();
    382 
    383   $cache_path .= md5($uri);
    384 
    385   if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
    386   {
    387    log_message('error', "Unable to write cache file: ".$cache_path);
    388    return;
    389   }
    390 
    391   //计算缓存文件过期时间。
    392   $expire = time() + ($this->cache_expiration * 60);
    393 
    394   if (flock($fp, LOCK_EX))
    395   {
    396    //按CI的格式写入缓存内容。
    397    fwrite($fp, $expire.'TS--->'.$output);
    398    flock($fp, LOCK_UN);
    399   }
    400   else
    401   {
    402    log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
    403    return;
    404   }
    405   fclose($fp);
    406   @chmod($cache_path, FILE_WRITE_MODE);
    407 
    408   log_message('debug', "Cache file written: ".$cache_path);
    409  }
    410 
    411  // --------------------------------------------------------------------
    412 
    413  /**
    414   * Update/serve a cached file
    415   */
    416  //在CodeIgniter.php里面有调用此方法,此方法是负责缓存的输出,如果在CodeIgniter.php中调用此方法有输出,则
    417  //本次请求的运行将直接结束,直接以缓存输出作为响应。
    418  function _display_cache(&$CFG, &$URI)
    419  {
    420   //取得保存缓存的路径
    421   $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');
    422 
    423   //一条准确的路由都会对应一个缓存文件,缓存文件是对应路由字符串的md5密文。
    424   $uri = $CFG->item('base_url').
    425     $CFG->item('index_page').
    426     $URI->uri_string;
    427 
    428   //计算出当前请求对应缓存文件的完整文件路径。
    429   $filepath = $cache_path.md5($uri);
    430 
    431   //如果没有此缓存文件,获取缓存内容失败,则可以返回FALSE。
    432   if ( ! @file_exists($filepath))
    433   {
    434    return FALSE;
    435   }
    436 
    437   //如果有此缓存文件,但是无法读,获取缓存内容失败,同样返回FALSE。
    438   if ( ! $fp = @fopen($filepath, FOPEN_READ))
    439   {
    440    return FALSE;
    441   }
    442 
    443   //打开到缓存文件,并以$fp作为句柄。下一步先取得共享锁(读取)。
    444   flock($fp, LOCK_SH);
    445 
    446   //读cache并保存到$cache中
    447   $cache = '';
    448   if (filesize($filepath) > 0)
    449   {
    450    $cache = fread($fp, filesize($filepath));
    451   }
    452 
    453   //解锁
    454   flock($fp, LOCK_UN);
    455   //关闭文件连接。
    456   fclose($fp);
    457 
    458   //下面这个TS--->字样,只是因为CI的缓存文件里面的内容是规定以数字+TS--->开头而已。这个数字是代表创建时间。
    459   //如果不符合此结构,可视为非CI的缓存文件,或者文件已损坏,获取缓存内容失败,返回FALSE。
    460   //如果匹配成功,则$match[1]中保存的是"1346901048TS--->"字样。其实在CI的这个版本$match[0]保存的是和
    461   //$match[1]相同的内容,为什么要分开?我觉得是为了以后扩展和方便改动吧,理解成
    462   //$match[0]是除页面内容之外的附加信息。
    463   //$match[1]是附加信息中和时间有关的信息。
    464   if ( ! preg_match("/(d+TS--->)/", $cache, $match))
    465   {
    466    return FALSE;
    467   }
    468 
    469   //去掉TS--->后,利用剩下的数字判断缓存是否已经过期,如果过期了,就把它删除,同样视为获取缓存内容失败(过期),
    470   //返回FALSE
    471   if (time() >= trim(str_replace('TS--->', '', $match['1'])))
    472   {
    473    if (is_really_writable($cache_path))
    474    {
    475     @unlink($filepath);
    476     log_message('debug', "Cache file has expired. File deleted");
    477     return FALSE;
    478    }
    479   }
    480 
    481   //来到这里,说明了能够顺利获得缓存,则去掉附加信息($match[0])后,调用Output::_display()方法输出缓存。并返回
    482   $this->_display(str_replace($match['0'], '', $cache));
    483   log_message('debug', "Cache file is current. Sending it to browser.");
    484   return TRUE;
    485  }
    486 
    487 
    488 }
  • 相关阅读:
    delphi不同版本字符串类型的演化(要支持基于firemonkey的app调用,字符串最好使用olevariant类型)
    IdHttpServer实现webservice(130篇DataSnap文章)
    hdu 1809 求SG函数
    delphi中无类型文件读写
    delpi中的RTTI初试
    后台调用外部程序的完美实现(使用CreateDesktop建立隐藏桌面)
    delphi之完美Splash方案(在TfrmMain.FormCreate里不断调用TfrmSplash显示加载进度文字,并且及时Update显示)
    查看内存数据的函数(ByteToHex和ByteToBin,最终都变成String)
    SQLsever2008 远程连接错误 linq
    delphi 利用HTTP的POST方法做个在线翻译的小工具 good
  • 原文地址:https://www.cnblogs.com/qxbj/p/4415229.html
Copyright © 2011-2022 走看看