为了提高页面加载速度,通常将css文件和js文件压缩成生成一个文件引入,其中css直接以文本形式放在页面头部,js以异步请求的方式放在页面底部。下面为CI框架下实现的压缩类。
压缩类文件:Compress.php
<?php
// +----------------------------------------------------------------------
// | 静态资源css,js压缩合并类Compress.class.php
// +----------------------------------------------------------------------
// | Copyright (c) 2003-2014 http://www.17php.org All rights reserved.
// | 支持压缩js,css,剔除换行,注释
// | 合并多个js和css
// | 自动修正css里的背景图片的地址,绝对路径和远程图片url不会被修正
// | 自动检测不存在的载入js,css 提示错误并退出
// | 缓存已经压缩合并的css和js,只有在载入的js,css有改动才会重新压缩合并
// | 会在根目录下生成/Static/Min/Js/和/Static/Min/Css/目录存放压缩合并后的文件
// +----------------------------------------------------------------------
class Compress {
const FILEDIR = '/static/min/';
static public $compress = null;
private $_type = 'css';
private $_mode = '';
private $_files = array(); //待处理的文件
private $_cacheFileDir = ''; //缓存文件存放目录
private $_cacheFileName = ''; //缓存文件名称
private $_fileContent = ''; //文件内容
public function init($files = array(), $type = "css", $outPath = null){
$this->_setType($type); //设置文件类型
$this->_setMode(); //设置文件处理模型
$this->_setFiles($files); //设置待处理文件
$this->_setCaheFileDir($outPath); //设置缓存文件目录
$this->_setCacheFileName(); // 设置缓存文件名称
$this->_setFileContent(); //设置文件内容
}
//获取压缩文件内容
static public function get($file = array(),$type='css',$outPath=null,$method='link'){
if(!self::$compress instanceof self){
self::$compress = new self();
}
self::$compress->init($file,$type,$outPath);
//更新文件内容
return self::$compress->_getFile($method);
}
//对外提供缓存文件名称
public function getCacheFile(){
return self::getDomain().self::FILEDIR."{$this->_type}/".$this->_cacheFileName;
}
//对外提供文件内容
public function getFileContent(){
return $this->_fileContent;
}
//获取缓存文件
private function _getFile($method='link'){
$method .= 'Reback';
$compress = $this->_mode;
return $compress::$method($this);
}
//设置文件类型
private function _setType($type){
$this->_type = $type;
}
//设置文件处理模型
private function _setMode(){
$mode = 'Compress'.ucfirst(strtolower($this->_type));
if(!class_exists($mode)) throw new CompressException("文件格式{$mode}无法识别");
return $this->_mode = $mode;
}
//设置缓存文件目录
private function _setCaheFileDir($outPath=''){
$outPath = empty($outPath) ? $_SERVER['DOCUMENT_ROOT'].self::FILEDIR."{$this->_type}/" : $outPath;
if(!is_dir($outPath)){
if(!mkdir($outPath,0775,TRUE)) throw new CompressException("缓存文件目录{$outPath}创建失败");
}
return $this->_cacheFileDir = $outPath;
}
//设置待处理文件
public function _setFiles($files=array()){
if(empty($files)) throw new CompressException("待压缩文件名缺失");
$files = is_array($files) ? $files : explode(',',$files);
foreach($files as $key=>$value){
$fileName = $_SERVER['DOCUMENT_ROOT'].$value;
if(!file_exists($fileName)) {
throw new CompressException("待压缩文件{$fileName}缺失");
break;
}
$files[$key] = $fileName;
}
return $this->_files = $files;
}
//生成缓存文件名称(依据文件名和文件内容)
private function _setCacheFileName(){
$cacheName = '';
foreach($this->_files as $key=>$value){
$cacheName .= md5_file($value);
}
return $this->_cacheFileName = md5($cacheName).".{$this->_type}";
}
//更新缓存文件内容
private function _setFileContent(){
$cacheFilePath = $this->_cacheFileDir.$this->_cacheFileName;
if(file_exists($cacheFilePath)){
$this->_fileContent = file_get_contents($cacheFilePath);
}else{
file_put_contents($cacheFilePath,$this->_getFileContent());
}
return $this->_fileContent;
}
//获取文件内容
private function _getFileContent(){
$content = '';
$compress = $this->_mode;
foreach($this->_files as $key=>$value){
$content .= $compress::strip($value);
}
return $this->_fileContent = $content;
}
//获取网站域名
public static function getDomain(){
/* 协议 */
$protocol = (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) ? 'https://' : 'http://';
/* 域名或IP地址 */
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$host = $_SERVER['HTTP_X_FORWARDED_HOST'];
} elseif (isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
} else {
$port = ''; //端口
if (isset($_SERVER['SERVER_PORT'])) {
$port = ':' . $_SERVER['SERVER_PORT'];
if ((':80' == $port && 'http://' == $protocol) || (':443' == $port && 'https://' == $protocol)) {
$port = '';
}
}
if (isset($_SERVER['SERVER_NAME'])) {
$host = $_SERVER['SERVER_NAME'] . $port;
} elseif (isset($_SERVER['SERVER_ADDR'])) {
$host = $_SERVER['SERVER_ADDR'] . $port;
}
}
return $protocol . $host;
}
}
/**
* 压缩类型父类
*/
interface CompressModel {
static public function strip($file='');
static public function linkReback(Compress $e);
static public function contentReback(Compress $e);
}
/**
* 压缩JS
*/
class Compressjs implements CompressModel{
private static $ignore = array("://", "\//", '"//', "'//");
private static $temporary = array("!@#temporary_string_one!@#", "!@#temporary_string_two!@#", "!@#temporary_string_three!@#", "!@#temporary_string_four!@#");
//过滤无效字符
static function strip($file=''){
$content = file_get_contents($file);
$content = str_replace(self::$ignore, self::$temporary, $content);
$content = preg_replace('#\/\/[^\r\n]*#', '', $content); //剔除js行注释
$content = str_replace(self::$temporary, self::$ignore, $content);
$content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content); //剔除内容里的块注释
$content = str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $content); //剔除换行
$content = str_replace(array(' ',' '),' ',$content); //剔除多空格
return rtrim($content,';').';';
}
//以link形式返回
static function linkReback(Compress $e){
return '<script type="text/javascript" src="' . $e->getCacheFile() . '"></script>';
}
//以文本形式返回
static function contentReback(Compress $e){
return "<style type=\"text/javascript\">".$e->getFileContent()."</style>";
}
}
/**
* 压缩CSS
*/
class CompressCss implements CompressModel {
/**
* strip 过滤无效字符
* @param string $file 待处理的文件路径
* @return string 处理后的内容
*/
static function strip($file=''){
$content = file_get_contents($file);
$path = dirname($file) . "/"; //获取载入文件的目录
preg_match_all("/url\((.*)\)/iUs", $content, $mach);
$mach = array_unique($mach[1]);
foreach ($mach as $k => $v) {
$url = trim($v, "'\"");
if(strpos($v,'data:image') !== false) continue;
if (substr($url, 0, 1) != "/" && substr(strtolower($url), 0, 4) != 'http') {
$url = explode("?", $path . trim($url, "'\""));
$url[0] = realpath($url[0]);
$url = str_replace(str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']), '', str_replace('\\', '/', implode("?", $url)));
$url = '/' . trim($url, "/");
if (!empty($url)) $content = str_replace($v, $url, $content);
}
}
$content = str_replace(array(", "), ',', $content);
$content = str_replace(array(': '), ':', $content);
$content = preg_replace('/@charset (.*);/i', '', $content); //剔除css编码信息
$content = str_replace(array("url()", "background:;", "background: ;"), '', $content);
$content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content); //剔除内容里的块注释
$content = str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $content); //剔除换行
$content = str_replace(array(' ',' '),' ',$content); //剔除多空格
return $content;
}
//以link形式返回
static function linkReback(Compress $e){
return '<link type="text/css" rel="stylesheet" href="' . $e->getCacheFile() . '"></link>';
}
//以文本形式返回
static function contentReback(Compress $e){
return "<style type=\"text/css\">".$e->getFileContent()."</style>";
}
}
class CompressException extends Exception {
public function __construct($message='',$code=0){
parent::__construct($message,$code);
}
public function handle(){
writeLog($this->getmessage(),'CompressException');
}
}
在 base_help.php 文件中定义执行入口:
if (!function_exists("css")) {
/**
* 压缩css
* @param array|string $files 要载入的css文件
* @param boolen $getUrl 是否获取压缩文件的url
* @return string
*/
function css($files = array(), $type = 'content') {
include_once APPPATH . '/libraries/Compress.class.php';
try{
return Compress::get($files,'css',null,$type);
}catch(CompressException $e){
$e->handle();
}
}
}
if (!function_exists("js")) {
/**
* 压缩js
* @param array|string $files 要载入的js文件
* @param boolen $getUrl 是否获取压缩文件的url
* @return string
*/
function js($files = array(), $type = 'link') {
include_once APPPATH . '/libraries/Compress.class.php';
try{
return Compress::get($files,'js',null,$type);
}catch(CompressException $e){
$e->handle();
}
}
}