Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Rodrigo54/93169db48194d470188f to your computer and use it in GitHub Desktop.
Save Rodrigo54/93169db48194d470188f to your computer and use it in GitHub Desktop.
PHP Function to Minify HTML, CSS and JavaScript
<?php
/**
* -----------------------------------------------------------------------------------------
* Based on `https://github.com/mecha-cms/mecha-cms/blob/master/system/kernel/converter.php`
* -----------------------------------------------------------------------------------------
*/
// HTML Minifier
function minify_html($input) {
if(trim($input) === "") return $input;
// Remove extra white-space(s) between HTML attribute(s)
$input = 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", "", $input));
// Minify inline CSS declaration(s)
if(strpos($input, ' style=') !== false) {
$input = preg_replace_callback('#<([^<]+?)\s+style=([\'"])(.*?)\2(?=[\/\s>])#s', function($matches) {
return '<' . $matches[1] . ' style=' . $matches[2] . minify_css($matches[3]) . $matches[2];
}, $input);
}
if(strpos($input, '</style>') !== false) {
$input = preg_replace_callback('#<style(.*?)>(.*?)</style>#is', function($matches) {
return '<style' . $matches[1] .'>'. minify_css($matches[2]) . '</style>';
}, $input);
}
if(strpos($input, '</script>') !== false) {
$input = preg_replace_callback('#<script(.*?)>(.*?)</script>#is', function($matches) {
return '<script' . $matches[1] .'>'. minify_js($matches[2]) . '</script>';
}, $input);
}
return preg_replace(
array(
// t = text
// o = tag open
// c = tag close
// Keep important white-space(s) after self-closing HTML tag(s)
'#<(img|input)(>| .*?>)#s',
// Remove a line break and two or more white-space(s) between tag(s)
'#(<!--.*?-->)|(>)(?:\n*|\s{2,})(<)|^\s*|\s*$#s',
'#(<!--.*?-->)|(?<!\>)\s+(<\/.*?>)|(<[^\/]*?>)\s+(?!\<)#s', // t+c || o+t
'#(<!--.*?-->)|(<[^\/]*?>)\s+(<[^\/]*?>)|(<\/.*?>)\s+(<\/.*?>)#s', // o+o || c+c
'#(<!--.*?-->)|(<\/.*?>)\s+(\s)(?!\<)|(?<!\>)\s+(\s)(<[^\/]*?\/?>)|(<[^\/]*?\/?>)\s+(\s)(?!\<)#s', // c+t || t+o || o+t -- separated by long white-space(s)
'#(<!--.*?-->)|(<[^\/]*?>)\s+(<\/.*?>)#s', // empty tag
'#<(img|input)(>| .*?>)<\/\1>#s', // reset previous fix
'#(&nbsp;)&nbsp;(?![<\s])#', // clean up ...
'#(?<=\>)(&nbsp;)(?=\<)#', // --ibid
// Remove HTML comment(s) except IE comment(s)
'#\s*<!--(?!\[if\s).*?-->\s*|(?<!\>)\n+(?=\<[^!])#s'
),
array(
'<$1$2</$1>',
'$1$2$3',
'$1$2$3',
'$1$2$3$4$5',
'$1$2$3$4$5$6$7',
'$1$2$3',
'<$1$2',
'$1 ',
'$1',
""
),
$input);
}
// CSS Minifier => http://ideone.com/Q5USEF + improvement(s)
function minify_css($input) {
if(trim($input) === "") return $input;
return preg_replace(
array(
// Remove comment(s)
'#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')|\/\*(?!\!)(?>.*?\*\/)|^\s*|\s*$#s',
// 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',
// Replace `0(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)` with `0`
'#(?<=[\s:])(0)(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)#si',
// Replace `:0 0 0 0` with `:0`
'#:(0\s+0|0\s+0\s+0\s+0)(?=[;\}]|\!important)#i',
// Replace `background-position:0` with `background-position:0 0`
'#(background-position):0(?=[;\}])#si',
// Replace `0.6` with `.6`, but only when preceded by `:`, `,`, `-` or a white-space
'#(?<=[\s:,\-])0+\.(\d+)#s',
// Minify string value
'#(\/\*(?>.*?\*\/))|(?<!content\:)([\'"])([a-z_][a-z0-9\-_]*?)\2(?=[\s\{\}\];,])#si',
'#(\/\*(?>.*?\*\/))|(\burl\()([\'"])([^\s]+?)\3(\))#si',
// Minify HEX color code
'#(?<=[\s:,\-]\#)([a-f0-6]+)\1([a-f0-6]+)\2([a-f0-6]+)\3#i',
// Replace `(border|outline):none` with `(border|outline):0`
'#(?<=[\{;])(border|outline):none(?=[;\}\!])#',
// Remove empty selector(s)
'#(\/\*(?>.*?\*\/))|(^|[\{\}])(?:[^\s\{\}]+)\{\}#s'
),
array(
'$1',
'$1$2$3$4$5$6$7',
'$1',
':0',
'$1:0 0',
'.$1',
'$1$3',
'$1$2$4$5',
'$1$2$3',
'$1:0',
'$1$2'
),
$input);
}
// JavaScript Minifier
function minify_js($input) {
if(trim($input) === "") return $input;
return preg_replace(
array(
// Remove comment(s)
'#\s*("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')\s*|\s*\/\*(?!\!|@cc_on)(?>[\s\S]*?\*\/)\s*|\s*(?<![\:\=])\/\/.*(?=[\n\r]|$)|^\s*|\s*$#',
// Remove white-space(s) outside the string and regex
'#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'|\/\*(?>.*?\*\/)|\/(?!\/)[^\n\r]*?\/(?=[\s.,;]|[gimuy]|$))|\s*([!%&*\(\)\-=+\[\]\{\}|;:,.<>?\/])\s*#s',
// Remove the last semicolon
'#;+\}#',
// Minify object attribute(s) except JSON attribute(s). From `{'foo':'bar'}` to `{foo:'bar'}`
'#([\{,])([\'])(\d+|[a-z_][a-z0-9_]*)\2(?=\:)#i',
// --ibid. From `foo['bar']` to `foo.bar`
'#([a-z0-9_\)\]])\[([\'"])([a-z_][a-z0-9_]*)\2\]#i'
),
array(
'$1',
'$1$2',
'}',
'$1$3',
'$1.$3'
),
$input);
}
@MattIPv4
Copy link

