Skip to content

Instantly share code, notes, and snippets.

@Shelob9
Last active June 25, 2020 09:59
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Shelob9/4abd39c57e31c1a37d689118236be729 to your computer and use it in GitHub Desktop.
Save Shelob9/4abd39c57e31c1a37d689118236be729 to your computer and use it in GitHub Desktop.
Combine, Minify and Inline CSS In WordPress, in the correct order

Combine, Minify and Inline CSS In WordPress, in the correct order

Installation

Use copypasta to add to a sub-dir of mu-plugins or use Composer:

  "require": {
    "shelob9/css-inliner" : "*"
  }

and

  "repositories": [
    {
      "type": "vcs",
      "url": "https://gist.github.com/Shelob9/4abd39c57e31c1a37d689118236be729.git"
    }
  ]

Example Usage:

In your main mu-plugin or whatever

  add_action( 'init', function(){
    if( ! is_admin() && ! wp_doing_ajax() ){
      include  WP_CONTENT_DIR  . '/mu-plugins/css-inliner/init.php';

      add_filter( 'josh_css_minifier_styles', function(){
        return [
          'tm_clicktotweet',
          'edd-styles',
          'edd_acp_frontend_css',
          'edd-sl-styles',
          'edd-recurring',
        ];

      });
    }
  });
{
"name": "shelob9/css-inliner",
"license": "GPL v2+",
"type": "wordpress-muplugin",
"repositories": [
{
"type": "composer",
"url": "http://wpackagist.org"
}
],
"require": {
"composer/installers" : "~1.0"
}
}
<?php
class Josh_CSS_Inliner {
protected $styles;
protected $prepared;
public function __construct( array $styles = array() )
{
$this->styles = $styles;
if( ! empty( $this->styles ) ){
$this->set_prepared();
if( ! empty( $this->prepared ) ){
add_action( 'wp_head', [ $this, 'wp_head' ] );
}
}
}
public function wp_head()
{
printf( "<style id='%s-inline-css' class='cfdc-inlined' type='text/css'>\n%s\n</style>\n", esc_attr( $this->cache_key() ), $this->prepared );
}
protected function cache_key() : string
{
return md5( implode( ',', array_values( $this->styles ) ) );
}
protected function set_prepared()
{
$key = $this->cache_key();
if( ! empty( $_prepared = get_transient( $key ) ) ){
$this->prepared = $_prepared;
}else{
$this->prepared = $this->process();
set_transient( $key, $this->prepared, WEEK_IN_SECONDS );
}
}
protected function process() : string
{
$ordered = [];
global $wp_styles;
foreach ( $this->styles as $style ){
$key = array_search( $style, $wp_styles->queue );
if( false !== $key ){
$ordered[ $key ] = str_replace( WP_CONTENT_URL, untrailingslashit( WP_CONTENT_DIR ), $wp_styles->registered[ $style ]->src );
unset( $wp_styles->queue[ $key ] );
unset( $wp_styles->registered[ $style ] );
}
}
ksort( $ordered );
ob_start();
foreach ( $ordered as $path ){
if( file_exists( $path ) ){
include $path;
}
}
$concat = ob_get_clean();
if( ! empty( $concat ) ){
return josh_minify_css($concat);
}else{
return '';
}
}
}
<?php
//https://gist.githubusercontent.com/tovic/d7b310dea3b33e4732c0/raw/a2aec8653a893c42f4cf22662e775814c2d91f0a/php-html-css-js-minifier.php
// Helper function(s) ...
define('X', "\x1A"); // a placeholder character
$SS = '"(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'';
$CC = '\/\*[\s\S]*?\*\/';
$CH = '<\!--[\s\S]*?-->';
function josh__minify_x($input) {
return str_replace(array("\n", "\t", ' '), array(X . '\n', X . '\t', X . '\s'), $input);
}
function josh__minify_v($input) {
return str_replace(array(X . '\n', X . '\t', X . '\s'), array("\n", "\t", ' '), $input);
}
/**
* =======================================================
* CSS MINIFIER
* =======================================================
* -- CODE: ----------------------------------------------
*
* echo josh_minify_css(file_get_contents('test.css'));
*
* -------------------------------------------------------
*/
function josh__minifiy_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 `@media (foo: bar) and (baz: qux)` [^2]
'#\]\s+#', '#\b\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]
'#(^|[\{\}])(?:[^\{\}]+)\{\}#',
// 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(', ')' . 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 josh_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) === "'") ||
(strpos($v, '/*') === 0 && substr($v, -2) === '*/')
) {
// Remove if not detected as important comment ...
if($v[0] === '/' && strpos($v, '/*!') !== 0) continue;
$output .= $v; // String or comment ...
} else {
$output .= josh__minifiy_css($v);
}
}
// Remove quote(s) where possible ...
$output = preg_replace(
array(
// '#(' . $CC . ')|(?<!\bcontent\:|[\s\(])([\'"])([a-z_][-\w]*?)\2#i',
'#(' . $CC . ')|\b(url\()([\'"])([^\s]+?)\3(\))#i'
),
array(
// '$1$3',
'$1$2$4$5'
),
$output);
return josh__minify_v($output);
}
<?php
add_action( 'wp_print_styles', function() {
$styles_to_inline = apply_filters( 'josh_css_minifier_styles', [ ] );
if ( empty( $styles_to_inline ) ) {
return;
}
include_once __DIR__ . '/functions.php';
include_once __DIR__ . '/css-inliner.php';
new Josh_CSS_Inliner( $styles_to_inline );
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment