如何压缩 PHP 页面的 HTML 输出?

158
我正在寻找一个能够像谷歌页面速度一样,压缩我的php页面的html输出的php脚本或类。
我该怎么做?

15
基于@RakeshS的答案,一句话翻译:ob_start(function($b){return preg_replace(['/\>[^\S ]+/s','/[^\S ]+\</s','/(\s)+/s'],['>','<','\\1'],$b);});意为使用ob_start()函数和一个匿名函数作为参数,在输出前对文本进行正则表达式替换,将多余的空格和换行符去除,使文本更加紧凑。 - Francisco Presencia
8
这样做真的很糟糕。你正在破坏脚本标签、预格式化标签等。 - Brad
1
这是真的,正如他在回答评论中指出的那样,它不适用于<pre><code>标签,因为它们需要空格来构建正确的结构。但是,<script>应该是外部的通常情况下,或者内联但使用严格的;方式,以便它也能工作。还可能会破坏哪些其他标签@Brad?我想不到其他的了。在我的先前评论之前,我应该添加“快速而粗略的方式”。 - Francisco Presencia
15个回答

237

CSS和Javascript

考虑使用以下链接来压缩Javascript/CSS文件:https://github.com/mrclay/minify

HTML

告诉Apache使用GZip发送HTML-这通常可以将响应大小减少约70%。 (如果您使用Apache,则配置gzip的模块取决于您的版本:Apache 1.3使用mod_gzip,而Apache 2.x使用mod_deflate。)

Accept-Encoding: gzip, deflate

Content-Encoding: gzip

使用以下代码片段结合ob_start的缓冲区来删除HTML中的空白:

<?php

function sanitize_output($buffer) {

    $search = array(
        '/\>[^\S ]+/s',     // strip whitespaces after tags, except space
        '/[^\S ]+\</s',     // strip whitespaces before tags, except space
        '/(\s)+/s',         // shorten multiple whitespace sequences
        '/<!--(.|\s)*?-->/' // Remove HTML comments
    );

    $replace = array(
        '>',
        '<',
        '\\1',
        ''
    );

    $buffer = preg_replace($search, $replace, $buffer);

    return $buffer;
}

ob_start("sanitize_output");

?>

63
这是一个不错的函数,但如果你使用 PRE 标签,请小心,有时会删除其中的换行符。 - fedmich
2
这段代码应该放在脚本的顶部还是底部? - jdepypere
8
你可以使用来自Minify库的Minify_HTML类 ($content = \Minify_HTML::minify($content);),它甚至可以为行内代码添加回调到js/css缩小器。请参见 https://github.com/mrclay/minify/blob/master/min/lib/Minify/HTML.php - Barryvdh
29
这也会破坏内联 JavaScript(即在<script>标签中的JavaScript),如果每个语句末尾没有;或者有使用//的注释。 - Konstantin Pereiaslov
8
这段代码可以去除文本区域、预处理区域、输入框和图像中的空格,但会破坏行内 JavaScript。如果不想使用 DOM 解析的复杂类,可以使用基于正则表达式的这个解决方案(链接在此:https://gist.github.com/tovic/d7b310dea3b33e4732c0)。 - Peter
显示剩余5条评论

29
这对我有效。
function minify_html($html)
{
   $search = array(
    '/(\n|^)(\x20+|\t)/',
    '/(\n|^)\/\/(.*?)(\n|$)/',
    '/\n/',
    '/\<\!--.*?-->/',
    '/(\x20+|\t)/', # Delete multispace (Without \n)
    '/\>\s+\</', # strip whitespaces between tags
    '/(\"|\')\s+\>/', # strip whitespaces between quotation ("') and end tags
    '/=\s+(\"|\')/'); # strip whitespaces between = "'

   $replace = array(
    "\n",
    "\n",
    " ",
    "",
    " ",
    "><",
    "$1>",
    "=$1");

    $html = preg_replace($search,$replace,$html);
    return $html;
}

2
这个代码移除了被接受答案未能移除的空格。谢谢! - Hugo H

28

如果你想正确地使用,可以打开gzip。你也可以像这样做:

$this->output = preg_replace(
    array(
        '/ {2,}/',
        '/<!--.*?-->|\t|(?:\r?\n[ \t]*)+/s'
    ),
    array(
        ' ',
        ''
    ),
    $this->output
);

通过将您的HTML转换为一行,无制表符、无换行符和无注释,可以减少约30%的页面大小。具体效果因情况而异。


1
这样做会进一步减少所需字节的数量。 - Wander Nauta
19
这可能潜在地很危险,因为它会移除IE条件语句……你需要将其更改为/<!--(?!\[if).*?-->/。 - Katai
4
不能正常工作,删除了太多内容,搞乱了代码。之前它符合W3C标准,但现在不符合了。 - Codebeat
3
不幸的是,它也会破坏JavaScript代码,例如用于生成更复杂实现的Google地图 - 这正是我需要这样一个函数的原因。 - richey
1
@dogmatic69 这是不正确的。Gzip 不仅仅是 删除空格,而是寻找重复(无论是什么,甚至包括空格)。Gzip 和代码压缩是互补的。 - fregante
显示剩余6条评论

26

我尝试了几个压缩工具,但它们要么移除太少,要么移除太多。

这段代码可以去除冗余的空格和可选的HTML(结束)标签。同时它保证不会移除任何可能破坏HTML、JS或CSS的内容。

此外,该代码还展示了在Zend框架中如何实现这一功能:

class Application_Plugin_Minify extends Zend_Controller_Plugin_Abstract {

  public function dispatchLoopShutdown() {
    $response = $this->getResponse();
    $body = $response->getBody(); //actually returns both HEAD and BODY

    //remove redundant (white-space) characters
    $replace = array(
        //remove tabs before and after HTML tags
        '/\>[^\S ]+/s'   => '>',
        '/[^\S ]+\</s'   => '<',
        //shorten multiple whitespace sequences; keep new-line characters because they matter in JS!!!
        '/([\t ])+/s'  => ' ',
        //remove leading and trailing spaces
        '/^([\t ])+/m' => '',
        '/([\t ])+$/m' => '',
        // remove JS line comments (simple only); do NOT remove lines containing URL (e.g. 'src="http://server.com/"')!!!
        '~//[a-zA-Z0-9 ]+$~m' => '',
        //remove empty lines (sequence of line-end and white-space characters)
        '/[\r\n]+([\t ]?[\r\n]+)+/s'  => "\n",
        //remove empty lines (between HTML tags); cannot remove just any line-end characters because in inline JS they can matter!
        '/\>[\r\n\t ]+\</s'    => '><',
        //remove "empty" lines containing only JS's block end character; join with next line (e.g. "}\n}\n</script>" --> "}}</script>"
        '/}[\r\n\t ]+/s'  => '}',
        '/}[\r\n\t ]+,[\r\n\t ]+/s'  => '},',
        //remove new-line after JS's function or condition start; join with next line
        '/\)[\r\n\t ]?{[\r\n\t ]+/s'  => '){',
        '/,[\r\n\t ]?{[\r\n\t ]+/s'  => ',{',
        //remove new-line after JS's line end (only most obvious and safe cases)
        '/\),[\r\n\t ]+/s'  => '),',
        //remove quotes from HTML attributes that does not contain spaces; keep quotes around URLs!
        '~([\r\n\t ])?([a-zA-Z0-9]+)="([a-zA-Z0-9_/\\-]+)"([\r\n\t ])?~s' => '$1$2=$3$4', //$1 and $4 insert first white-space character found before/after attribute
    );
    $body = preg_replace(array_keys($replace), array_values($replace), $body);

    //remove optional ending tags (see http://www.w3.org/TR/html5/syntax.html#syntax-tag-omission )
    $remove = array(
        '</option>', '</li>', '</dt>', '</dd>', '</tr>', '</th>', '</td>'
    );
    $body = str_ireplace($remove, '', $body);

    $response->setBody($body);
  }
}

但需要注意的是使用gZip压缩后,你的代码被压缩的程度比任何代码缩小技术都要多,所以结合代码缩小和gZip是没有意义的,因为下载节省的时间会因为代码缩小而损失,同时节省的效果也很有限。

以下是我的测试结果(通过3G网络下载):

 Original HTML:        150kB       180ms download
 gZipped HTML:          24kB        40ms
 minified HTML:        120kB       150ms download + 150ms minification
 min+gzip HTML:         22kB        30ms download + 150ms minification

5
我同意这看起来好像毫无意义,但它可以在谷歌页面速度上为你赢得一两个珍贵的分数,这与你的谷歌排名有关。你的代码很适合去除不必要的空格。谢谢 :-) - Tschallacka
1
这个很好用,但是遇到了 ="/" 的问题,所以我将 / 从'([\r\n\t ])?([a-zA-Z0-9]+)="([a-zA-Z0-9_/\-]+)"([\r\n\t ])?s' 中移除了=>'$1$2=$3$4', // $1 和 $4 插入属性之前/之后找到的第一个空格字符 - Will Bowman
事实上,我不是想删除空格来加快速度,而是因为这样HTML才能正常运行,比如内联块元素,但我也在寻找一个能够忽略需要在前后添加一个空格的元素(例如文本块中的粗体元素)。 - Deji
我发现了一些与Jquery/Foundation相关的问题...除非我注释掉以下这些行: //remove "empty" lines containing only JS's block end character; join with next line (e.g. "}\n}\n</script>" --> "}}</script>" // '/}[\r\n\t ]+/s' => '}', // '/}[\r\n\t ]+,[\r\n\t ]+/s' => '},', - Ian
1
如果您使用服务器端缓存(例如Smarty V3),则min+gzip是一个很好的解决方案,除了第一次调用。因此,如果在第15次调用之后,它将对服务器时间产生利益。 规则= 40x15 =(30x15 + 150)。但对于第二次调用,访问者已经会更快。 - Meloman
序列 ), 存在问题,例如 (CEO), 2015。倒数第二个正则表达式(“删除JS行末的换行符...”)会错误地删除它们之间的空格。 - Ti Hausmann

22

之前所有的preg_replace()解决方案都存在单行注释、条件注释和其他问题。我建议利用经过良好测试的Minify项目而不是自己从头开始创建正则表达式。

在我的情况下,我将以下代码放置在PHP页面的顶部来对其进行缩小:

function sanitize_output($buffer) {
    require_once('min/lib/Minify/HTML.php');
    require_once('min/lib/Minify/CSS.php');
    require_once('min/lib/JSMin.php');
    $buffer = Minify_HTML::minify($buffer, array(
        'cssMinifier' => array('Minify_CSS', 'minify'),
        'jsMinifier' => array('JSMin', 'minify')
    ));
    return $buffer;
}
ob_start('sanitize_output');

2
你的代码没有将HTML放在一行中。 - karadayi
请阅读Minify项目FAQ中的第一个问题。简而言之:忽略它们。 - Andrew
我尝试过了,但它不起作用。在我的 PHP 文件中,我有在 <style> 标签之间的 CSS 和在 <script> 标签之间使用 PHP 嵌入的 JavaScript。 - João Pimentel Ferreira
你把这段代码放在哪里?是放在页脚还是页眉最后面? - Francesco
@francesco 这应该是您页面上的第一行代码。 - Andrew

4

Create a PHP file outside your document root. If your document root is

/var/www/html/

create the a file named minify.php one level above it

/var/www/minify.php

Copy paste the following PHP code into it

<?php
function minify_output($buffer){
    $search = array('/\>[^\S ]+/s','/[^\S ]+\</s','/(\s)+/s');
    $replace = array('>','<','\\1');
    if (preg_match("/\<html/i",$buffer) == 1 && preg_match("/\<\/html\>/i",$buffer) == 1) {
        $buffer = preg_replace($search, $replace, $buffer);
    }
    return $buffer;
}
ob_start("minify_output");?>

Save the minify.php file and open the php.ini file. If it is a dedicated server/VPS search for the following option, on shared hosting with custom php.ini add it.

auto_prepend_file = /var/www/minify.php

参考资料:http://websistent.com/how-to-use-php-to-minify-html-output/

本文介绍如何使用PHP来压缩HTML输出内容。通过减少代码的大小,可以提高网站的加载速度并节省带宽。在这篇文章中,我们将讨论两种方法:手动压缩和使用第三方库。


3

2
您可以了解一下HTML TIDY- http://uk.php.net/tidy
它可以作为一个PHP模块进行安装,正确而安全地剥离空白和所有其他不良因素,同时仍然输出完全有效的HTML / XHTML标记。 它还可以清理您的代码,这可能是好事或坏事,具体取决于您在编写有效代码方面的能力;-)
此外,您可以使用以下代码在文件开头压缩输出:
ob_start('ob_gzhandler');

问题在于该网站将托管在共享服务器上,我将无法访问安装此类模块。 - m3tsys
很有可能已经安装了。检查 phpinfo()... 至少应该安装了 zlib,这样您就可以使用 ob_gzhandler - Rudi Visser
我已经使用了 if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler"); else ob_start();,这不是一样的吗? - m3tsys
2
没错,你真的不需要 else ob_start() 部分,也不需要 gzip 检查... ob_gzhandler 内部检测浏览器是否支持任何压缩方法。只需使用 ob_start('ob_gzhandler'); 就足够了。 - Rudi Visser
@RudiVisser 哦,好主意。当然,并非每个页面的HTML都可以被缓存,这取决于它需要多新鲜,但大多数页面都可以,所以你说得对,这并不重要。顺便说一下,Node.js的Derby框架采用了一种有趣的方法,即剥离HTML中任何不符合规范的标签(如<html>)和属性引号,但我相信只有极少数应用程序真正需要在HTML缩小方面走得那么远。 - Matt Browne
显示剩余2条评论

2
如果您想删除页面中的所有换行符,请使用以下快速代码:
ob_start(function($b){
if(strpos($b, "<html")!==false) {
return str_replace(PHP_EOL,"",$b);
} else {return $b;}
});

PHP_EOL比我之前使用的array("\r\n","\n", "\r")更好,因为它不会从文本区域的内容中删除换行符,而PHP_EOL则不会。谢谢! - Moseleyi

2
我有一个GitHub代码片段,其中包含用于压缩HTML、CSS和JS文件的PHP函数 → https://gist.github.com/taufik-nurrohman/d7b310dea3b33e4732c0 以下是如何使用输出缓冲器实时压缩HTML输出的方法:
<?php

include 'path/to/php-html-css-js-minifier.php';

ob_start('minify_html');

?>

<!-- HTML code goes here ... -->

<?php echo ob_get_clean(); ?>

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