Skip to content

Instantly share code, notes, and snippets.

@ozws
Last active August 19, 2021 02:20
Show Gist options
  • Save ozws/a24544a3df576361d389fdac861aa280 to your computer and use it in GitHub Desktop.
Save ozws/a24544a3df576361d389fdac861aa280 to your computer and use it in GitHub Desktop.
php class Minify
namespace YourNamespace;
class Minify {
/*
Use:
Minify::html( string )
Minify::css( string )
Minify::js( string )
*/
/**
* Constructor.
*
*/
public function __construct() {}
public static function html( $html ) {
if ( trim( $html ) === '' )
return $html;
// Remove extra white-space(s) between HTML attribute(s)
$html = preg_replace_callback('#<([^\/\s<>!]+)(?:\s+([^<>]*?)\s*|\s*)(\/?)>#s', function( $matches ) {
return '<' . $matches[1] . preg_replace('#([^\s=]+)(\=([\'"]?)(.*?)\3)?(\s+|$)#s', ' $1$2', $matches[2]) . $matches[3] . '>';
}, str_replace( "\r", "", $html ) );
// Minify inline CSS declaration(s)
if ( strpos( $html, ' style=' ) !== false ) {
$html = preg_replace_callback('#<([^<]+?)\s+style=([\'"])(.*?)\2(?=[\/\s>])#s', function( $matches ) {
return '<' . $matches[1] . ' style=' . $matches[2] . Minify::css($matches[3]) . $matches[2];
}, $html);
}
// Handle style tags
if ( strpos( $html, '</style>' ) !== false ) {
$html = preg_replace_callback('#<style(.*?)>(.*?)</style>#is', function( $matches ) {
return '<style' . $matches[1] .'>'. Minify::css($matches[2]) . '</style>';
}, $html);
}
// Handle script tags
if ( strpos( $html, '</script>' ) !== false ) {
$html = preg_replace_callback('#<script(.*?)>(.*?)</script>#is', function( $matches ) {
return '<script' . $matches[1] .'>'. Minify::js($matches[2]) . '</script>';
}, $html);
}
$html = preg_replace(
[
// t = text
// o = tag open
// c = tag close
// 1 - Keep important white-space(s) after self-closing HTML tag(s)
'#<(img|input)(>| .*?>)#s',
// 2 - Remove a line break and two or more white-space(s) between tag(s)
'#(<!--.*?-->)|(>)(?:\n*|\s{2,})(<)|^\s*|\s*$#s',
// 3 - t+c || o+t
'#(<!--.*?-->)|(?<!\>)\s+(<\/.*?>)|(<[^\/]*?>)\s+(?!\<)#s',
// 4 - o+o || c+c
'#(<!--.*?-->)|(<[^\/]*?>)\s+(<[^\/]*?>)|(<\/.*?>)\s+(<\/.*?>)#s',
// 5 - c+t || t+o || o+t -- separated by long white-space(s)
'#(<!--.*?-->)|(<\/.*?>)\s+(\s)(?!\<)|(?<!\>)\s+(\s)(<[^\/]*?\/?>)|(<[^\/]*?\/?>)\s+(\s)(?!\<)#s',
// 6 - empty tag
'#(<!--.*?-->)|(<[^\/]*?>)\s+(<\/.*?>)#s',
// 7 - reset previous fix
'#<(img|input)(>| .*?>)<\/\1>#s',
// 8 - clean up ...
'#(&nbsp;)&nbsp;(?![<\s])#',
// 9 - --ibid
'#(?<=\>)(&nbsp;)(?=\<)#',
// 10 - Remove HTML comment(s) except IE comment(s)
'#\s*<!--(?!\[if\s).*?-->\s*|(?<!\>)\n+(?=\<[^!])#s'
],
[
'<$1$2</$1>', // 1
'$1$2$3', // 2
'$1$2$3', // 3
'$1$2$3$4$5', // 4
'$1$2$3$4$5$6$7', // 5
'$1$2$3', // 6
'<$1$2', // 7
'$1 ', // 8
'$1', // 9
"", // 10
],
$html
);
return $html;
}
public static function css( $css ) {
if ( trim( $css ) === '' )
return $css;
$css = preg_replace(
[
// 1 - Remove comment(s)
'#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')|\/\*(?!\!)(?>.*?\*\/)|^\s*|\s*$#s',
// 2 - Remove unused white-space(s)
'#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'|\/\*(?>.*?\*\/))|\s*+;\s*+(})\s*+|\s*+([*$~^|]?+=|[{};,>~]|\s(?![0-9\.])|!important\b)\s*+|([[(:])\s++|\s++([])])|\s++(:)\s*+(?!(?>[^{}"\']++|"(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')*+{)|^\s++|\s++\z|(\s)\s+#si',
// 3 - Replace `0(cm|rem|em|ex|in|mm|pc|pt|px|vh|vw|%)` with `0`
'#(?<=[\s:])(0)(cm|rem|em|ex|in|mm|pc|pt|px|vh|vw|%)#si',
// 4 - Replace `:0 0 0 0` with `:0`
'#:(0\s+0|0\s+0\s+0\s+0)(?=[;\}]|\!important)#i',
// 5 - Replace `background-position:0` with `background-position:0 0`
'#(background-position):0(?=[;\}])#si',
// 6 - Replace `0.6` with `.6`, but only when preceded by `:`, `,`, `-` or a white-space
'#(?<=[\s:,\-])0+\.(\d+)#s',
// 7/8 - Minify string value
'#(\/\*(?>.*?\*\/))|(?<!content\:)([\'"])([a-z_][a-z0-9\-_]*?)\2(?=[\s\{\}\];,])#si',
'#(\/\*(?>.*?\*\/))|(\burl\()([\'"])([^\s]+?)\3(\))#si',
// 9 - Minify HEX color code
'#(?<=[\s:,\-]\#)([a-f0-6]+)\1([a-f0-6]+)\2([a-f0-6]+)\3#i',
// 10 - Replace `(border|outline):none` with `(border|outline):0`
'#(?<=[\{;])(border|outline):none(?=[;\}\!])#',
// 11 - Remove empty selector(s)
'#(\/\*(?>.*?\*\/))|(^|[\{\}])(?:[^\s\{\}]+)\{\}#s',
],
[
'$1', // 1
'$1$2$3$4$5$6$7', // 2
'$1', // 3
':0', // 4
'$1:0 0', // 5
'.$1', // 6
'$1$3', // 7
'$1$2$4$5', // 8
'$1$2$3', // 9
'$1:0', // 10
'$1$2', // 11
],
$css
);
return trim( $css );
}
public static function js( $js ) {
if ( trim( $js ) === '' )
return $js;
$js = preg_replace(
[
// 1 - Remove comment(s)
'#\s*("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')\s*|\s*\/\*(?!\!|@cc_on)(?>[\s\S]*?\*\/)\s*|\s*(?<![\:\=])\/\/.*(?=[\n\r]|$)|^\s*|\s*$#',
// 2 - Remove white-space(s) outside the string and regex
'#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'|\/\*(?>.*?\*\/)|\/(?!\/)[^\n\r]*?\/(?=[\s.,;]|[gimuy]|$))|\s*([!%&*\(\)\-=+\[\]\{\}|;:,.<>?\/])\s*#s',
// 3 - Remove the last semicolon
'#;+\}#',
// 4 - Minify object attribute(s) except JSON attribute(s). From `{'foo':'bar'}` to `{foo:'bar'}`
'#([\{,])([\'])(\d+|[a-z_][a-z0-9_]*)\2(?=\:)#i',
// 5 - --ibid. From `foo['bar']` to `foo.bar`
'#([a-z0-9_\)\]])\[([\'"])([a-z_][a-z0-9_]*)\2\]#i',
],
[
'$1', // 1
'$1$2', // 2
'}', // 3
'$1$3', // 4
'$1.$3', // 5
],
$js
);
return $js;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment