之前接确Mootools时,发现其官方网站的下载页面提供定制下载功能。除了定制包之外,还可定制所下载脚本的压缩比,对缩后的脚本体积更小,但能正确在主流浏览器中运行。对在流量大的网站来说,更小的页面体积(包括HTML、JavaScript、CSS等)意味着可以节省更多的服务器资源、网络带宽和流量,因此JavaScript的压缩在Web开发中有着重要的意义。该压缩功能是用一个叫“JavaScript Packer”的类库实现的,原版由Dean Edwards提供,其PHP版本可在http://joliclic.free.fr/php/javascript-packer/en/下载得到,由于用了相关的面向对象特性,所以该版本只支持PHP 5。
该类库的使用方法很简单,很看一个最简单的例子:
<?php
require ‘class.JavaScriptPacker.php’;//导入包
$packer = new JavaScriptPacker($script);//构造压缩器对象
$result = $packer->pack();//执行压缩并返回结果
?>
以上例子中,变量$script代表要所压缩的JavaScript脚本(字符串形式),$result而存放了压缩后的脚本。我们就拿Mootools的库来做实验,从官方下载某版本的所有包,未经压缩的源代码一共是139kb,使用默认参数压缩后,得到的大小为35.1kb,经测试,其功能能正常使用。
JavaScriptPacker这个类的构造函数还可以接受其它的参数,实现对压缩行为的更详细定制。先看看其函数原形
public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false)
一共有四个参数:$_script是输入的脚本字符串;$_encoding是压缩比,可接受0,10,62,95四个压缩级别,默认值62;$_fastDecode是快速解压器的开关,打开后会略为增加文件大小,但能提高脚本运行速度,默认true;$_specialChars用于指示脚本中是否有特殊字符,默认false。
知道了以上这些,我们就可以写出相关的脚本,帮助我们定制压缩JavaScript。以下是我写好的两个版本的脚本,读者可以直接保存使用。前者是在web服务器中运行的,接受上传一个脚本,压缩返还给浏览器下载。后者是可运行在系统控制台的脚本,配置好PHP CLI的运行环境之后,调用php console.php input.js output.js即可使用默认参数把input.js文件压缩并输出为output.js,而使用php console.php则可查看使用方法。
web.php 运行于Web服务器上版本
<?php
error_reporting(0);
require 'class.JavaScriptPacker.php';
if (!empty(
$_FILES['source_file'])) {
$_POST['encoding'] = isset($_POST['encoding']) ? (int) $_POST['encoding'] : 62;
$_POST['fastdecode'] = isset($_POST['fastdecode']) ? (bool) $_POST['fastdecode'] : true;
$_POST['specialchars'] = isset($_POST['specialchars']) ? (bool) $_POST['specialchars'] : true;
$packer = new JavaScriptPacker(file_get_contents($_FILES['source_file']['tmp_name']), $_POST['encoding'], $_POST['fastdecode'], $_POST['specialchars']);
if (
preg_match('/^(.*)\.js$/i', $_FILES['source_file']['name'], $matches) == 0) {
$matches = array('none.js', 'none');
}
header('Content-Type: application/x-javascript', true);
header('Content-Disposition: attachment; filename='.$matches[1].'_packed.js');
echo $packer->pack();
exit;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JavaScript Packer</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data" name="file_form" id="file_form">
<input type="file" name="source_file" id="source_file" />
<select name="encoding" id="encoding">
<option value="0">None</option>
<option value="10">Numeric</option>
<option value="62" selected="selected">Normal</option>
<option value="95">High ASCII</option>
</select>
<input type="checkbox" name="fastdecode" value="true" id="fastdecode" />
<label for="fastdecode">fast deconde</label>
<input type="checkbox" name="specialchars" value="true" id="specialchars" />
<label for="specialchars">special characters</label>
<input type="submit" name="submit_button" value="start pack" id="submit_button" />
</form>
</body>
</html>
console.php 运行于控制台的脚本
<?php
error_reporting(0);
$help = <<<EOF
Usage:
$argv[0] [options] <input_file> <output_file>
Options:
-e int level of encoding
0: None, 10: Numeric, 62: Normal, 95: High ASCII
default: 62
-f bool include the fast decoder in the packed result
0: false, 1: true
default : true
-s bool if you are flagged your private and local variables in the script
0: false, 1: true
default : false
EOF;
function
packJavaScript($script, $output, $encoding, $fastDecode, $specialChars) {
include_once 'class.JavaScriptPacker.php';
$packer = new JavaScriptPacker(file_get_contents($script), $encoding, $fastDecode, $specialChars);
file_put_contents($output, $packer->pack());
}
if (
$argc < 3) {
echo $help;
exit;
} elseif (!file_exists($_SERVER['argv'][$argc - 2])) {
echo "\nError, your input file dose not exist!\n\n";
echo $help;
exit;
} elseif (file_exists($_SERVER['argv'][$argc -1])) {
$input_stream = fopen('php://stdin','r');
echo "\nOutput file ".$_SERVER['argv'][$argc - 1]." already exist, cancle process(c), rename(r), or override(o)?(a/r/o) ";
$action = fread($input_stream, 1);
if (strcasecmp($action, 'a') == 0) {
echo "\nprogram terminated\n";
exit;
} elseif (strcasecmp($action, 'r') == 0) {
echo "\nPlease input new file name, less than 255 characters: ";
$output = trim(fread($input_stream, 255));
}
fclose($input_stream);
}
$script = $_SERVER['argv'][$argc - 2];
$output = !isset($output) ? $_SERVER['argv'][$argc -1] : $output;
$encoding = ($index = array_search('-e', $_SERVER['argv'])) !== false ? (int) $_SERVER['argv'][$index + 1] : 62;
$fastDecode = ($index = array_search('-f', $_SERVER['argv'])) !== false ? (bool) $_SERVER['argv'][$index + 1] : true;
$specialChars = ($index = array_search('-s', $_SERVER['argv'])) !== false ? (bool) $_SERVER['argv'][$index + 1] : false;
packJavaScript($script, $output, $encoding, $fastDecode, $specialChars);
echo
"\nOriginal file size:\t".filesize($script)."\tbytes";
echo "\nCompressed file size:\t".filesize($output)."\tbytes\n";
?>
