Concatenate styles in WordPress


||

online-marketing-squeeze.jpg To continue WordPress optimization let’s take a look into styles.

Like scripts we have to include styles by enveloping function wp_enqueue_style and next:

In first put changes into /wp-includes/script-loader.php

  1. // TODO: Wott add styles to compact load
  2. require( ABSPATH . 'wp-content/styles.php' );

And make a file /wp-content/styles.php:

  1. <?php
  2.  
  3. $styles->default_dirs[] = '/wp-content/themes/';
  4. $styles->default_dirs[] = '/wp-content/plugins/';
  5. $styles->default_dirs[] = '/wp-includes/js/';
  6.  
  7. $styles->add( 'main', '/wp-content/themes/elegant-box/style.css', false,  '20100313' );
  8.  
  9. $styles->add( 'main-ie', '/wp-content/themes/elegant-box/ie.css', false,  '4.1.1' );
  10. $styles->add_data( 'main-ie', 'conditional', 'lte IE 7' );
  11.  
  12. $styles->add( 'styles-global', '/wp-content/themes/elegant-box/styles/global.css', false,  '4.1.1' );
  13.  
  14. $styles->add( 'highslide', '/wp-includes/js/highslide/highslide.css', false,  '4.1.1' );
  15.  
  16. $styles->add( 'codebox', '/wp-content/plugins/codebox/css/codebox.css', false,  '20100313' );
  17.  
  18. $styles->add( 'social', '/wp-content/plugins/social-bookmarks/social_bookmarks.css', false,  '20100314' );
  19.  
  20. $styles->add( 'wp-contentthemeselegant-boxstylesblackdefault', '/wp-content/themes/elegant-box/styles/black/default.css', false,  '' );
  21. $styles->add( 'wp-contentthemeselegant-boxstylesbluedefault', '/wp-content/themes/elegant-box/styles/blue/default.css', false,  '' );
  22. $styles->add( 'wp-contentthemeselegant-boxstylesbrowndefault', '/wp-content/themes/elegant-box/styles/brown/default.css', false,  '' );
  23. $styles->add( 'wp-contentthemeselegant-boxstylesgreendefault', '/wp-content/themes/elegant-box/styles/green/default.css', false,  '' );
  24. $styles->add( 'wp-contentthemeselegant-boxstylespurpledefault', '/wp-content/themes/elegant-box/styles/purple/default.css', false,  '' );
  25. $styles->add( 'wp-contentthemeselegant-boxstyleswhitedefault', '/wp-content/themes/elegant-box/styles/white/default.css', false,  '' );
  26. ?>

Like in script case we have to add folders for concatenated styles and define every used style. Last group look odd, but I describe it later.

But I have to note that function for admin and user styles looks different, but we can solve it: in file /wp-includes/functions.wp-styles.php:

  1. /**
  2.  * Display styles that are in the queue or part of $handles.
  3.  *
  4.  * @since r79
  5.  * @uses do_action() Calls 'wp_print_styles' hook.
  6.  * @global object $wp_styles The WP_Styles object for printing styles.
  7.  *
  8.  * @param array $handles (optional) Styles to be printed.  (void) prints queue, (string) prints that style, (array of strings) prints those styles.
  9.  * @return bool True on success, false on failure.
  10.  */
  11. function wp_print_styles( $handles = false ) {
  12.     // TODO: Wott - function rewrites as print_admin_styles
  13.     do_action( 'wp_print_styles' );
  14.     if ( '' === $handles ) // for wp_head
  15.         $handles = false;
  16.  
  17.     global $wp_styles, $concatenate_scripts, $compress_css;
  18.  
  19.     if ( !is_a($wp_styles, 'WP_Styles') ) {
  20.         if ( !$handles )
  21.             return array(); // No need to instantiate if nothing's there.
  22.         else
  23.             $wp_styles = new WP_Styles();
  24.     }
  25.  
  26.     script_concat_settings();
  27.     $wp_styles->do_concat = $concatenate_scripts;
  28.     $zip = $compress_css ? 1 : 0;
  29.     if ( $zip && defined('ENFORCE_GZIP') && ENFORCE_GZIP )
  30.         $zip = 'gzip';
  31.  
  32.     $wp_styles->do_items( $handles );
  33.  
  34.     if ( apply_filters('wp_print_styles', true) ) {
  35.         if ( !empty($wp_styles->concat) ) {
  36.             $dir = $wp_styles->text_direction;
  37.             $ver = md5("$wp_styles->concat_version{$dir}");
  38.             $href = $wp_styles->base_url . "/wp-admin/load-styles.php?c={$zip}&dir={$dir}&load=" . trim($wp_styles->concat, ', ') . "&ver=$ver";
  39.             echo "<link rel='stylesheet' href='" . esc_attr($href) . "' type='text/css' media='all' />\n";
  40.         }
  41.  
  42.         if ( !empty($wp_styles->print_html) )
  43.             echo $wp_styles->print_html;
  44.     }
  45.  
  46.     $wp_styles->do_concat = false;
  47.     $wp_styles->concat = $wp_styles->concat_version = $wp_styles->print_html = '';
  48.     return $wp_styles->done;
  49.  
  50. }

After you will receive styles concatenated.

Alternative styles

