PHP-php程序实现css文件和js文件的压缩
那几天优化公司网站的时候
一直在寻找php程序可以直接将css和js压缩并且可以正常运行
看到有很多网站的插件支持压缩,
我想做个我的项目里自动压缩的程序
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
那几天优化公司网站的时候
一直在寻找php程序可以直接将css和js压缩并且可以正常运行
看到有很多网站的插件支持压缩,
我想做个我的项目里自动压缩的程序
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(6)
提供一个程序
<?php
/**
* 类 WebPuts 用于向浏览器直接输出数据(例如下载文件)
* */
class WebPuts
{
/**
* 所有要输出的内容
*
* @var array
*/
protected $_output = array();
/**
* 输出文件名
*
* @var string
*/
protected $_output_filename;
/**
* 输出类型
*
* @var string
*/
protected $_mime_type;
/**
* 输出文件名的字符集
*
* @var string
*/
protected $_filename_charset = 'utf-8';
/**
* 允许客户端缓存输出的文件
*
* @var boolean
*/
protected $_enabled_client_cache = true;
/**
* 构造函数
*
* @param string $output_filename
* @param string $mime_type
* @param string $content
*/
function __construct($output_filename, $mime_type = 'application/octet-stream', $content = null)
{
$this->_output_filename = $output_filename;
$this->_mime_type = $mime_type;
if ($content) { $this->appendData($content); }
}
/**
* 添加一个要输出的文件
*
* @param string $filename
*
* @return WebPuts
*/
function addFile($filename)
{
$this->_output[] = array('file', $filename);
return $this;
}
/**
* 追加要输出的数据
*
* @param string $content
*
* @return WebPuts
*/
function appendData($content)
{
$this->_output[] = array('raw', $content);
return $this;
}
/**
* 设置输出文件名
*
* @param string $output_filename
*
* @return WebPuts
*/
function setOutputFilename($output_filename)
{
$this->_output_filename = $output_filename;
return $this;
}
/**
* 设置输出文件名的编码
*
* @param string $charset
*
* @return WebPuts
*/
function setOutputFilenameCharset($charset)
{
$this->_filename_charset = $charset;
return $this;
}
/**
* 设置是否允许客户端缓存输出的文件
*
* @param boolean $enabled
*
* @return WebPuts
*/
function enableClientCache($enabled = true)
{
$this->_enabled_client_cache = $enabled;
return $this;
}
/**
* 设置输出类型
*
* @param string $mime_type
*
* @return WebPuts
*/
function setMimeType($mime_type)
{
$this->_mime_type = $mime_type;
return $this;
}
/**
* 执行响应
*/
function execute()
{
header("Content-Type: {$this->_mime_type}");
$filename = '"' . htmlspecialchars($this->_output_filename) . '"';
$filesize = 0;
foreach ($this->_output as $output)
{
list($type, $data) = $output;
if ($type == 'file')
{
$filesize += filesize($data);
}
else
{
$filesize += strlen($data);
}
}
header("Content-Disposition: attachment; filename={$filename}; charset={$this->_filename_charset}");
if ($this->_enabled_client_cache)
{
header('Pragma: cache');
}
header('Cache-Control: public, must-revalidate, max-age=0');
header("Content-Length: {$filesize}");
foreach ($this->_output as $output) {
list($type, $data) = $output;
if ($type == 'file') {
readfile($data);
} else {
echo $data;
}
}
}
}
推荐minify,已经移到github了:
https://github.com/mrclay/minify
<?php
ob_start("ob_gzhandler");
header("Content-type: text/css; charset: UTF-8");
header("Expires: ".gmdate("D, d M Y H:i:s", time() + 60*60)." GMT");
include('common.css');
echo "nn";
include('another.css');
echo "nn";
ob_flush();
?>
上面是压缩css的,同样如果多个js文件,也按上面处理!
我们之前刚做完这个,使用的是一套开源的程序,minfy(http://code.google.com/p/minify/),后端用了Redis缓存(切记MC不可以,因为mc的value最大只支持1M),并且加了CDN缓存。其实淘宝的Tengine是更好的选择。
在存放CSS的文件夹中新建一个style.php文件,在此文件中加入以下代码:
<?php
if(extension_loaded('zlib')){//检查服务器是否开启了zlib拓展
ob_start('ob_gzhandler');
}
header ("content-type: text/css; charset: gb2312");//注意修改到你的编码
header ("cache-control: must-revalidate");
$offset = 60 * 60 * 24;//css文件的距离现在的过期时间,这里设置为一天
$expire = "expires: " . gmdate ("D, d M Y H:i:s", time() + $offset) . " GMT";
header ($expire);
ob_start("compress");
function compress($buffer) {//去除文件中的注释和空行
$patterns = array('!/*[^*]**+([^/][^*]**+)*/!', '/n[s| ]*r/',);
$buffer = preg_replace($patterns, '', $buffer);
return $buffer;
}
//包含你的全部css文档
include('global.css');
include('layout.css');
if(extension_loaded('zlib')){
ob_end_flush();//输出buffer中的内容,即压缩后的css文件
}
?>
如果你处理的是JavaScript文件,你需要将上面代码中的第5行的Content-type修改成以下:
header ("content-type:application/x-javascript; charset: gb2312");
同样需要注意的是文件的编码,这里我用的是gb2312,如果你采用的是UTF-8或其他编码,修改成对应的即可。
修改完成之后,在原引入CSS和JS文件的地方,将.css后缀/.js后缀的文件更换成这个style.php文件即可,如:
<script type="text/javascript" src="http://www.ppt52.net/scripts/autoSuggest.js.php?v=121></script>
由于上面代码中使用到了HTTP的Expires(过期)属性用于在客户端缓存CSS/JS代码,所以,如果过期时间设置的太长(比如2020 年),当你在服务器端修改了JS/CSS代码时,客户端可能不会立即生效。解决办法是:在php文件后面添加一个随机参数,如上面例子中的v=121,当下次修改了文件时,记得相应修改此随机参数即可。
压缩js
利用jsmin类
compress.php
header('Content-type: text/javascript');
require 'jsmin.php';
echo JSMin::minify(file_get_contents('common.js') . file_get_contents('common2.js'));
common.js
alert('first js');
common2.js
alert('second js');
jsmin.php
<?php
/**
* jsmin.php - extended PHP implementation of Douglas Crockford's JSMin.
*
* <code>
* $minifiedJs = JSMin::minify($js);
* </code>
*
* This is a direct port of jsmin.c to PHP with a few PHP performance tweaks and
* modifications to preserve some comments (see below). Also, rather than using
* stdin/stdout, JSMin::minify() accepts a string as input and returns another
* string as output.
*
* Comments containing IE conditional compilation are preserved, as are multi-line
* comments that begin with "/*!" (for documentation purposes). In the latter case
* newlines are inserted around the comment to enhance readability.
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com> (PHP port)
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
const ACTION_KEEP_A = 1;
const ACTION_DELETE_A = 2;
const ACTION_DELETE_A_B = 3;
protected $a = "n";
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
/**
* Minify Javascript
*
* @param string $js Javascript to be minified
* @return string
*/
public static function minify($js)
{
// look out for syntax like "++ +" and "- ++"
$p = '\+';
$m = '\-';
if (preg_match("/([$p$m])(?:\1 [$p$m]| (?:$p$p|$m$m))/", $js)) {
// likely pre-minified and would be broken by JSMin
return $js;
}
$jsmin = new JSMin($js);
return $jsmin->min();
}
/*
* Don't create a JSMin instance, instead use the static function minify,
* which checks for mb_string function overloading and avoids errors
* trying to re-minify the output of Closure Compiler
*
* @private
*/
public function __construct($input)
{
$this->input = $input;
}
/**
* Perform minification, return result
*/
public function min()
{
if ($this->output !== '') { // min already run
return $this->output;
}
$mbIntEnc = null;
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
$mbIntEnc = mb_internal_encoding();
mb_internal_encoding('8bit');
}
$this->input = str_replace("rn", "n", $this->input);
$this->inputLength = strlen($this->input);
$this->action(self::ACTION_DELETE_A_B);
while ($this->a !== null) {
// determine next command
$command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') {
if (! $this->isAlphaNum($this->b)) {
$command = self::ACTION_DELETE_A;
}
} elseif ($this->a === "n") {
if ($this->b === ' ') {
$command = self::ACTION_DELETE_A_B;
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
} elseif ($this->b === null
|| (false === strpos('{[(+-', $this->b)
&& ! $this->isAlphaNum($this->b))) {
$command = self::ACTION_DELETE_A;
}
} elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' '
|| ($this->b === "n"
&& (false === strpos('}])+-"'', $this->a)))) {
$command = self::ACTION_DELETE_A_B;
}
}
$this->action($command);
}
$this->output = trim($this->output);
if ($mbIntEnc !== null) {
mb_internal_encoding($mbIntEnc);
}
return $this->output;
}
/**
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
* ACTION_DELETE_A = Copy B to A. Get the next B.
* ACTION_DELETE_A_B = Get the next B.
*/
protected function action($command)
{
switch ($command) {
case self::ACTION_KEEP_A:
$this->output .= $this->a;
// fallthrough
case self::ACTION_DELETE_A:
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') { // string literal
$str = $this->a; // in case needed for exception
while (true) {
$this->output .= $this->a;
$this->a = $this->get();
if ($this->a === $this->b) { // end quote
break;
}
if (ord($this->a) <= self::ORD_LF) {
throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte "
. $this->inputIndex . ": {$str}");
}
$str .= $this->a;
if ($this->a === '\') {
$this->output .= $this->a;
$this->a = $this->get();
$str .= $this->a;
}
}
}
// fallthrough
case self::ACTION_DELETE_A_B:
$this->b = $this->next();
if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
$this->output .= $this->a . $this->b;
$pattern = '/'; // in case needed for exception
while (true) {
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === '/') { // end pattern
break; // while (true)
} elseif ($this->a === '\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
} elseif (ord($this->a) <= self::ORD_LF) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte "
. $this->inputIndex .": {$pattern}");
}
$this->output .= $this->a;
}
$this->b = $this->next();
}
// end case ACTION_DELETE_A_B
}
}
protected function isRegexpLiteral()
{
if (false !== strpos("n{;(,=:[!&|?", $this->a)) { // we aren't dividing
return true;
}
if (' ' === $this->a) {
$length = strlen($this->output);
if ($length < 2) { // weird edge case
return true;
}
// you can't divide a keyword
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
if ($this->output === $m[0]) { // odd but could happen
return true;
}
// make sure it's a keyword, not end of an identifier
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
if (! $this->isAlphaNum($charBeforeKeyword)) {
return true;
}
}
}
return false;
}
/**
* Get next char. Convert ctrl char to space.
*/
protected function get()
{
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
} else {
return null;
}
}
if ($c === "r" || $c === "n") {
return "n";
}
if (ord($c) < self::ORD_SPACE) { // control char
return ' ';
}
return $c;
}
/**
* Get next char. If is ctrl character, translate to a space or newline.
*/
protected function peek()
{
$this->lookAhead = $this->get();
return $this->lookAhead;
}
/**
* Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
*/
protected function isAlphaNum($c)
{
return (preg_match('/^[0-9a-zA-Z_\$\\]$/', $c) || ord($c) > 126);
}
protected function singleLineComment()
{
$comment = '';
while (true) {
$get = $this->get();
$comment .= $get;
if (ord($get) <= self::ORD_LF) { // EOL reached
// if IE conditional comment
if (preg_match('/^\/@(?:cc_on|if|elif|else|end)\b/', $comment)) {
return "/{$comment}";
}
return $get;
}
}
}
protected function multipleLineComment()
{
$this->get();
$comment = '';
while (true) {
$get = $this->get();
if ($get === '*') {
if ($this->peek() === '/') { // end of comment reached
$this->get();
// if comment preserved by YUI Compressor
if (0 === strpos($comment, '!')) {
return "n/*" . substr($comment, 1) . "*/n";
}
// if IE conditional comment
if (preg_match('/^@(?:cc_on|if|elif|else|end)\b/', $comment)) {
return "/*{$comment}*/";
}
return ' ';
}
} elseif ($get === null) {
throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte "
. $this->inputIndex . ": /*{$comment}");
}
$comment .= $get;
}
}
/**
* Get the next character, skipping over comments.
* Some comments may be preserved.
*/
protected function next()
{
$get = $this->get();
if ($get !== '/') {
return $get;
}
switch ($this->peek()) {
case '/': return $this->singleLineComment();
case '*': return $this->multipleLineComment();
default: return $get;
}
}
}
class JSMin_UnterminatedStringException extends Exception {}
class JSMin_UnterminatedCommentException extends Exception {}
class JSMin_UnterminatedRegExpException extends Exception {}
使用
<script type="text/javascript" src="fonts.php"></script>