PHP压缩HTML-使用正则安全的压缩
对于没有办法开启gzip等个人空间服务器,可以通过压缩静态网页中的HTML代码达到减轻宽带浪费、加快显示等,使用PHP就可以轻易的达到目的 ...
分析HTML代码
HTML一般包括以下的部分:
- 标签,以<>包裹
- 文档声明、注释等<!...>格式
- 半开标签<..../>
- 闭合标签</..>
- 内容,非以<>包裹,这一部分内容中多空格是无效果的
PHP正则分解html代码
html代码是以标签为界限的,所以,只要按标签分解就可以了。在PHP中使用preg_split:
$segments = preg_split("/(<[^>]+?>)/si",$html, null,PREG_SPLIT_NO_EMPTY| PREG_SPLIT_DELIM_CAPTURE); 例如,以下的代码
$html = <<<HTML
<!doctype html>
<html>
<head>
<title>狼魂博客</title>
<meta charset="utf-8">
<meta name="description" content="关注WEB,体悟生活;珍惜生命,远离代码。">
</head>
<body>
<div class="wrap">
<a class="rss" href="http://pjiaxu.com/rss.xml">文章RSS</a>
<a class="rss" href="http://pjiaxu.com/map.html">网站地图</a>
<a class="rss" href="http://pjiaxu.com/archives.html">日期归档</a>
<a class="rss" href="http://pjiaxu.com/tags.html">标签归档</a>
<h2 id="logo"><a href="http://pjiaxu.com/" title="狼魂博客">狼魂博客</a></h2>
<p id="webdesc">关注WEB,体悟生活;珍惜生命,远离代码。</p>
<div class="clear"></div>
</div>
</body>
</html>
HTML;
print_r(preg_split("/(<[^>]+?>)/si",$html, -1,PREG_SPLIT_NO_EMPTY| PREG_SPLIT_DELIM_CAPTURE)); 会产生如下的输出:
Array
(
[0] => <!doctype html>
[1] =>
[2] => <html>
[3] =>
[4] => <head>
[5] =>
[6] => <title>
[7] => 狼魂博客
[8] => </title>
[9] =>
[10] => <meta charset="utf-8">
[11] =>
[12] => <meta name="description" content="关注WEB,体悟生活;珍惜生命,远离代码。">
[13] =>
[14] => </head>
[15] =>
[16] => <body>
[17] =>
[18] => <div class="wrap">
[19] =>
[20] => <a class="rss" href="http://pjiaxu.com/rss.xml">
[21] => 文章RSS
[22] => </a>
[23] =>
[24] => <a class="rss" href="http://pjiaxu.com/map.html">
[25] => 网站地图
[26] => </a>
[27] =>
[28] => <a class="rss" href="http://pjiaxu.com/archives.html">
[29] => 日期归档
[30] => </a>
[31] =>
[32] => <a class="rss" href="http://pjiaxu.com/tags.html">
[33] => 标签归档
[34] => </a>
[35] =>
[36] => <h2 id="logo">
[37] => <a href="http://pjiaxu.com/" title="狼魂博客">
[38] => 狼魂博客
[39] => </a>
[40] => </h2>
[41] =>
[42] => <p id="webdesc">
[43] => 关注WEB,体悟生活;珍惜生命,远离代码。
[44] => </p>
[45] =>
[46] => <div class="clear">
[47] => </div>
[48] =>
[49] => </div>
[50] =>
[51] => </body>
[52] =>
[53] => </html>
)
最简单但有损的PHP压缩
最简单的压缩就是直接连接所有的非空项,同时非标签去掉所有的空白:
$compressed = array();
foreach($segments as $seg)
{
$seg = trim($seg);
if($seg)
{
//非标签中的空白是无效的字符
$compressed[] = $seg[0] === '<' ? $seg : preg_replace('!\s!','',$seg);
}
}
return join('',$compress);
如以上的HTML通过这样的压缩生成的代码是(为了方便显示,我手动将它们断行了):
<!doctype html><html><head><title>狼魂博客</title><meta charset="utf-8"> <meta name="description" content="关注WEB,体悟生活;珍惜生命,远离代码。"></head> <body><div class="wrap"><a class="rss" href="http://pjiaxu.com/rss.xml">文章RSS</a> <a class="rss" href="http://pjiaxu.com/map.html">网站地图</a> <a class="rss" href="http://pjiaxu.com/archives.html">日期归档</a> <a class="rss" href="http://pjiaxu.com/tags.html">标签归档</a><h2 id="logo"> <a href="http://pjiaxu.com/" title="狼魂博客">狼魂博客</a></h2> <p id="webdesc">关注WEB,体悟生活;珍惜生命,远离代码。</p><div class="clear"> </div></div></body></html>正常情况下这没错,但是也有“不正常”的情况:遇到不能去掉空白的内容时。比如script、code、pre、style是不可以去掉空白的,这时,就要使用栈进行压缩了:
使用栈进行安全压缩html
使用栈的规则是:<..>标签入栈,</..>标签出栈,<!..>和<../>不理,但有一种可能的情况是,<../>不一定有结尾的反斜杠如:
<meta> <meta/>都是可以的,这时就要特殊的处理这种情况:
<?php
$html = <<<HTML
<body>
<div class="wrap">
<a class="rss" href="http://pjiaxu.com/rss.xml">文章RSS</a>
<a class="rss" href="http://pjiaxu.com/map.html">网站地图</a>
<a class="rss" href="http://pjiaxu.com/archives.html">日期归档</a>
<a class="rss" href="http://pjiaxu.com/tags.html">标签归档</a>
<h2 id="logo"><a href="http://pjiaxu.com/" title="狼魂博客">狼魂博客</a></h2>
<p id="webdesc">关注WEB,体悟生活;珍惜生命,远离代码。</p>
<div class="clear"></div>
</div>
<pre>
var say = "Hello world!";
print say;
</pre>
</body>
HTML;
$segments = preg_split("/(<[^>]+?>)/si",$html, -1,PREG_SPLIT_NO_EMPTY| PREG_SPLIT_DELIM_CAPTURE);
$compressed = array();
$stack = array();
$tag = '';
$half_open = array('meta','input','link','img','br');
$cannot_compress = array('pre','code','script','style');
foreach($segments as $seg)
{
if(trim($seg) === '')
{
continue;
}
//<.../>
if(preg_match("!<([a-z0-9]+)[^>]*?/>!si",$seg, $match))
{
//$tag = self::format_tag($match[1]);
format_tag($match[1]);
$compressed[] = $seg;
}
else if(preg_match("!</([a-z0-9]+)[^>]*?>!si",$seg,$match))//</..>
{
$tag = format_tag($match[1]);
if(count($stack) > 0 && $stack[count($stack)-1] == $tag)
{
array_pop($stack);
$compressed[] = $seg;
}
//这里再最好加一段判断,可以用于修复错误的html
//...
}
else if(preg_match("!<([a-z0-9]+)[^>]*?>!si",$seg,$match))//<>
{
$tag = format_tag($match[1]);
//半闭合标签不需要入栈,如<br/>,<img/>
if(!in_array($tag, $half_open))
{
array_push($stack,$tag);
}
$compressed[] = $seg;
}
else if(preg_match("~<![^>]*>~", $seg))
{
//文档声明和注释,注释也不能删除,如<!--ie条件-->
$compressed[] = $seg;
}
else
{
$compressed[] = in_array($tag, $cannot_compress) ? $seg : preg_replace('!\s!', '', $seg);
}
}
function format_tag($tag)
{
return trim(strtolower($tag));
}
echo join('',$compressed); 以上的代码产生如下的输出(为了方便显示,我手工断行了):
<body><div class="wrap"><a class="rss" href="http://pjiaxu.com/rss.xml">文章RSS</a> <a class="rss" href="http://pjiaxu.com/map.html">网站地图</a> <a class="rss" href="http://pjiaxu.com/archives.html">日期归档</a> <a class="rss" href="http://pjiaxu.com/tags.html">标签归档</a><h2 id="logo"> <a href="http://pjiaxu.com/" title="狼魂博客">狼魂博客</a></h2><p id="webdesc"> 关注WEB,体悟生活;珍惜生命,远离代码。</p><div class="clear"></div></div><pre> var say = "Hello world!"; print say; </pre></body>
最安全的HTML压缩
当然,以上的代码正确运行的基础是:HTML都是正确合法的,没有出现如<a><b></a></b>等,但是在普通应用是没有问题的,如果需要安全的压缩HTML代码,可以使用HTML解析库进行修复并进行压缩。
原文链接:php使用正则轻度压缩html代码
$html = <<<HTML
<body>
<div class="wrap">
<a class="rss" href="http://pjiaxu.com/rss.xml">文章RSS</a>
<a class="rss" href="http://pjiaxu.com/map.html">网站地图</a>
<a class="rss" href="http://pjiaxu.com/archives.html">日期归档</a>
<a class="rss" href="http://pjiaxu.com/tags.html">标签归档</a>
<h2 id="logo"><a href="http://pjiaxu.com/" title="狼魂博客">狼魂博客</a></h2>
<p id="webdesc">关注WEB,体悟生活;珍惜生命,远离代码。</p>
<div class="clear"></div>
</div>
<pre>
var say = "Hello world!";
print say;
</pre>
</body>
HTML;
$segments = preg_split("/(<[^>]+?>)/si",$html, -1,PREG_SPLIT_NO_EMPTY| PREG_SPLIT_DELIM_CAPTURE);
$compressed = array();
$stack = array();
$tag = '';
$half_open = array('meta','input','link','img','br');
$cannot_compress = array('pre','code','script','style');
foreach($segments as $seg)
{
if(trim($seg) === '')
{
continue;
}
//<.../>
if(preg_match("!<([a-z0-9]+)[^>]*?/>!si",$seg, $match))
{
//$tag = self::format_tag($match[1]);
format_tag($match[1]);
$compressed[] = $seg;
}
else if(preg_match("!</([a-z0-9]+)[^>]*?>!si",$seg,$match))//</..>
{
$tag = format_tag($match[1]);
if(count($stack) > 0 && $stack[count($stack)-1] == $tag)
{
array_pop($stack);
$compressed[] = $seg;
}
//这里再最好加一段判断,可以用于修复错误的html
//...
}
else if(preg_match("!<([a-z0-9]+)[^>]*?>!si",$seg,$match))//<>
{
$tag = format_tag($match[1]);
//半闭合标签不需要入栈,如<br/>,<img/>
if(!in_array($tag, $half_open))
{
array_push($stack,$tag);
}
$compressed[] = $seg;
}
else if(preg_match("~<![^>]*>~", $seg))
{
//文档声明和注释,注释也不能删除,如<!--ie条件-->
$compressed[] = $seg;
}
else
{
$compressed[] = in_array($tag, $cannot_compress) ? $seg : preg_replace('!\s!', '', $seg);
}
}
function format_tag($tag)
{
return trim(strtolower($tag));
}
echo join('',$compressed);