If you have several alternative styles you can not concatenate it, because it switched by javascript to enable/disable links and every style should have own link tag. But we have to define all alternative styles in the styles.php to changes images to data:URI

Another problem that wp_enqueue_style can not add parameters for alternative styles but we can add new function in the theme’s functions.php :

function wp_enqueue_style_with_title( $handle, $src = false, $deps = array(), $ver = false, $media = false, $alt = false, $title = false ) {
    global $wp_styles;
    if ( !is_a($wp_styles, 'WP_Styles') )
        $wp_styles = new WP_Styles();

    if ( $src ) {
        $_handle = explode('?', $handle);
        $wp_styles->add( $_handle[0], $src, $deps, $ver, $media );
        if ($alt)   $wp_styles->add_data( $_handle[0], 'alt', 1 );
        if ($title) $wp_styles->add_data( $_handle[0], 'title', $title );
    }
    $wp_styles->enqueue( $handle );
}

Styles will included by : ( this is part of my header.php )

// get the styles folder listing
$styleFolder = TEMPLATEPATH . '/styles/';
$styleArray = array();
$objStyleFolder = dir($styleFolder);
while(false !== ($styleFile = $objStyleFolder->read())) {
    if(is_dir($styleFolder . $styleFile) && $styleFile != '.' &&  $styleFile != '..') {
        $styleArray[] = $styleFile;
    }
}
$objStyleFolder->close();

// display other styles
if (is_array($styleArray)) {
    foreach ($styleArray as $style) {
        if($style != $default_style) {
            wp_enqueue_style_with_title($style,get_bloginfo('stylesheet_directory')."/styles/$style/default.css",array('main','default'),'','all',true, $style);
        }
    }
}

data:URI

For concatenated styles it’s pretty simple.

First, add function for seek and replace image links to encoded content in CSS:

In file functions.wp-styles.php

  1. /**
  2.  * Convert links of small images to data:URI
  3.  *
  4.  * @param $content
  5.  * @param $base
  6.  */
  7. function wp_datauri_style($content, $base ) {
  8.     // TODO: Wott
  9.  
  10.     $base .= '/';
  11.     $blog_home='https?:\/\/'.preg_replace('/\./','\.',$_SERVER["SERVER_NAME"]);
  12.  
  13.     if (preg_match_all("/url\(['\"]?(?!(?:data:))(?:$blog_home)?([-\w.\/]*?)\.(jpe?g|png|gif|bmp)['\"]?\)/i",$content,$m,PREG_SET_ORDER)) {
  14.         foreach ($m as $mm) {
  15.             $path = $_SERVER["DOCUMENT_ROOT"].((substr($mm[1],0,1)!='/')?$base:'').$mm[1].'.'.$mm[2];
  16.             if (file_exists($path) && filesize($path) < 32*1024*3/4 && is_readable($path)) {
  17.                 $type = ($mm[2]=='jpg')?'jpeg':$mm[2];
  18.                 $content = str_replace($mm[0],"url(data:image/$type;base64,".base64_encode(file_get_contents($path)).")",$content);
  19.             }
  20.         }
  21.     }
  22.  
  23.     return $content;
  24. }

And will use it for styles:

In file /wp-admin/load-styles.php:

  1.     //$out .= str_replace( '../images/', 'images/', $content );
  2.     if (function_exists('wp_datauri_style')&&!preg_match('/msie (5|6|7)\.0/i',$_SERVER["HTTP_USER_AGENT"])) {
  3.         $out .= wp_datauri_style($content, dirname($style->src));
  4.     } else {
  5.         // TODO: Wott:  find and change URL to absolute path
  6.         $out .= preg_replace("/url\(['\"]?(?!(?:data:|\/))([\w-.\/]*?)['\"]?\)/i",'url('.dirname($style->src).'/$1)',$content);
  7.     }

you can find that if not IE 5,6 or 7, then change link ( absolute or relative ) of own image to file encoded content. Or change path to absolute.

But if we have alternative styles we need to make same. And this is pretty simple.

Adding to .htaccess before index.php rule:

RewriteCond %{HTTP_REFERER} !^http://www\.wordpress\.org/wp-admin/.*$ [NC]
RewriteRule ^(.*)\.css /wp-admin/load-styles.php?c=gzip&dir=ltr&load=$1 [L]

in RewriteCond you have to set your blog address of course :)

By this rule we reroute all style ( *.css ) to already working script, but for right answer you need define style in /wp-content/styles.php like something

$styles->add( 'full path without slashes', '/wp-content/themes/elegant-box/styles/white/default.css', false,  '' );

Only 2 first parameters is important. Second is path from document root. First – same but without slash and extention – looks for example above.

The sense in using full path as handle for script to find reuse code for concatenated styles and we doesn’t need to change something more.

And condition in .htaccess for exclude admin styles.

By all of this we can enjoy with dramatically decreasing of loading files and size. In the last part I will describe the page content squeeze and bring into the small images. And review the total numbers.

feedback content * preview
Comments
  • I found that

    RewriteCond %{HTTP_REFERER} !^http://www\.wordpress\.org/(wp-admin|wp-includes)/.*$ [NC]
    RewriteRule ^(.*)\.css /wp-admin/load-styles.php?c=gzip&dir=ltr&load=$1 [L]

    is better