首页 > 代码库 > CI框架缓存的实现原理
CI框架缓存的实现原理
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | 今天花了点时间看了下CI框架源码缓存的实现,写出来梳理下思路. 1:在CI框架中加载视图文件使用的是 $this ->load->view();方法,所以从load类库着手,在ci的system文件夹中可以看到Loader.php,这个类库是在Controller.php中被加载的。Loader类中有个方法: function view( $view , $vars = array (), $return = FALSE) //加载视图 { return $this ->_ci_load( array ( ‘_ci_view‘ => $view , ‘_ci_vars‘ => $this ->_ci_object_to_array( $vars ), ‘_ci_return‘ => $return )); } 调用了自身的一个私有方法_ci_load(),这个方法其中关键部分在: ob_start(); //开启缓存 // If the PHP installation does not support short tags we‘ll // do a little string replacement, changing the short tags // to standard PHP echo statements. if ((bool) @ ini_get ( ‘short_open_tag‘ ) === FALSE AND config_item( ‘rewrite_short_tags‘ ) == TRUE) { echo eval ( ‘?>‘ .preg_replace( "/;*\s*\?>/" , "; ?>" , str_replace (‘< php echo file_get_contents_ci_pathbr /> } else { //将视图包含进来 include ( $_ci_path ); // include() vs include_once() allows for multiple views with the same name } if (ob_get_level() < $this -<_ci_ob_level + 1) { ob_end_flush(); } else { $_ci_CI -<output-<append_output(ob_get_contents()); //获取缓存,调用了output类中的append_output方法将缓存的内容放到了output类的全局变量final_output中,供后面使用。 @ob_end_clean(); } 2:CI框架中设置缓存的方法是 $this -<output-<cache(n) //n是分钟数 打开system/core/Output.php在里面有个cache方法: function cache( $time ) { $this -<cache_expiration = ( ! is_numeric ( $time )) ? 0 : $time ; //output类中变量cache_expiration赋上缓存时间 return $this ; } 3:打开system/core/Codeigniter.php这个核心文件。可以看到如下代码: $OUT =& load_class( ‘Output‘ , ‘core‘ ); //实例化output类 // 调用钩子 cache_override hook if ( $EXT ->_call_hook( ‘cache_override‘ ) === FALSE) //如果没有设置这个缓存钩子就使用默认的_display_cache方法 { if ( $OUT ->_display_cache( $CFG , $URI ) == TRUE) //将config,uri类的对象传入 { exit ; //如果调用缓存成功就会直接显示页面中断程序,不会加载实例化下面的类,进行一些请求,这就是缓存的好处; } } 4:找到Output.php类中的私有方法_display_cache( $CFG , $URI ): function _display_cache(& $CFG , & $URI ) { //是否在配置文件中定义了缓存路径,如果没有是用系统默认的cache文件夹作为缓存目录 $cache_path = ( $CFG ->item( ‘cache_path‘ ) == ‘‘ ) ? APPPATH. ‘cache/‘ : $CFG ->item( ‘cache_path‘ ); // 构造文件路径。文件名是 URI 的 md5 值 $uri = $CFG ->item( ‘base_url‘ ). $CFG ->item( ‘index_page‘ ). $URI ->uri_string; //这是请求的页面的控制器/方法/参数那一串字符 $filepath = $cache_path .md5( $uri ); // 判断文件是否存在 if ( ! @ file_exists ( $filepath )) { return FALSE; //到了这里就中断了,而是按照正常的向服务器请求页面内容,下面的return false同理 } if ( ! $fp = @ fopen ( $filepath , FOPEN_READ)) { return FALSE; } flock ( $fp , LOCK_SH); //读取文件前给文件加个共享锁 $cache = ‘‘ ; if ( filesize ( $filepath ) > 0) { $cache = fread ( $fp , filesize ( $filepath )); } flock ( $fp , LOCK_UN); //释放锁 fclose( $fp ); // 匹配内嵌时间戳 if ( ! preg_match( "/(\d+TS--->)/" , $cache , $match )) { return FALSE; } // Has the file expired? If so we‘ll delete it. // 文件过期了,就删掉 if (time() >= trim( str_replace ( ‘TS--->‘ , ‘‘ , $match [ ‘1‘ ]))) { if (is_really_writable( $cache_path )) { @unlink( $filepath ); log_message( ‘debug‘ , "Cache file has expired. File deleted" ); return FALSE } // Display the cache // 显示缓存,到了这里说明有缓存文件并且缓存文件没过期,然后执行_display方法 $this ->_display( str_replace ( $match [ ‘0‘ ], ‘‘ , $cache )); log_message( ‘debug‘ , "Cache file is current. Sending it to browser." ); return TRUE; } 5:找到Output方法中的_display( $output = ‘‘ )方法,这个 方法有两处调用了,1个是在上述的_display_cache中,将缓存文件中的内容取出赋于 $output 变量然后传入_display( $output = ‘‘ )中,这时候只会执行_display中的: //$CI 对象不存在,我们就知道我们是在处理缓存文件,所以简单的输出和退出 if ( ! isset( $CI )) { echo $output ; //直接将缓存输出,返回ture中断codeigniter继续执行 log_message( ‘debug‘ , "Final output sent to browser" ); log_message( ‘debug‘ , "Total execution time: " . $elapsed ); return TRUE; } 第二处调用是,当 if ( $OUT ->_display_cache( $CFG , $URI ) == TRUE)这个判断不成立codeigniter向下执行, 先后实例化了一些系统核心类,以及url中请求的控制器方法等.最后执行一个钩子: // 调用 display_override hook if ( $EXT ->_call_hook( ‘display_override‘ ) === FALSE) { $OUT ->_display(); } 这时候执行这个方法是无缓存的情况下. 这时候 $output 为空所以执行了: // 设置输出数据 if ( $output == ‘‘ ) { $output =& $this ->final_output; //这就是在Loader中设置的输出缓存. } 接下来如果执行了 $this ->output->cache()方法设置了 $this ->cache_expiration 参数且没有缓存文件时: // 启用 cache 时,$CI 没有 _output 函数时,调用 $this->_write_cache,写缓存文件 if ( $this ->cache_expiration > 0 && isset( $CI ) && ! method_exists( $CI , ‘_output‘ )) { $this ->_write_cache( $output ); } _write_cache( $output )方法如下: function _write_cache( $output ) { $CI =& get_instance(); $path = $CI ->config->item( ‘cache_path‘ ); $cache_path = ( $path == ‘‘ ) ? APPPATH. ‘cache/‘ : $path ; // $cache_path 是目录并且可写 if ( ! is_dir ( $cache_path ) OR ! is_really_writable( $cache_path )) { log_message( ‘error‘ , "Unable to write cache file: " . $cache_path ); return ; } $uri = $CI ->config->item( ‘base_url‘ ). $CI ->config->item( ‘index_page‘ ). $CI ->uri->uri_string(); $cache_path .= md5( $uri ); if ( ! $fp = @ fopen ( $cache_path , FOPEN_WRITE_CREATE_DESTRUCTIVE)) { log_message( ‘error‘ , "Unable to write cache file: " . $cache_path ); return ; } // 加个时间戳,指示过期时间 $expire = time() + ( $this ->cache_expiration * 60); if ( flock ( $fp , LOCK_EX)) //写入前先加个独占锁 { fwrite( $fp , $expire . ‘TS--->‘ . $output ); flock ( $fp , LOCK_UN); //写完解锁 } else { log_message( ‘error‘ , "Unable to secure a file lock for file at: " . $cache_path ); return ; } fclose( $fp ); @ chmod ( $cache_path , FILE_WRITE_MODE); log_message( ‘debug‘ , "Cache file written: " . $cache_path ); } 写完缓存后会进行一系列处理比如设置header等 最后输出 $output : if (method_exists( $CI , ‘_output‘ )) { $CI ->_output( $output ); } else { echo $output ; // Send it to the browser! } 总结:CI的缓存是在要输出的页面设置ob_start(),使用ob_get_contents()获取缓存内容,然后通过判断设置中 是否设置缓存.如果设置了则将缓存将页面的url地址进行MD5哈希作为缓存文件名创建之,然后将(当前时间+设置的缓存时间)+一个特殊符号+内容写到 缓存文件中,下次访问时候将访问的url进行MD5查找这个缓存文件,如果没有则再创建.有则取出其中的内容,分离出过期时间和内容,判断时间是否过期, 如果过期则丢弃内容,继续进行请求,如果没过期直接取出内容输出到页面,中断执行。CI将这一套缓存机制用面向对象的方法写到了框架中,使用起来很方便。 CI默认的这种缓存方法是缓存整个页面。但有时候只要缓存页面中不变的元素header和footer比较好,CI中还有钩子的机制,可以自己设置缓存的 方法替换其中的_display_cache()方法。具体的可以看手册 |
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。