Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PHP Function to Minify HTML, CSS and JavaScript
<?php
/**
* ----------------------------------------------------------------------------------------
* Based on `https://github.com/mecha-cms/mecha-cms/blob/master/engine/plug/converter.php`
* ----------------------------------------------------------------------------------------
*/
// Helper function(s) ...
define('X', "\x1A"); // a placeholder character
$SS = '"(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'';
$CC = '\/\*[\s\S]*?\*\/';
$CH = '<\!--[\s\S]*?-->';
$TB = '<%1$s(?:>|\s[^<>]*?>)[\s\S]*?<\/%1$s>';
function __minify_x($input) {
return str_replace(array("\n", "\t", ' '), array(X . '\n', X . '\t', X . '\s'), $input);
}
function __minify_v($input) {
return str_replace(array(X . '\n', X . '\t', X . '\s'), array("\n", "\t", ' '), $input);
}
/**
* =======================================================
* HTML MINIFIER
* =======================================================
* -- CODE: ----------------------------------------------
*
* echo minify_html(file_get_contents('test.html'));
*
* -------------------------------------------------------
*/
function _minify_html($input) {
return preg_replace_callback('#<\s*([^\/\s]+)\s*(?:>|(\s[^<>]+?)\s*>)#', function($m) {
if(isset($m[2])) {
// Minify inline CSS declaration(s)
if(stripos($m[2], ' style=') !== false) {
$m[2] = preg_replace_callback('#( style=)([\'"]?)(.*?)\2#i', function($m) {
return $m[1] . $m[2] . minify_css($m[3]) . $m[2];
}, $m[2]);
}
return '<' . $m[1] . preg_replace(
array(
// From `defer="defer"`, `defer='defer'`, `defer="true"`, `defer='true'`, `defer=""` and `defer=''` to `defer` [^1]
'#\s(checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)(?:=([\'"]?)(?:true|\1)?\2)#i',
// Remove extra white-space(s) between HTML attribute(s) [^2]
'#\s*([^\s=]+?)(=(?:\S+|([\'"]?).*?\3)|$)#',
// From `<img />` to `<img/>` [^3]
'#\s+\/$#'
),
array(
// [^1]
' $1',
// [^2]
' $1$2',
// [^3]
'/'
),
str_replace("\n", ' ', $m[2])) . '>';
}
return '<' . $m[1] . '>';
}, $input);
}
function minify_html($input) {
if( ! $input = trim($input)) return $input;
global $CH, $TB;
// Keep important white-space(s) after self-closing HTML tag(s)
$input = preg_replace('#(<(?:img|input)(?:\s[^<>]*?)?\s*\/?>)\s+#i', '$1' . X . '\s', $input);
// Create chunk(s) of HTML tag(s), ignored HTML group(s), HTML comment(s) and text
$input = preg_split('#(' . $CH . '|' . sprintf($TB, 'pre') . '|' . sprintf($TB, 'code') . '|' . sprintf($TB, 'script') . '|' . sprintf($TB, 'style') . '|' . sprintf($TB, 'textarea') . '|<[^<>]+?>)#i', $input, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$output = "";
foreach($input as $v) {
if($v !== ' ' && trim($v) === "") continue;
if($v[0] === '<' && substr($v, -1) === '>') {
if($v[1] === '!' && substr($v, 0, 4) === '<!--') { // HTML comment ...
// Remove if not detected as IE comment(s) ...
if(substr($v, -12) !== '<![endif]-->') continue;
$output .= $v;
} else {
$output .= __minify_x(_minify_html($v));
}
} else {
// Force line-break with `&#10;` or `&#xa;`
$v = str_replace(array('&#10;', '&#xA;', '&#xa;'), X . '\n', $v);
// Force white-space with `&#32;` or `&#x20;`
$v = str_replace(array('&#32;', '&#x20;'), X . '\s', $v);
// Replace multiple white-space(s) with a space
$output .= preg_replace('#\s+#', ' ', $v);
}
}
// Clean up ...
$output = preg_replace(
array(
// Remove two or more white-space(s) between tag [^1]
'#>([\n\r\t]\s*|\s{2,})<#',
// Remove white-space(s) before tag-close [^2]
'#\s+(<\/[^\s]+?>)#'
),
array(
// [^1]
'><',
// [^2]
'$1'
),
$output);
$output = __minify_v($output);
// Remove white-space(s) after ignored tag-open and before ignored tag-close (except `<textarea>`)
return preg_replace('#<(code|pre|script|style)(>|\s[^<>]*?>)\s*([\s\S]*?)\s*<\/\1>#i', '<$1$2$3</$1>', $output);
}
/**
* =======================================================
* CSS MINIFIER
* =======================================================
* -- CODE: ----------------------------------------------
*
* echo minify_css(file_get_contents('test.css'));
*
* -------------------------------------------------------
*/
function _minify_css($input) {
// Keep important white-space(s) in `calc()`
if(stripos($input, 'calc(') !== false) {
$input = preg_replace_callback('#\b(calc\()\s*(.*?)\s*\)#i', function($m) {
return $m[1] . preg_replace('#\s+#', X . '\s', $m[2]) . ')';
}, $input);
}
// Minify ...
return preg_replace(
array(
// Fix case for `#foo [bar="baz"]` and `#foo :first-child` [^1]
'#(?<![,\{\}])\s+(\[|:\w)#',
// Fix case for `[bar="baz"] .foo` and `url(foo.jpg) no-repeat` [^2]
'#\]\s+#', '#\)\s+\b#',
// Minify HEX color code ... [^3]
'#\#([\da-f])\1([\da-f])\2([\da-f])\3\b#i',
// Remove white-space(s) around punctuation(s) [^4]
'#\s*([~!@*\(\)+=\{\}\[\]:;,>\/])\s*#',
// Replace zero unit(s) with `0` [^5]
'#\b(?:0\.)?0([a-z]+\b|%)#i',
// Replace `0.6` with `.6` [^6]
'#\b0+\.(\d+)#',
// Replace `:0 0`, `:0 0 0` and `:0 0 0 0` with `:0` [^7]
'#:(0\s+){0,3}0(?=[!,;\)\}]|$)#',
// Replace `background(?:-position)?:(0|none)` with `background$1:0 0` [^8]
'#\b(background(?:-position)?):(0|none)\b#i',
// Replace `(border(?:-radius)?|outline):none` with `$1:0` [^9]
'#\b(border(?:-radius)?|outline):none\b#i',
// Remove empty selector(s) [^10]
'#(^|[\{\}])(?:[^\s\{\}]+)\{\}#',
// Remove the last semi-colon and replace multiple semi-colon(s) with a semi-colon [^11]
'#;+([;\}])#',
// Replace multiple white-space(s) with a space [^12]
'#\s+#'
),
array(
// [^1]
X . '\s$1',
// [^2]
']' . X . '\s', ')' . X . '\s',
// [^3]
'#$1$2$3',
// [^4]
'$1',
// [^5]
'0',
// [^6]
'.$1',
// [^7]
':0',
// [^8]
'$1:0 0',
// [^9]
'$1:0',
// [^10]
'$1',
// [^11]
'$1',
// [^12]
' '
),
$input);
}
function minify_css($input) {
if( ! $input = trim($input)) return $input;
global $SS, $CC;
// Keep important white-space(s) between comment(s)
$input = preg_replace('#(' . $CC . ')\s+(' . $CC . ')#', '$1' . X . '\s$2', $input);
// Create chunk(s) of string(s), comment(s) and text
$input = preg_split('#(' . $SS . '|' . $CC . ')#', $input, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$output = "";
foreach($input as $v) {
if(trim($v) === "") continue;
if(
($v[0] === '"' && substr($v, -1) === '"') ||
($v[0] === "'" && substr($v, -1) === "'") ||
(substr($v, 0, 2) === '/*' && substr($v, -2) === '*/')
) {
// Remove if not detected as important comment ...
if($v[0] === '/' && substr($v, 0, 3) !== '/*!') continue;
$output .= $v; // String or comment ...
} else {
$output .= _minify_css($v);
}
}
// Remove quote(s) where possible ...
$output = preg_replace(
array(
'#(' . $CC . ')|(?<!\bcontent\:)([\'"])([a-z_][-\w]*?)\2#i',
'#(' . $CC . ')|\b(url\()([\'"])([^\s]+?)\3(\))#i'
),
array(
'$1$3',
'$1$2$4$5'
),
$output);
return __minify_v($output);
}
/**
* =======================================================
* JAVASCRIPT MINIFIER
* =======================================================
* -- CODE: ----------------------------------------------
*
* echo minify_js(file_get_contents('test.js'));
*
* -------------------------------------------------------
*/
function _minify_js($input) {
return preg_replace(
array(
// Remove inline comment(s) [^1]
'#\s*\/\/.*$#m',
// Remove white-space(s) around punctuation(s) [^2]
'#\s*([!%&*\(\)\-=+\[\]\{\}|;:,.<>?\/])\s*#',
// Remove the last semi-colon and comma [^3]
'#[;,]([\]\}])#',
// Replace `true` with `!0` and `false` with `!1` [^4]
'#\btrue\b#', '#false\b#', '#return\s+#'
),
array(
// [^1]
"",
// [^2]
'$1',
// [^3]
'$1',
// [^4]
'!0', '!1', 'return '
),
$input);
}
function minify_js($input) {
if( ! $input = trim($input)) return $input;
// Create chunk(s) of string(s), comment(s), regex(es) and
global $SS, $CC;
$input = preg_split('#(' . $SS . '|' . $CC . '|\/[^\n]+?\/(?=[.,;]|[gimuy]|$))#', $input, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$output = "";
foreach($input as $v) {
if(trim($v) === "") continue;
if(
($v[0] === '"' && substr($v, -1) === '"') ||
($v[0] === "'" && substr($v, -1) === "'") ||
($v[0] === '/' && substr($v, -1) === '/')
) {
// Remove if not detected as important comment ...
if(substr($v, 0, 2) === '//' || (substr($v, 0, 2) === '/*' && substr($v, 0, 3) !== '/*!' && substr($v, 0, 8) !== '/*@cc_on')) continue;
$output .= $v; // String, comment or regex ...
} else {
$output .= _minify_js($v);
}
}
return preg_replace(
array(
// Minify object attribute(s) except JSON attribute(s). From `{'foo':'bar'}` to `{foo:'bar'}` [^1]
'#(' . $CC . ')|([\{,])([\'])(\d+|[a-z_]\w*)\3(?=:)#i',
// From `foo['bar']` to `foo.bar` [^2]
'#([\w\)\]])\[([\'"])([a-z_]\w*)\2\]#i'
),
array(
// [^1]
'$1$2$4',
// [^2]
'$1.$3'
),
$output);
}
@david-hc

This comment has been minimized.

Copy link

commented Jan 3, 2015

Mungkin javascript juga bisa dengan trik yang sama? regex ?

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jan 3, 2015

Ini sampel untuk JavaScript → https://rawgit.com/tovic/mini-web-tools/master/converter.css.html

Contoh untuk minifier HTML → http://jsbin.com/soyawixaho/1/edit?js,output

// HTML Minifier
function minify_html(input) {
    return input
    .replace(/<\!--(?!\[if)([\s\S]+?)-->/g, "") // Remove HTML comments except IE comments
    .replace(/>[^\S ]+/g, '>')
    .replace(/[^\S ]+</g, '<')
    .replace(/>\s{2,}</g, '><');
}

// Test
alert(minify_html('<div>      <span><a href="">test</a> <span></span>\t</span>  \t\r\n\n\n\n\t\t       </div>'));
@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jan 3, 2015

Example Usage

$str = file_get_contents('p­ath/to/style.css');

// echo
echo minify_css($str);

//­ save as `style.min.css`
file_put_contents('path­/to/style.min.css', minify_css($str));
@kudataz

This comment has been minimized.

Copy link

commented Jan 7, 2015

example usage yg html dengan inline js & css ?

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jan 12, 2015

@kudataz

<style>
<?php echo minify_css(file_get_contents('p­ath/to/style.css')); ?>
</style>

<script>
<?php echo minify_js(file_get_contents('p­ath/to/engine.js')); ?>
</script>
@nakome

This comment has been minimized.

Copy link

commented Jul 22, 2015

Nice functions ;)

@mdmunir

This comment has been minimized.

Copy link

commented Oct 3, 2015

👍 Keren gan.
syntak angularjs diminify juga atau dipertahankan?

<table>
    <tr ng-repeat="item in items | filter:q">
        <td>{{item.name}}</td>
        <td>{{item.qty * item.price | number}}</td>
    </>
<table>
@tovic

This comment has been minimized.

Copy link
Owner Author

commented Nov 21, 2015

@mdmunir Harusnya fungsi ini cuma akan menghapus whitespaces di antara > dan < saja, jadi tidak akan terkompres kode Angular–mu.

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Nov 21, 2015

Example with Output Buffer

<?php

include 'path/to/php-html-css-js-minifier.php';

ob_start('minify_html');

?>

<!-- HTML code goes here ... -->

<?php echo ob_get_clean(); ?>
@mortensassi

This comment has been minimized.

Copy link

commented Dec 22, 2015

This is just awesome mate! Thank

@AndrewBerry

This comment has been minimized.

Copy link

commented Feb 28, 2016

Just a heads up - your minify_css function invalidates any use of calc() by removing the whitespace around the + and - operators.
See https://developer.mozilla.org/en/docs/Web/CSS/calc

Otherwise, amazing work! Thanks.

@AndrewBerry Fixed now thanks!

@huiralb

This comment has been minimized.

Copy link

commented Mar 7, 2016

Great !!
thanks gan, ngirit waktu.

ob_start();

// html content
// ......
$content = minify_html( ob_get_clean() );

echo $content;
@abantecart

This comment has been minimized.

Copy link

commented Mar 22, 2016

Great works on this. Can we use this in our open source under (OSL 3.0) ?
Thank you

@tovic

This comment has been minimized.

Copy link
Owner Author

commented May 15, 2016

The original license is GPL v3 from the Mecha CMS repository.

@EnterpriseBranding

This comment has been minimized.

Copy link

commented May 16, 2016

@tovic Do you have a php based HTML formatter? The opposite of the minifier for HTML? Or can I contact you about making one?

@NoahjChampion Simply use the native PHP Tidy extension.

@viktor-zhuromskyy

This comment has been minimized.

Copy link

commented May 18, 2016

Nice piece of code!

Could you please integrate automatic parsing of inline JavaScript code as you did with CSS in your main block?

@devdesco-ceo Something like this:

$content = preg_replace_callback('#<script(>|\s[^<>]*?>)([\s\S]*?)<\/script>#', function($m) {
    return '<script' . $m[1] . minify_js($m[2]) . '</script>';
}, $content);
@viktor-zhuromskyy

This comment has been minimized.

Copy link

commented May 18, 2016

Would you be so kind adding exception for removing the following comments tags since these are used by Magento2 for XHR requests markup?

Opening tag <!-- ko
Closing tag /ko -->

Example string not to touch:

<!-- ko template: getTemplate() --><!-- /ko -->

@devdesco-ceo The current version allows you to check specific pattern from the chunks:

if(strpos($v, '<!-- ko') === 0 || strpos($v, '<!-- /ko') === 0) {
    $output .= $v;
    continue; // skip!
}
@noasset

This comment has been minimized.

Copy link

commented May 24, 2016

Bagaimana dengan 'newline' yang ada pada tag pre?
apa juga akan ter 'mini'?

@noasset Tidak pak. Sejak versi yang pertama, proses minifikasi hanya terjadi di antara karakter > dan < saja. Versi yang sekarang bahkan memungkinkan untuk tidak menyentuh tag HTML di dalam blok <(code|pre|script|style)>.

@solankin1576

This comment has been minimized.

Copy link

commented May 25, 2016

You can replace the code which you don't need with another word like.
html_code=html_code.replace(/script/g, 'test');
here test is a new word for your code

@sammyukavi

This comment has been minimized.

Copy link

commented Jun 9, 2016

Excellent code! However if minify_html() is used with ob_start,, it fails to parse <!DOCTYPE html> if the html page starts with

<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8"> <![endif]-->  
<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->  
<!--[if !IE]><!--> <html lang="en"> <!--<![endif]-->  
@wannaco

This comment has been minimized.

Copy link

commented Jun 26, 2016

great, just that css files generated by lesscss usually have duplicate element tags html, body etc and they are simply stripped out.

@tmb-github

This comment has been minimized.

Copy link

commented Jul 13, 2016

The HTML minifier cannot handle embedded UL lists if there is additional text following the end of the embedded list. In the example below, "...and these are important" is ending the enclosing <li> beginning with "Some finer points:". However, the minification routine places "...and these are important" and the tabs that precede it on a new line following a CRLF:

<div>
    <ul>
        <li>AAAAAAAAAAAAAA</li>
<!-- li begins: -->
        <li>Some finer points:
            <ul> 
                <li>BBBBBBBBBBBB</li>
                <li>CCCCCCCCCCCC</li>
                <li>DDDDDDDDDDDD</li>
            </ul>
            ...and these are important.</li>
<!-- li ends -->
        <li>EEEEEEEEEEE</li>
    </ul>
</div>

...like this:

<div><ul><li>AAAAAAAAAAAAAA</li><li>Some finer points:
            <ul><li>BBBBBBBBBBBB</li><li>CCCCCCCCCCCC</li><li>DDDDDDDDDDDD</li></ul>    ...and these are important.</li><li>EEEEEEEEEEE</li></ul></div>
@tmb-github

This comment has been minimized.

Copy link

commented Jul 13, 2016

Single quotes and double quotes around HTML attributes should be removed with the following restrictions:

  1. The quoted material MUST exist within a tag <> (e.g., <mytag b="yes"> becomes <mytag b=yes>, but <script>var b="yes"</script> stays intact).

  2. The quoted material may not have a space character nor an equal sign (e.g., <mytag b="no no" c="no=no"> stays intact).

  3. The quoted material may not be in an href or src definition.

  4. It must handle unicode.

  5. Attribute names may include hyphens, dots, and colons, so these must be accounted for.

Here are three test cases:

<img test="xxx" href="http://example.com/images/Vögel.jpg" alt="Vögel" height="100" width="100"> <img test="xxx" src="http://example.com/images/Vögel.jpg" alt="Vögel" height="100" width="100">

<img test-time="xxx" src="http://example.com/images/Vögel.jpg" alt="Vögel" height="100" width="100"> <img test_time="xxx" src="http://example.com/images/Vögel.jpg" alt="Vögel" height="100" width="100">

<img aa-bb="xxx" cc-dd="xxx yyy" src="http://example.com/Vögel.jpg" temp="Bird" alt="The Bird" height="100" width="100">

Adding this as the second item in the regex array seems to do the job:

'#(?:<[a-z][\w:.-]*|(?!^)\G)(?:\s+(?:(?:src|href)=(?:"[^"]*"|\'[^\']*\')|[a-z][\w:.-]*="(?:[^"=]*\s[^"=]*|[^"\s]*=[^"\s]*)"))*\s+(?!(?:src|href)=)[a-z][\w:.-]*=\K(?|"([^\s"=]*)"|\'([^\s\'=]*)\')#ui',

...with this as as the second parameter array element:

'$1'

(This solution was devised by Wiktor Stribiżew on StackOverflow, so thanks go to him.)

@tmb-github This solution should be optional. You can create another wrapper that will run after the default HTML minifier. Not all people like to see mixed HTML attributes with and without quotes like this → <tag foo=bar baz=1 qux="a b c">

function minify_html_smart_quotes($input) {
    $input = preg_split('#(<[^<>]+?>)#', $input, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    $output = "";
    foreach($input as $v) {
        if($v[0] === '<' && substr($v, -1) === '>') {
            $output .= _do_something_with_tag($v); // remove optional quotes, etc…
        } else {
            $output .= $v;
        }
    }
    return $output;
}

echo minify_html_smart_quotes(minify_html($input));
@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jul 17, 2016

A Total Overhaul

Now the character type will be clearer so that you can easily check whether it is string, regular expression, comment or html tag. Now I’m using PHP preg_split to generate each parts correctly in an array. This way, you can make custom string manipulation safely by checking the character pattern first, especially at the beginning and end of the characters. Example:

$output = "";
foreach($input as $v) {
    if(trim($v) === "") continue; // white-space(s) or empty string
    if($v[0] === '"' && substr($v, -1) === '"') { … } // string with double quote
    if($v[0] === "'" && substr($v, -1) === "'") { … } // string with single quote
    if($v[0] === '<' && substr($v, -1) === '>') { // should be a HTML tag or HTML comment
        if($v[1] === '!') { … } // maybe a HTML comment, CDATA or DOCTYPE
        if(substr($v, -12) === '<![endif]-->') { … } // maybe an IE conditional comment
    }
    if(strpos($v, '//') === 0) { … } // maybe a JavaScript comment
    if(substr($v, 0, 2) === '/*' && substr($v, -2) === '*/') { … } // maybe a CSS comment
    // Others… (safe to be minified)
}

If you are interested in doing some more robust testing, you can see some examples here, here and here (look at the $test variable).

@tmb-github

This comment has been minimized.

Copy link

commented Jul 21, 2016

There’s a problem with the new CSS minifier. When I first ran it on my CSS, my fonts were not correct. This was not the case with the previous version of the CSS minifier. I eventually tracked down the problem: the old minifier was preserving quotes around all of the text in parentheses in the @font-face rule, like this:

@font-face{font-family:Roman;font-weight:400;font-style:normal;src:url('../fonts/Open-Sans-regular.eot');src:url('../fonts/Open-Sans-regular.eot?#iefix') format('embedded-opentype'),
local('Open Sans'),
local('Open-Sans-regular'),
url('../fonts/Open-Sans-regular.woff2') format('woff2'),
url('../fonts/Open-Sans-regular.woff') format('woff'),
url('../fonts/Open-Sans-regular.ttf') format('truetype'),
url('../fonts/Open-Sans-regular.svg#OpenSans') format('svg')}

This worked. The new CSS minifier strips them out except when there is a space character in the string, like this:

@font-face{font-family:Roman;font-weight:400;font-style:normal;src:url(./fonts/Open-Sans-regular.eot);src:url(./fonts/Open-Sans-regular.eot?#iefix) format(embedded-opentype),
local('Open Sans'),
local(Open-Sans-regular),
url(./fonts/Open-Sans-regular.woff2) format(woff2),
url(./fonts/Open-Sans-regular.woff) format(woff),
url(./fonts/Open-Sans-regular.ttf) format(truetype),
url(./fonts/Open-Sans-regular.svg#OpenSans) format(svg)}

This prevents the fonts from being employed; restoring the surrounding quotes corrected the problem. Every string within parentheses in the @font-face should apparently be enclosed within quotes; the MDN spec on @font-face is here:

https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face

Even though it doesn’t specifically address this except in reference to arguments specifically described as strings, it appears to be true for URLs, etc., as well.

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jul 22, 2016

@tmb-github Thanks for the input. Changing the (?<!\bcontent\:) part with (?<!\bcontent\:|\bformat\(|\blocal\() should fix the issue.

@tmb-github

This comment has been minimized.

Copy link

commented Jul 28, 2016

@tovic Scratch what I posted earlier here, if you saw it...something else is amiss. I'll write back when I figure it out. The urls seem fine without surrounding quotes.

@tmb-github

This comment has been minimized.

Copy link

commented Jul 28, 2016

@tovic: I've found it. The new routine is stripping out quotes around arguments in format():

/* works--this is what the old version of the CSS minifier produced: */
@font-face{font-family:Roman;font-weight:400;font-style:normal;src:url(./fonts/Muli-regular.eot);src:url(./fonts/Muli-regular.eot?#iefix) format('embedded-opentype'),local('Muli'),local('Muli-regular'),url(./fonts/Muli-regular.woff2) format('woff2'),url(./fonts/Muli-regular.woff) format('woff'),url(./fonts/Muli-regular.ttf) format('truetype'),url(./fonts/Muli-regular.svg) format('svg')}
/* fails--this is what the new version of the CSS minifier produces--text in format() is losing its quotes: */
@font-face{font-family:Roman;font-weight:400;font-style:normal;src:url(./fonts/Muli-regular.eot);src:url(./fonts/Muli-regular.eot?#iefix) format(embedded-opentype),local('Muli'),local('Muli-regular'),url(./fonts/Muli-regular.woff2) format(woff2),url(./fonts/Muli-regular.woff) format(woff),url(./fonts/Muli-regular.ttf) format(truetype),url(./fonts/Muli-regular.svg) format(svg)}
@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jul 28, 2016

Sigh. I think I will just comment-out that parts for now. Other problem may also occurs on the @charset rules.

@mrizwan47

This comment has been minimized.

Copy link

commented Aug 13, 2016

Great snippet. I've been using for a long time. But recently found a problem, when I minify this script it gives error:

Uncaught SyntaxError: Unexpected identifier

I fixed it by commenting line 248 and 258 but those are important replacements for minification 😞

Any help would be appreciated 😄

@mrizwan47

This comment has been minimized.

Copy link

commented Aug 20, 2016

Hi @tovic, faced another problem in js minification:

Input:

this.isomethign()

this.test()

Output:

this.isomethign()this.test()

which raises error: Unexpected token this.

Expected output:

this.isomethign();this.test()
@tovic

This comment has been minimized.

Copy link
Owner Author

commented Aug 24, 2016

@mrizwan47 This minifier does not do any code validation. No characters added by these functions to the source input. You should validate your own code.

@kwiatuLodz

This comment has been minimized.

Copy link

commented Sep 14, 2016

something is wrong when css like this

before:
.foo:not(.active) .bar:nth-child(3) {

}

after:
.foo:not(.active).bar:nth-child(3){}

This compiler does not include space between .foo:not(.active) and .bar:nth-child(3)

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Sep 24, 2016

@bbykwiatu This is weird. Because I don’t trim any white-spaces arround dot characters → https://gist.github.com/tovic/d7b310dea3b33e4732c0/a2aec8653a893c42f4cf22662e775814c2d91f0a#file-php-html-css-js-minifier-php-L147

And the replacement of #\)\s+\b# to )<space placeholder> before it should fix the issue :\

@avsoftenterprise

This comment has been minimized.

Copy link

commented Oct 3, 2016

I have an issue with this minifier.
I'm going to improve my SEO. This is the reason why I convert all image src with path in image base64 string.

When I parse my HTML with minifier it crash and the response page is empty.

Do you have any suggestion?

@Fusseldieb

This comment has been minimized.

Copy link

commented Oct 5, 2016

When comments have any " ' ", it'll break the javascript badly...

Ex: " // That isn't for something " -> This break the js!

Other than that, nice php :)

EDIT:
I fixed it!

Replace:

// Remove inline comment(s) [^1]
            '#\s*\/\/.*$#m',

with:

// Remove inline comment(s) [^1]
            '#((\/\*)[\S\s]*?(\*\/))|((\/\/)[\S\s]*?(?=[\n\r]))#',

Now it will accept even multiline comments.
Big thanks to http://regexr.com/ :p

EDIT2:
But it still doesn't accept code comments.
Like those: // mydiv.classList.add('fadeout');
It'll break.

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Oct 29, 2016

@Fusseldieb the parser splits your comment code as string and comment like this:

array(
    '// ',
    'myDiv.classList.add(',
    '\'fadeout\'',
    ');'
);

Will try to fix this issue next time. Maybe by setting the comment string in a higher priority to be captured than the string string.

@mocanuga

This comment has been minimized.

Copy link

commented Nov 10, 2016

I have some issues with the js minifier.

'use strict' becomes 'usestrict'.
if('foo' in bar) becomes if('foo'inbar).
var Gesture becomes varGesture
var rule = typeof selector == 'object' ? selector : this.get(selector) becomes varrule=typeofselector=='object'?selector:this.get(selector)

I suppose there are more but can't detect them as the parser breaks on first issue.

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Jan 7, 2017

Updated!

Added function arguments:

$output = minify_{$any}($input, $comment = 2, $quote = 2);
  • 0: Remove
  • 1: Keep
  • 2: Remove if/but/when … (example: keep if detected as IE conditional comments, keep if contains @license, etc.)
@MarlonGnauck

This comment has been minimized.

Copy link

commented Jan 22, 2017

<span>0</span>
will be "optimized" to
<span></span>

If I use an other number... all is correct
<span>1</span>

@MarlonGnauck Found the problem. PHP treat the string 0 as boolean false. Weird. ☹️

2017-01-27_033037

@suporteon

This comment has been minimized.

Copy link

commented Feb 2, 2017

Great job! Congratulations!!!

It's good to have minify solutions in a single script or class.

It would be more interesting if you could adapt your code to a "Minify" class with public methods such as minify_html(), minify_css() and minify_js().

I tried many minifiers and all of them (including this one) fail with my scripts containing specific situations:

Case 1: single quote in a js comment:

number = Math.round(number - number % 1); // Plain Math.round doesn't just truncate

Case 2: two consecutive empty blocks of jquery (two "empty" files being minified):

$(function() {

});

$(function() {

});

Case 3: a comma after the last element of a JSON object:

var config= {
	app_name : "Suporte On",
	app_username : "suporte_on",
	app_domain : "suporteon.com.br",
};

Thank you!

@suporteon

It would be more interesting if you could adapt your code to a "Minify" class with public methods such as …

Yes, these functions are based on the Minify class from a PHP CMS called Mecha.

I tried many minifiers and all of them (including this one) fail with my scripts containing specific situations.

Trying to fix them. Do you have short examples that represent about before and after JavaScript output using this minifier?

FYI, I don’t have any plan to add ability to minify the variable name too. That‘s too overweight.

@Saacy

This comment has been minimized.

Copy link

commented Feb 3, 2017

Hello. Thanks for the minifier. I have found a bug.

.text { background-position:0 -190px; }
transforms to:
.text{background-position:0 0 -190px}

@Saacy 👍

@dad72

This comment has been minimized.

Copy link

commented Feb 28, 2017

The JavaScript in the HTML and use of fn_minify_html () is not minimized. and comments in PHP is not minimized too.

Is it possible that this is supported? Thank you

See #gistcomment-1781082. PHP does not required to be minified because PHP files are not downloaded directly like normal files.

@sodabob

This comment has been minimized.

Copy link

commented Mar 24, 2017

When minimizing javascript, the end } of a function and the next line are placed together. For example:

window.onload = function(e) {
	externalLinks();
}

var phone_pattern = new RegExp("\\d{3}\\-\\d{3}\\-\\d{4}");

becomes

window.onload = function(e) {externalLinks();}var phone_pattern = new RegExp("\\d{3}\\-\\d{3}\\-\\d{4}");

which causes a javascript error.

EDIT: the below fixed the javascript error, but it broke the minimization. Not sure why?
To fix, I removed the part that removes white space(s) around { or } i.e. line 271 becomes:

'#\s*([!%&*\(\)\-=+\[\]|;:,.<>?\/])\s*#',

Thanks for a great script, it is a HUGE time saver for me attempting to do this from scratch.

FWIW, Google PageSpeed Insights still considers the javascript to be minimized with this change (which makes no sense, since it wasn't minimized).

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Mar 27, 2017

@sodabob This minifier does not do any code validation. Your code is invalid because semicolon after object access is important (expected `;` and instead saw `(end)`). It is like when you put semicolon at the end of variable definition. This will fix the issue:

window.onload = function() {

}; // ← :)

Explanation

window.onload = function() {

} // ← :(

function test() {

} // ← :)

window.onload = test // ← :(
window.onload = test; // ← :)
@dlzi

This comment has been minimized.

Copy link

commented Apr 18, 2017

Hello.

Does the script concatenate multiple files? I couldn't find any info about that on the examples above.

Thanks.

@danielzilli No this script only works as a minifier. Concatenating requires extra function to do something like: get the content, minify, combine all of them and save the combined contents to a new file.

@TriForceX

This comment has been minimized.

Copy link

commented May 3, 2017

@tovic a ) symbol is missing at the end of the if in the line 257 (https://gist.github.com/tovic/d7b310dea3b33e4732c0#file-php-html-css-js-minifier-php-L257)

actually is:
if ($part[0] === '/' && (substr($part, -1) === '/' || preg_match('#\/[gimuy]*$#', $part)) {

should be:
if ($part[0] === '/' && (substr($part, -1) === '/' || preg_match('#\/[gimuy]*$#', $part))) {

@TriForceX Fixed thanks.

@radhava

This comment has been minimized.

Copy link

commented May 17, 2017

Hi, You have a wonderful script there...
Is it possible to include this php-html-css-js-minifier.php file in the header.php (For oscommerce stores) and get all CSS, JS, html compressed automatically on the fly?
`<?php
//header.php file
include 'includes/php-html-css-js-minifier.php';

//What code goes here to TRIGGER THE minify for all JS CSS FILES AND HTML on the fly.`

Please your help will be deeply appreciated;
Warm Regds.

@radhava Please check #gistcomment-2063222

To make such functionality requires a lot of work. And sometimes by making everything automatic will also lead to uncontrollable results.

@ivobarbosa

This comment has been minimized.

Copy link

commented May 24, 2017

Hello, your function minify_css dont work with bootstrap medias querys, can you help me to fix them?
I try to minify this https://scotch.io/tutorials/default-sizes-for-twitter-bootstraps-media-queries
The file css show is empty.
Thanks.

@ #

@ivobarbosa Haven’t checked yet, but will add this to my TODO list.

@alan-zhang-

This comment has been minimized.

Copy link

commented Jun 27, 2017

It's a great tool, but has weird bug:
{% for lang_abbr, lang_name in langs %} <li> <a class="flag flag-{{ lang_abbr }}" title="{{ lang_name }}" href="{{ sites[lang_abbr] ~ uri }}"> {{ lang_name }} </a> </li> {% endfor %}
But this fork saved me : https://gist.github.com/Rodrigo54/93169db48194d470188f

@moonston3

This comment has been minimized.

Copy link

commented Aug 2, 2017

Thx for your work.

I've had a problem with the CSS Minifier:

// Replace zero unit(s) with0[^5] '#\b(?:0\.)?0([a-z]+\b|%)#i',
Matches e.g. '2.0vmax' and changed it to "2.0" because "\b" matches "." and '(?:0.)?' is not required.
Here is my poor "update" of your Pattern, maybe someone can do it in elegant:

Search:
'#(\s|:|,\()(?:0\.0|0)(?:[a-z]+\b|%)|(?:0\.0)#i',
Replace with:
'${1}0',

By the way I added a replace for "0.0" (zero float without a unit).

Best regards

Edit:
Changed the pattern to ''#(\s|:|,()(?:0.0|0)(?:[a-z]+\b|%)|(?:0.0[^0-9])#i',' because not only '0.0' was changed to '0'. Also '0.07em' was changed to '07em'... Maybe someone would write a clean pattern? ;)

@moonston3 I am still playing around with this regex → https://regex101.com/r/odUcvs/1

@rinku17

This comment has been minimized.

Copy link

commented Aug 11, 2017

The last update introduced a bug that I have experienced in JS minification.
It removes this / ( forward slash ) character within js regex. But the previous js minify definition with define('MINIFY_PATTERN_JS', '\b/[^\n]+?/[gimuy]*\b'); works fine.

thanks.

@BabyDead

This comment has been minimized.

Copy link

commented Aug 24, 2017

This really breaks on regex in javascript minification. Or, better yet, it deletes literally anything that starts with a forward slash. "/".
For now, something like this is too complicated for me to fix, unfortunately :(

@rinku17 @BabyDead Thanks for the report. The last update should fix the issue. My mistake, sorry.

@tovic

This comment has been minimized.

Copy link
Owner Author

commented Aug 29, 2017

Found the regex, unfortunately not reverse-able with JavaScript:

2017-08-29_193409

@Aljnk

This comment has been minimized.

Copy link

commented Nov 5, 2017

javascript code var txt="am"; converted to var txt=am; without ";

also, you can use 0 and 1 for replace false and true (or !1 and !0 - I saw this in some minified files);

@Aljnk

also, you can use 0 and 1 for replace false and true (or !1 and !0 - I saw this in some minified files);

https://gist.github.com/tovic/d7b310dea3b33e4732c0/8420914f9a62b27ddc240ab10e1e6ace08fa9b5f#file-php-html-css-js-minifier-php-L276

@tmiecz

This comment has been minimized.

Copy link

commented Feb 20, 2018

Nice script. I noticed that MINIFY_HTML removes domain name to make the paths relative - that's problematic in some instances, for example, all of your og:image tags will show relative paths, which will break when someone posts your URL to social media.

@ArnoldLovasHeute

This comment has been minimized.

Copy link

commented May 17, 2018

hi, i found two css issues:

  1. it changes the unicode-range at the @font-face, this font wont be visible eg
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
    to
    unicode-range:U+0000-0,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD

the related part in your code:
// Replace zero unit(s) with0 [^5] '#\b(?<!\d\.)(?:0+\.)?0+(?:[a-z]+\b)#i',

@ArnoldLovasHeute

This comment has been minimized.

Copy link

commented May 17, 2018

  1. if you use an svg as a background-image, it removes the quote, but it needs it
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23fff' d='M211.9 197.4h-36.7v59.9h36.7v175.8h70.5V256.5h49.2l5.2-59.1h-54.4v-33.7c0-13.9 2.8-19.5 16.3-19.5h38.2V82.9h-48.8c-52.5 0-76.1 23.1-76.1 67.3-.1 38.6-.1 47.2-.1 47.2z'/%3E%3C/svg%3E")

the related part in your code:
// <https://www.w3.org/TR/CSS2/syndata.html#uri> substr($prev, -4) === 'url(' && preg_match('#\burl\($#', $prev) ||

Thanks for the reports! Will update this next time.

@dipesh711

This comment has been minimized.

Copy link

commented Jun 23, 2018

Hello @tovic,
This is Works Great Only one Thing I have notice that is in all the place where URL is write with external or internal link in the code that is Converted to /"

Like example .
Original Html : <link href="https://bootstrap/bootstrap.min.css" rel="stylesheet">
Converted Minify HTML : <link href=/"https://bootstrap/bootstrap.min.css/" rel=/"stylesheet/">

And I have see the code where Find the Comment you have writed
// Minify URL(s)

In that you have coded str_replace the " with /" and ' with /' can I known what is the purpose behind that to do.

Thanks,

@dipesh711 There must be some errors in the $url value. Please check by dumping it like var_dump($url). What do you see?

In that you have coded str_replace the " with /" and ' with /' can I know what is the purpose behind that to do (?)

Yes. The code said: str_replace the http://example.com" with /" and http://example.com' with /'

@pwFoo

This comment has been minimized.

Copy link

commented Jul 1, 2018

Maybe gist should be rewritten to a library with a git repo instead of just a gist?

@pwFoo Yes, it IS a library, but it depends to the Mecha CMS core.

@zdhw

This comment has been minimized.

Copy link

commented Sep 24, 2018

If an if/else statement is used without curly brackets, an extra line feed appears.

for example:

if (a1 == "1")
E ="err1";
else
E ="err2";

Will become:

if(a1=="1")E="err1";else
E="err2";

Thanks

@384400

This comment has been minimized.

Copy link

commented Sep 11, 2019

Thanks. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.