Hey guys, this removes spaces in css that are inside a calc() which leads to it becoming an invalid property value, does anyone know the fix?

Copy link

ghost commented May 24, 2019

Thanks <3

@TriForceX
Copy link

Hey guys, this removes spaces in css that are inside a calc() which leads to it becoming an invalid property value, does anyone know the fix?

maybe is too late, but just FYI ive fixed it on a old project i made TriForceX/BootstrapExtended#14

@AugustGarcia
Copy link

Ran into a bug using this function with exceptionally large files (like 1 MB+ worth of HTML to minify). Specifically, above a certain string size, minify_html seems to return an empty string (or some other false-y value).

Added a "ghetto hack" as shown below. HTML will not be minified, but will at least return the original HTML.

    $original_input = $input; 

   /* The rest of the function */ 

    if (!$input)
        return $original_input;  
    return $input; 

@cproetti
Copy link

cproetti commented Mar 6, 2020

One problem, it removes whitespace in CSS calc() for example
font-size: calc(36px + (80 - 36) * (100vw - 320px) / (1366 - 320));

Becomes:
font-size: calc(36px+(80-36) * (100vw-320px) / (1366-320));

Which is invalid.

@Rodrigo54
Copy link
Author

Fixed problem in whitespace in CSS calc()

thanks @jcmarchi and @TriForceX for solution

@sabrysuleiman
Copy link

sabrysuleiman commented Jul 21, 2020

css replace 0% It will cause crash keyframes uses ( 0% ) ...

@jcmarchi
Copy link

css replace 0% It will cause crash keyframes uses ( 0% ) ...

Hi Rodrigo, any lucky looking into this one? keyframes are increasingly getting more important in nowadays CSS implementations and it can surely start being a problem in many cases.

Copy link

ghost commented Oct 12, 2020

css replace 0% It will cause crash keyframes uses ( 0% ) ...

Though I don't do css much, I think \d% in keyframe rules is always followed by {. Hence this pattern would work: (?<=[\s:])(0)(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)(?!\s*\{).

@jamesit8791
Copy link

One problem,
var loadPage = function(){
// Content
}
var loadPage = function(){
// Content
}
When compressing an error.

Here is correct.
var loadPage = function(){
// Content
};
var loadPage = function(){
// Content
};
How to fix this.
Thank!

@JiveDig
Copy link

JiveDig commented Jan 11, 2023

These are great, thanks! Like others, I removed the 0 replacement line. Thanks for much for sharing.

@sonomzy
Copy link

sonomzy commented Nov 14, 2023

Thank you so much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment