压缩 HTML/PHP

7
我正在使用gzip来压缩我的html/php文件以及js/css等。这样可以很好地减少负载,但我还想“缩小”我的.html和.php页面的标记。理想情况下,我希望能够从.htaccess文件中控制这个过程(在那里我也进行了gzip),而不是将php包含到每个文件中。
我希望输出结果像http://google.comhttp://www.w3-edge.com/wordpress-plugins/w3-total-cache/http://css-tricks.com一样(都是由WordPress的W3 Total Cache插件生成的)。
有人能推荐一个好的方法吗?
3个回答

8

看这些例子,压缩HTML输出的效果几乎微不足道。想一想:压缩对于使用许多重复变量和函数名称的Javascript非常适用,因为压缩的主要作用是缩短它们以及所有对它们的引用。

另一方面,HTML标记没有变量或函数的概念。页面的大部分重量都来自实际标记和内容。这无法被压缩。即使表单变量也需要保持原样,因为它们必须具有原始值才能被服务器正确处理。

Gzip已经可以非常高效地压缩空格。在HTML中,这确实是唯一能做的事情。

此外,压缩PHP没有任何应用,因为虽然它有变量和函数,但它永远不会发送给客户端。缩短名称对于在服务器上编译的东西没有性能优势。

如果你决定压缩你的HTML,请查看WordPress插件的源代码。这就是开源的美妙之处。然而,我怀疑与Gzip相比,收益将微不足道。


8
我不讨论压缩 PHP,因为这并不适用。PHP 代码永远不会发送到客户端。 - Peter Anselmo
@Peter Anselmo:你应该把那个评论加到你的答案里。 - Powerlord
1
我认为他指的是PHP页面的HTML输出。 - Jarrod Nettles
6
“压缩 HTML 输出几乎没有什么作用。”我基本上同意“除非真的需要,否则不要压缩”的观点。然而,Google 显然有自己的理由这样做(虽然这个理由可能不适用于 99% 的项目)。有几个原因让我认真考虑对我的项目进行 HTML 压缩。(1) iPhone 只缓存小于 30k 左右的对象,压缩并不能帮助规避这个限制。(2) 如果你不必担心空格和注释被发送到客户端,你就可以使用比你通常更多的它们——我们在标记上进行了大量缩进。 - Frank Farmer
为什么我不使用gzip?简单来说,它不支持UTF8。请参考http://php.net/manual/en/function.ob-start.php#91963。 - Adam Ramadhan
显示剩余2条评论

5

Peter Anselmo将代码缩小与混淆代码混淆了。在代码混淆中,代码被缩小,并且变量被重命名为最短的任意名称。缩小仅仅是减少代码大小的做法,例如删除空格,而不改变代码的值、名称或语法。

Peter Anselmo还错误地认为缩小标记会导致无关紧要的节省。例如,这个页面显示了18.31%的节省,而且它本来就很整洁。显然,他在发表意见之前从未测试过。您可以使用Pretty Diff工具自己查看成本节省情况:http://prettydiff.com/

您可以尝试反向工程化用于执行PHP的缩小引擎。该代码和配套文档可在prettydiff.com/markupmin.js找到。


4
通常混淆并不仅仅是这样!他确实说,与gzip相比,缩小代码的收益微不足道。经过快速试验,这是18%对75%。现在,在gzip压缩缩小后的版本将再节省1-2%,但你仅仅使用gzip已经完成了大部分工作。 - Rup
1
是的,你完全断章取义了我的话,我是在谈论缩小文件大小以及gzip压缩。你的工具pretty diff并没有评估这种情况,它只评估未压缩代码与压缩后的未压缩代码之间的差异。此外,许多缩小器(包括YUI压缩器、Closure Compilier和Packer)确实会缩短变量名。也许我应该更清楚地说明缩小与混淆的区别,以便于OP理解,但我仍然认为没有必要编辑我的帖子。 - Peter Anselmo
我应该再清楚一些,但是我确实是指简单的缩小和去除空格而不是混淆。我发现一个好的方法是使用输出缓冲与obstart()结合使用,可以在头部简单地调用它,并使用一些正则表达式来剥离标记中的任何不必要的内容,然后将其发送到浏览器。关于PHP页面,是的,我只是指这些页面的标记。prettydiff工具非常有用。谢谢你的提示! - ianyoung
1
你说得对。Peter Anselmo的回答确实需要改变。代码最小化(不做任何修改,这是正常的最小化过程)确实会有所不同。当人们声称不会有区别时,这令人沮丧。话虽如此,压缩gzip会带来最大的差异。 - Chuck Le Butt
如果你发送了一百万封电子邮件,18%就是很多了。 - Rid Iculous

3
我已经创建了3个简单的函数,它们可能需要进行优化,但是它们可以完成任务,它们是我用来格式化代码、日期、值等的一个更大类的一部分:
要使用它,只需调用:
echo format::minify_html($html_output);

以下是代码(目前还在beta测试阶段,但是目前还没有出现很多问题)

<?php
class format(){
    static function minify_css($text){
        $from   = array(
        //                  '%(#|;|(//)).*%',               // comments:  # or //
            '%/\*(?:(?!\*/).)*\*/%s',       // comments:  /*...*/
            '/\s{2,}/',                     // extra spaces
            "/\s*([;{}])[\r\n\t\s]/",       // new lines
            '/\\s*;\\s*/',                  // white space (ws) between ;
            '/\\s*{\\s*/',                  // remove ws around {
            '/;?\\s*}\\s*/',                // remove ws around } and last semicolon in declaration block
            //                  '/:first-l(etter|ine)\\{/',     // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
        //                  '/((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value\\s+/x',     // Use newline after 1st numeric value (to limit line lengths).
        //                  '/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i',
        );
        $to     = array(
        //                  '',
            '',
            ' ',
            '$1',
            ';',
            '{',
            '}',
            //                  ':first-l$1 {',
        //                  "$1\n",
        //                  '$1#$2$3$4$5',
        );
        $text   = preg_replace($from,$to,$text);
        return $text;
    }
    static function minify_js($text){
        $file_cache     = strtolower(md5($text));
        $folder         = TMPPATH.'tmp_files'.DIRECTORY_SEPARATOR.substr($file_cache,0,2).DIRECTORY_SEPARATOR;
        if(!is_dir($folder))            @mkdir($folder, 0766, true);
        if(!is_dir($folder)){
            echo 'Impossible to create the cache folder:'.$folder;
            return 1;
        }
        $file_cache     = $folder.$file_cache.'_content.js';
        if(!file_exists($file_cache)){
            if(strlen($text)<=100){
                $contents = $text;
            } else {
                $contents = '';
                $post_text = http_build_query(array(
                                'js_code' => $text,
                                'output_info' => 'compiled_code',//($returnErrors ? 'errors' : 'compiled_code'),
                                'output_format' => 'text',
                                'compilation_level' => 'SIMPLE_OPTIMIZATIONS',//'ADVANCED_OPTIMIZATIONS',//'SIMPLE_OPTIMIZATIONS'
                            ), null, '&');
                $URL            = 'http://closure-compiler.appspot.com/compile';
                $allowUrlFopen  = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
                if($allowUrlFopen){
                    $contents = file_get_contents($URL, false, stream_context_create(array(
                            'http'          => array(
                                'method'        => 'POST',
                                'header'        => 'Content-type: application/x-www-form-urlencoded',
                                'content'       => $post_text,
                                'max_redirects' => 0,
                                'timeout'       => 15,
                            )
                    )));
                }elseif(defined('CURLOPT_POST')) {
                    $ch = curl_init($URL);
                    curl_setopt($ch, CURLOPT_POST, true);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
                    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_text);
                    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
                    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
                    $contents = curl_exec($ch);
                    curl_close($ch);
                } else {
                    //"Could not make HTTP request: allow_url_open is false and cURL not available"
                    $contents = $text;
                }
                if($contents==false || (trim($contents)=='' && $text!='') || strtolower(substr(trim($contents),0,5))=='error' || strlen($contents)<=50){
                    //No HTTP response from server or empty response or error
                    $contents = $text;
                }
            }
            if(trim($contents)!=''){
                $contents = trim($contents);
                $f = fopen($file_cache, 'w');
                fwrite($f, $contents);
                fclose($f);
            }
        } else {
            touch($file_cache);     //in the future I will add a timetout to the cache
            $contents = file_get_contents($file_cache);
        }
        return $contents;
    }
    static function minify_html($text){
        if(isset($_GET['no_mini'])){
            return $text;
        }
        $file_cache     = strtolower(md5($text));
        $folder         = TMPPATH.'tmp_files'.DIRECTORY_SEPARATOR.substr($file_cache,0,2).DIRECTORY_SEPARATOR;
        if(!is_dir($folder))            @mkdir($folder, 0766, true);
        if(!is_dir($folder)){
            echo 'Impossible to create the cache folder:'.$folder;
            return 1;
        }
        $file_cache     = $folder.$file_cache.'_content.html';
        if(!file_exists($file_cache)){
            //get CSS and save it
            $search_css = '/<\s*style\b[^>]*>(.*?)<\s*\/style>/is';
            $ret = preg_match_all($search_css, $text, $tmps);
            $t_css = array();
            if($ret!==false && $ret>0){
                foreach($tmps as $k=>$v){
                    if($k>0){
                        foreach($v as $kk=>$vv){
                            $t_css[] = $vv;
                        }
                    }
                }
            }
            $css = format::minify_css(implode('\n', $t_css));

/*
            //get external JS and save it
            $search_js_ext = '/<\s*script\b.*?src=\s*[\'|"]([^\'|"]*)[^>]*>\s*<\s*\/script>/i';
            $ret = preg_match_all($search_js_ext, $text, $tmps);
            $t_js = array();
            if($ret!==false && $ret>0){
                foreach($tmps as $k=>$v){
                    if($k>0){
                        foreach($v as $kk=>$vv){
                            $t_js[] = $vv;
                        }
                    }
                }
            }
            $js_ext = $t_js;
*/
            //get inline JS and save it
            $search_js_ext  = '/<\s*script\b.*?src=\s*[\'|"]([^\'|"]*)[^>]*>\s*<\s*\/script>/i';
            $search_js      = '/<\s*script\b[^>]*>(.*?)<\s*\/script>/is';
            $ret            = preg_match_all($search_js, $text, $tmps);
            $t_js           = array();
            $js_ext         = array();
            if($ret!==false && $ret>0){
                foreach($tmps as $k=>$v){
                    if($k==0){
                        //let's check if we have a souce (src="")
                        foreach($v as $kk=>$vv){
                            if($vv!=''){
                                $ret = preg_match_all($search_js_ext, $vv, $ttmps);
                                if($ret!==false && $ret>0){
                                    foreach($ttmps[1] as $kkk=>$vvv){
                                        $js_ext[] = $vvv;
                                    }
                                }
                            }
                        }
                    } else {
                        foreach($v as $kk=>$vv){
                            if($vv!=''){
                                $t_js[] = $vv;
                            }
                        }
                    }
                }
            }
            $js = format::minify_js(implode('\n', $t_js));

            //get inline noscript and save it
            $search_no_js = '/<\s*noscript\b[^>]*>(.*?)<\s*\/noscript>/is';
            $ret = preg_match_all($search_no_js, $text, $tmps);
            $t_js = array();
            if($ret!==false && $ret>0){
                foreach($tmps as $k=>$v){
                    if($k>0){
                        foreach($v as $kk=>$vv){
                            $t_js[] = $vv;
                        }
                    }
                }
            }
            $no_js = implode('\n', $t_js);

            //remove CSS and JS
            $search = array(
                $search_js_ext,
                $search_css,
                $search_js,
                $search_no_js,
                '/\>[^\S ]+/s', //strip whitespaces after tags, except space
                '/[^\S ]+\</s', //strip whitespaces before tags, except space
                '/(\s)+/s',  // shorten multiple whitespace sequences
            );
            $replace = array(
                '',
                '',
                '',
                '',
                '>',
                '<',
                '\\1',
            );
            $buffer = preg_replace($search, $replace, $text);

            $append = '';
            //add CSS and JS at the bottom
            if(is_array($js_ext) && count($js_ext)>0){
                foreach($js_ext as $k=>$v){
                    $append .= '<script type="text/javascript" language="javascript" src="'.$v.'" ></script>';
                }
            }
            if($css!='')        $append .= '<style>'.$css.'</style>';
            if($js!=''){
                //remove weird '\n' strings
                $js = preg_replace('/[\s]*\\\n/', "\n", $js);
                $append .= '<script>'.$js.'</script>';
            }
            if($no_js!='')      $append .= '<noscript>'.$no_js.'</noscript>';
            $buffer = preg_replace('/(.*)(<\s*\/\s*body\s*>)(.*)/','\\1'.$append.'\\2\\3', $buffer);
            if(trim($buffer)!=''){
                $f = fopen($file_cache, 'w');
                fwrite($f, trim($buffer));
                fclose($f);
            }
        } else {
            touch($file_cache);     //in the future I will add a timetout to the cache
            $buffer = file_get_contents($file_cache);
        }

        return $buffer;
    }

}
?>

我认为在PHP中优化HTML的最大用处是HTML通常是动态的,因此虽然我可以从bash构建脚本中缩小JS和CSS,但我无法对HTML进行这样的操作。也就是说,这是每个请求都需要完成的任务。 - Camilo Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接