cache_path)) { FileHandler::makeDir($this->cache_path); } } /** * @brief optimize 대상 파일을 받아서 처리 후 optimize 된 파일이름을 return **/ function getOptimizedFiles($source_files, $type = "js") { if(!is_array($source_files) || !count($source_files)) return; // $source_files의 역슬래쉬 경로를 슬래쉬로 변경 (윈도우즈 대비) foreach($source_files as $key => $file) $source_files[$key] = str_replace("\\","/",$file); // 관리자 설정시 설정이 되어 있지 않으면 패스 $db_info = Context::getDBInfo(); if($db_info->use_optimizer == 'N') return $source_files; // 캐시 디렉토리가 없으면 실행하지 않음 if(!is_dir($this->cache_path)) return $source_files; if(!count($source_files)) return; foreach($source_files as $file) { if(!$file) continue; $file = str_replace("\\","/",$file); if(substr($file,7)=='http://' || $file == './common/css/button.css') $files[] = $file; else $targets[] = $file; } if(!count($targets)) return $files; $hashed_filename = $this->getHashFilename($targets); $filename = sprintf("%s%s.%s.php", $this->cache_path, $hashed_filename, $type); $this->doOptimizedFile($filename, $targets, $type); $files[] = $filename; return $files; } /** * @brief optimize는 대상 파일을 \n로 연결후 md5 hashing하여 파일이름의 중복을 피함 * 개별 파일과 optimizer 클래스 파일의 변경을 적용하기 위해 각 파일들과 Optimizer.class.php의 filemtime을 비교, 파일이름에 반영 **/ function getHashFilename($files) { $buff = implode("\n", $files); return md5($buff); } /** * @brief 이미 저장된 캐시 파일과의 시간등을 검사하여 새로 캐싱해야 할지를 체크 **/ function doOptimizedFile($filename, $targets, $type) { // optimized 파일이 없으면 새로 옵티마이징 if(!file_exists($filename)) return $this->makeOptimizedFile($filename, $targets, $type); // 개별 요소들 또는 Optimizer.class.php파일이 갱신되었으면 새로 옵티마이징 $count = count($targets); $last_modified = 0; for($i=0;$i<$count;$i++) { $mtime = filemtime($targets[$i]); if($last_modified < $mtime) $last_modified = $mtime; } $mtime = filemtime($filename); if($mtime < $last_modified || $mtime < filemtime('./classes/optimizer/Optimizer.class.php')) return $this->makeOptimizedFile($filename, $targets, $type); } /** * @brief css나 js파일을 묶어서 하나의 파일로 만들고 gzip 압축이나 헤더등을 통제하기 위해서 php파일을 별도로 만들어서 진행함 **/ function makeOptimizedFile($filename, $targets, $type) { /** * 실제 css나 js의 내용을 합친 것을 구함 **/ // 대상 파일의 내용을 구해오고 css 파일일 경우 url()내의 경로를 변경 foreach($targets as $file) { $str = FileHandler::readFile($file); $str = Context::convertEncodingStr($str); // css 일경우 background:url() 변경 if($type == "css") $str = $this->replaceCssPath($file, $str); $content_buff .= $str."\n"; } if($type == "css") $content_buff = '@charset "utf-8";'."\n".$content_buff; $content_file = substr($filename, 0, -4); $content_filename = str_replace($this->cache_path, '', $content_file); FileHandler::writeFile($content_file, $content_buff); /** * 캐시 타임을 제대로 이용하기 위한 헤더 파일 구함 **/ // 확장자별 content-type 체크 if($type == 'css') $content_type = 'text/css'; elseif($type == 'js') $content_type = 'text/javascript'; // 캐시를 위한 처리 $unique = crc32($content_filename); $size = filesize($content_file); $mtime = filemtime($content_file); $class_mtime = filemtime("./classes/optimizer/Optimizer.class.php"); // js, css 파일을 php를 통해서 출력하고 이 출력시에 헤더값을 조작하여 캐싱과 압축전송이 되도록 함 (IE6는 CSS파일일 경우 gzip압축하지 않음) $header_buff = ' '.$class_mtime.') { header("HTTP/1.1 304"); $cached = true; } } if( preg_match("/MSIE 6.0/i",$_SERVER["HTTP_USER_AGENT"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip")===false || !function_exists("ob_gzhandler") ) { $size = filesize($content_filename); } else { $f = fopen($content_filename,"r"); $buff = fread($f, filesize($content_filename)); fclose($f); $buff = ob_gzhandler($buff, 5); $size = strlen($buff); header("Content-Encoding: gzip"); } header("Content-Type: '.$content_type.'; charset=utf-8"); header("Date: '.substr(gmdate('r'), 0, -5).'GMT"); header("Expires: '.substr(gmdate('r', strtotime('+1 MONTH')), 0, -5).'GMT"); header("Cache-Control: private, max-age=2592000"); header("Pragma: cache"); header("Last-Modified: '.substr(gmdate('r', $mtime), 0, -5).'GMT"); header("ETag: \"'.dechex($unique).'-".dechex($size)."-'.dechex($mtime).'\""); if(!$cached) { if(!$buff) { $f = fopen($content_filename,"r"); fpassthru($f); } else print $buff; } ?>'; FileHandler::writeFile($filename, $header_buff); } /** * @brief css의 경우 import/ background 등의 속성에서 사용되는 url내의 경로를 변경시켜줌 **/ function replaceCssPath($file, $str) { // css 파일의 위치를 구함 $this->tmp_css_path = Context::getRequestUri().preg_replace("/^\.\//is","",dirname($file))."/"; // url() 로 되어 있는 css 파일의 경로를 변경 $str = preg_replace_callback('!url\(("|\')?([^\)]+)("|\')?\)!is', array($this, '_replaceCssPath'), $str); $str = preg_replace('!\/([^\/]*)\/\.\.\/!is','/', $str); // charset 지정 문구를 제거 $str = preg_replace('!@charset([^;]*?);!is','',$str); return $str; } function _replaceCssPath($matches) { if(preg_match("/^(http|\/|\.\/common\/)/is",$matches[2])) return $matches[0]; return sprintf('url(%s%s)', $matches[1], $this->tmp_css_path.$matches[2]); } } ?>