css/js压缩,页面缓存扩展 require 异步js
压缩正则 方案 找到现成的。参考文章
jsmin类库:https://github.com/rgrove/jsmin-php/
http://blog.csdn.net/jsbba/article/details/16367685
案例页面 http://www.mhbuy.cn/ 公共类css js 基本用这个压缩完成
20140815: 优化 支持304页面缓存判断,404文件过滤,Gzip,压缩正则优化
20140815:require 优化异步加载js 压缩正则修改 amd 规范修改
20141217: 新开项目手机微站,准备采用seajs 延迟加载,所以目前完整的解决方式上传备份下
准备开发seajs模式的,LocalStorage 本地存储,模仿微信那种加载方式
<?php
// +----------------------------------------------------------------------
// | Author: lhb <lihaibo123as@163.com>
// +----------------------------------------------------------------------
namespace Extend\Lib;
/**
* css/js 压缩扩展
*
*/
class CompressFile {
//页面缓存时间
public $expire = null;
//压缩缓存文件目录
public $cache_dir = null;
//文件后缀
public $content_type = null;
//允许压缩文件类型
public $content_type_arr = array(
'css' => "text/css",
'js' => "application/x-javascript"
);
//文件注释信息
public $file_note = null;
public $file_path_type = 1;
//gzip开关
public $encode_gzip = true;
//压缩开关
public $min_flag = true;
/**
* 初始化compressFile
* @param type $expire
* @param type $cache_dir
*/
public function __construct($expire = 0, $cache_dir = '') {
$this->expire = $expire ? $expire : 3 * 24 * 3600; //
$this->cache_dir = $cache_dir ? $cache_dir : RUNTIME_PATH . 'Static/'; //运行目录
$this->file_note = ';/*' . PHP_EOL . '%s' . PHP_EOL . 'Cache Time:' . date("Y-m-d H:i:s") . PHP_EOL . '*/' . PHP_EOL;
$this->min_flag = C('MIN_CSS_JS_DO_FLAG') ? true : false;
}
/**
* 初始化头文件
*/
public function initHeader() {
header("Content-Type: {$this->content_type_arr[$this->content_type]}; charset:utf-8"); //注意修改到你的编码
// header("Cache-Control: must-revalidate");
header("Cache-Control: max-age={$this->expire}");
header("Pragma: cache");
header("Age:10");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $this->expire) . " GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
header("Access-Control-Allow-Origin: *");
header('X-Powered-By:lihaibo123as@163.com');
}
/**
* 无js header
*/
public function noneJSFile() {
header("Content-Type: application/x-javascript; charset:utf-8"); //注意修改到你的编码
// header("Cache-Control: must-revalidate");
header("Cache-Control: max-age={$this->expire}");
header("Pragma: cache");
header("Age:10");
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $this->expire) . " GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
header("Access-Control-Allow-Origin: *");
header('X-Powered-By:lihaibo123as@163.com');
header('HTTP/1.1 200 OK');
}
/**
* 压缩主进程
* @param type $files 文件路劲名数组
* @param type $file_path_type 文件路径类型 1 原始路径 2 静态压缩路径 原始文件hash唯一
*/
public function run($files, $file_path_type = 1) {
$this->checkType304();
$content = "";
$this->file_path_type = $file_path_type;
if (!empty($files)) {
$this->checkType404($files[0]);
if ($this->content_type == "css") {
$content .= '@charset "utf-8";' . PHP_EOL; // {//注释情况特殊处理
}
$this->cache_dir .="/" . $this->content_type . '/';
if ($this->cache_dir && !is_dir($this->cache_dir)) {
\Extend\Lib\PublicTool::mkdirs($this->cache_dir);
}
foreach ($files as $file) {
if ($this->fixFileType($file) == $this->content_type) {
$content .= $this->cacheFile($file);
$content .=PHP_EOL;
} else {
continue;
}
}
}
$this->checkGzip();
if ($this->encode_gzip && Extension_Loaded('zlib'))
Ob_Start('ob_gzhandler');
$this->initHeader();
echo $content;
if ($this->encode_gzip && Extension_Loaded('zlib'))
Ob_End_Flush();
exit();
}
/**
* 构造URL
* @param type $files
* @return type
*/
public static function buildUrl($files) {
// $sub_site = \Extend\Lib\PublicTool::getSubSite();
// if (!key_exists(C('MIN_SERVER_SITE'), C('APP_SUB_DOMAIN_RULES'))) {
// $sub_site .='/extend';
// } else {
// $sub_site = \Extend\Lib\PublicTool::getSubSite(C('MIN_SERVER_SITE'));
// }
$param = "";
// $site = "http://{$sub_site}/combo/?f=";
if (C('MIN_CSS_JS_HIDDEN_PATH')) {
$data = array();
// $site = "http://{$sub_site}/combo/?mf=";
$this_ = new \Extend\Lib\CompressFile();
$this_->file_note = "";
if (!empty($files) && $this_->checkFileAuth($files[0])) {
$this_->cache_dir .="/" . $this_->content_type . '/';
if ($this_->cache_dir && !is_dir($this_->cache_dir)) {
\Extend\Lib\PublicTool::mkdirs($this_->cache_dir);
}
foreach ($files as $file) {
if ($this_->fixFileType($file) == $this_->content_type) {
$data[] = $this_->saveCacheFile($file);
} else {
continue;
}
}
}
$files = $data;
$param = 'mf=' . implode(';', $files);
} else {
$param = 'f=' . implode(';', $files);
}
return U('Extend/combo/index', '', true, true) . '?' . $param;
// return $site . implode(';', $files);
}
/**
* 压缩并缓存文件
* @param type $file
* @return string
*/
private function cacheFile($file) {
$content = '';
switch ($this->file_path_type) {
case 1:
// $filename = md5_file($filepath) . '.' . $this->content_type;
$filepath = \Extend\Lib\PublicTool::complateFilePath($file);
$filehash = hash_file('crc32', $filepath);
$filename = preg_replace("/\/|\.|-/", "_", $file) . '_' . $filehash . '.' . $this->content_type;
if (file_exists($this->cache_dir . $filename)) {
$content = file_get_contents($this->cache_dir . $filename);
} else {
$content = file_get_contents($filepath);
$content = $this->minCssJS($content);
$content = $this->complateImageUrl($content, $file);
$content = sprintf($this->file_note, $file) . $content;
file_put_contents($this->cache_dir . $filename, $content);
}
break;
case 2:
if (file_exists($this->cache_dir . $file)) {
$content = file_get_contents($this->cache_dir . $file);
}
break;
}
return $content;
}
/**
* 保存缓存文件
* @param type $file
* @return string
*/
public function saveCacheFile($file) {
$filepath = \Extend\Lib\PublicTool::complateFilePath($file);
$content = '';
if (file_exists($filepath)) {
// $filename = md5_file($filepath) . '.' . $this->content_type;
$filehash = hash_file('crc32', $filepath);
$filename = hash("crc32b", preg_replace("/\/|\.|-/", "_", $file)) . '_' . $filehash . '.' . $this->content_type;
$cachename = $this->cache_dir . $filename;
if (!file_exists($cachename)) {
$content = file_get_contents($filepath);
$content = $this->minCssJS($content);
$content = $this->complateImageUrl($content, $file);
$content = sprintf($this->file_note, $file) . $content;
file_put_contents($cachename, $content);
}
}
return $filename;
}
/**
* 补全css imgurl路劲
* @param type $content
* @param type $file
* @return type
*/
private function complateImageUrl($content, $file) {
$file_arr = explode("/", $file);
unset($file_arr[count($file_arr) - 1]);
// var_dump($file_arr);
preg_match_all("/url\([\"\']?(.*?)[\"\']?\)/", $content, $matchs);
$urls = $matchs[1];
$replace_urls = array();
// var_dump($matchs);
if (!empty($urls)) {
foreach ($urls as $key => $k) {
$k_arr = explode("/", $k);
$temp_k_arr = $file_arr;
if (0 === strpos($k, 'data:')) {//查找首字母 data:
$replace_urls[$key] = $matchs[0][$key];
continue;
}
foreach ($k_arr as $kk => $vv) {
if ($vv == '..' && count($temp_k_arr) > 0) {
unset($temp_k_arr[count($temp_k_arr) - 1]);
} else {
$temp_k_arr[] = $vv;
}
}
// $replace_urls[] = \Extend\Lib\PublicTool::complateUrl(implode("/", $temp_k_arr));
$replace_urls[$key] = str_replace($k, \Extend\Lib\PublicTool::complateUrl(implode("/", $temp_k_arr)), $matchs[0][$key]);
// var_dump($temp_k_arr);
}
$content = str_replace($matchs[0], $replace_urls, $content);
}
// echo $content;
// var_dump($urls);
// var_dump($replace_urls);
// exit();
return $content;
}
/**
* 压缩正则 自己摸索查资料完善,不保证压缩后保持js或者css完整度
* @param type $content
* @return type
*/
private function minCssJS($content) {
switch ($this->content_type) {
case "css":
return $this->minCss($content);
break;
case "js":
return $this->minJS($content);
break;
}
return $content;
$chunks = preg_split('/(<pre.*?\/pre>)/ms', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
$content = '';
foreach ($chunks as $c) {
if (!$this->min_flag) {
$content .=$c;
continue;
}
if (strpos($c, '<pre') !== 0) {
//[\s]*
// preg_match_all('/(^\/\/[^\n]*)|([\s]+\/\/[^\n]*)/', $c, $matchs);
// var_dump($matchs);
// $c = str_replace($matchs, '', $c);
if ($this->content_type == "css") {
$c = preg_replace('/^@charset(.*)?;/', '', $c); // {//注释情况特殊处理
}
$c = preg_replace('/{(\/\/[^\n]*)/', '{', $c); // {//注释情况特殊处理
$c = preg_replace('/(^\/\/[^\n]*)|([\s]+\/\/[^\n]*)/', '', $c); //行注释
// echo $c;
// exit();
if ($this->content_type == "js") {
//js特殊处理补全
$c = preg_replace("/}[\s]+\\$/", "};$", $c);
}
$c = preg_replace('/[\n\r\t]+/', ' ', $c); //换行空格等过滤
$c = preg_replace('/>\\s</', '><', $c);
$c = preg_replace('/\\/\\*.*?\\*\\//i', '', $c);
$c = preg_replace("/[\s]{2,}/", " ", $c);
$c = preg_replace("/[\s]+}/", "}", $c);
$c = preg_replace("/}[\s]+/", "}", $c);
$c = preg_replace("/[\s]+{/", "{", $c);
$c = preg_replace("/{[\s]+/", "{", $c);
$c = preg_replace("/[\s]+;/", ";", $c);
$c = preg_replace("/;[\s]+/", ";", $c);
$c = preg_replace("/[\s]+:/", ":", $c);
$c = preg_replace("/:[\s]+/", ":", $c);
$c = preg_replace("/[\s]+=/", "=", $c);
$c = preg_replace("/=[\s]+/", "=", $c);
$c = preg_replace("/,[\s]{2,}/", ", ", $c);
if ($this->content_type == "js") {
//js特殊处理补全
$c = preg_replace("/;}/", "}", $c);
// $c = preg_replace("/}\\$/", "};$", $c);
$c = preg_replace("/}var/", "};var", $c);
$c = preg_replace("/}return/", "};return", $c);
}
// $c = preg_replace('/\\s{2,}/', ' ', $c);
}
$content .= $c;
}
// echo $c;
// exit();
return trim($content);
}
/**
* 压缩css
* @param type $content
* @return type
*/
private function minCss($content) {
$content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content);
$content = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $content);
return $content;
}
/**
* 压缩js
* @param type $content
* @return type
*/
private function minJS($content) {
return \Extend\Lib\JSMin::minify($content);
}
/**
* 304缓存检查
*/
protected function checkType304() {
$expires = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
if ($expires + $this->expire > time()) {
header("Cache-Control: max-age={$this->expire}");
header("Pragma: cache");
header("Age:10");
header("Expires: " . gmdate("D, d M Y H:i:s", $expires + $this->expire) . " GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
header('X-Powered-By:lihaibo123as@163.com');
header("Access-Control-Allow-Origin: *");
header('HTTP/1.1 304 Not Modified');
exit();
}
}
/**
* 验证是否是gzip
* @return boolean
*/
protected function checkGzip() {
if (stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE) {
return false;
}
$this->encode_gzip = true;
return true;
}
/**
* 文件 404 过滤
* @param type $filename
*/
protected function checkType404($filename) {
if (!$this->checkFileAuth($filename)) {
header('HTTP/1.1 404 Not Found');
exit();
}
}
/**
* 验证文件
* @param type $filename
* @return boolean
*/
public function checkFileAuth($filename) {
$type = $this->fixFileType($filename);
if (!key_exists($type, $this->content_type_arr)) {
return false;
}
$this->content_type = $type;
return true;
}
/**
* 获取文件类型后缀
* @param type $filename
* @return boolean
*/
protected function fixFileType($filename) {
preg_match_all('/\.(([a-zA-Z]+)?)/', $filename, $match);
$length = count($match);
$type = $match[$length - 1][count($match[$length - 1]) - 1];
if (empty($match) || !key_exists($type, $this->content_type_arr)) {
return FALSE;
}
return $type;
}
}