css/js压缩,页面缓存扩展 require 异步js

浏览:3419 最后更新:2014-12-17 16:36 分类:类库 关键字: css js 压缩,页面缓存,require 异步js
css/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;
    }

}
评论( 相关
后面还有条评论,点击查看>>