Browse Source

Initial commit

master
Chris Wiegman 3 years ago
commit
a2625935fe
30 changed files with 5068 additions and 0 deletions
  1. +22
    -0
      .htaccess
  2. +170
    -0
      config/batcache-config.php
  3. +1469
    -0
      config/is-mobile.php
  4. +132
    -0
      config/roles.php
  5. +23
    -0
      config/vip-config.php
  6. +4
    -0
      index.php
  7. +10
    -0
      local-config.php
  8. +1
    -0
      pv-hosts
  9. +28
    -0
      pv-init.sh
  10. +1
    -0
      pv-mappings
  11. +119
    -0
      vip.wordpress.pv.pp
  12. +2
    -0
      wp-cli.yml
  13. +105
    -0
      wp-config.php
  14. +523
    -0
      wp-content/advanced-cache.php
  15. +2
    -0
      wp-content/index.php
  16. +97
    -0
      wp-content/mu-plugins/alloptions-limit.php
  17. +46
    -0
      wp-content/mu-plugins/batcache.php
  18. +193
    -0
      wp-content/mu-plugins/http-concat/cssconcat.php
  19. +777
    -0
      wp-content/mu-plugins/http-concat/cssmin.php
  20. +188
    -0
      wp-content/mu-plugins/http-concat/jsconcat.php
  21. +216
    -0
      wp-content/mu-plugins/http-concat/ngx-http-concat.php
  22. +60
    -0
      wp-content/mu-plugins/infinite-scroll.php
  23. +59
    -0
      wp-content/mu-plugins/login.php
  24. +44
    -0
      wp-content/mu-plugins/mobile-theme-bootstrap.php
  25. +41
    -0
      wp-content/mu-plugins/quickstart.php
  26. +138
    -0
      wp-content/mu-plugins/rewrite-rules-inspector-helper.php
  27. +28
    -0
      wp-content/mu-plugins/vip.php
  28. +155
    -0
      wp-content/mu-plugins/wpcom.php
  29. +402
    -0
      wp-content/object-cache.php
  30. +13
    -0
      wp-content/sunrise.php

+ 22
- 0
.htaccess View File

@@ -0,0 +1,22 @@
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteRule ^wp-admin(.*) /wp/wp-admin$1 [R=301,L]


RewriteRule ^wp/_static/(.*) /wp-content/mu-plugins/http-concat/ngx-http-concat.php$1 [L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^(wp-(content|admin|includes).*) /wp/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ /wp/$2 [L]
RewriteRule . index.php [L]
</IfModule>

# END WordPress

+ 170
- 0
config/batcache-config.php View File

@@ -0,0 +1,170 @@
<?php

define( 'WP_CACHE', true );

// utility variables that are use a bunch of times
$_batcache_script_name = basename( $_SERVER['SCRIPT_NAME'] );

// VIP Quickstart: Fix module activation
$_module = isset( $_GET['module'] ) ? $_GET['module'] : null;

$__batcacheignore_args = array(); // which query params to ignore
$__batcacheignore_args2 = array(); // which query params to ignore
if ( isset( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] ) {
$__query_args = explode( '&', $_SERVER['QUERY_STRING'] );
$__batcacheignore_defaults = array(
'hpt', 'eref', 'iref', 'fbid', 'om_rid', 'utm_source', 'utm_content', 'utm_medium', 'utm_campaign', 'utm_term', 'fb_xd_bust', 'fb_xd_fragment', 'npt', 'module', 'iid', 'icid', 'ncid', 'snapid', '_',
);

foreach ( $__batcacheignore_defaults as $bkey => $__batcacheignore_default ) {
foreach ( $__query_args as $qkey => $__query_arg ) {
if ( $__query_arg == $__batcacheignore_default || 0 === stripos( $__query_arg, "$__batcacheignore_default=" ) ) {
$__batcacheignore_args[] = $__batcacheignore_default;
break;
}
}
}
}

// These are cookies that would normally prohibit caching, but
// we want to serve cached pages to these folks anyway
$batcache['noskip_cookies'] = array(
'wordpress_test_cookie',
'wordpress_eli',
'wpcom-browser-extension-promos-chrome',
'wpcom-browser-extension-promos-firefox',
'wpcom_geo',
);

// These variables are for the default configuration. Domain-specific configs follow.

$batcache['max_age'] = 300; // Expire batcache items aged this many seconds (zero to disable supercache)
$batcache['remote'] = 0; // whether to replicate the cache across datacenters (req/sec not replicated)

$batcache['times'] = 4; // Only batcache a page after it is accessed this many times... (two or more)
$batcache['seconds'] = 60; // ...in this many whole seconds (zero to ignore this and use batcache immediately)

$batcache['group'] = 'supercache'; // Name of memcached group

// Unset all the explicitly ignored GET args to make caching more efficient
if ( $__batcacheignore_args ) {
// batcache requires a normalized query string
$_SERVER['QUERY_STRING'] = preg_replace( '#((' . implode( '|', $__batcacheignore_args ) . ')=[^&]*&?)#', '', $_SERVER['QUERY_STRING'] );
// batcache requires an empty get superglobal
foreach ( $__batcacheignore_args as $getarg ) {
unset( $_GET[ $getarg ] );
}
}

// Special signup page
if ( '/wp-signup.php' == $_SERVER['PHP_SELF'] && isset( $_COOKIE['ref'] ) && '360' == $_COOKIE['ref'] ) {
$batcache['max_age'] = 0; // disable batcache
}

// Don't cache PHP files except the root index.php
if ( substr( $_SERVER['REQUEST_URI'], - 4 ) == '.php' && ( '/index.php' != $_SERVER['REQUEST_URI'] ) ) {
$batcache['max_age'] = 0; // disable batcache
}
if ( substr( $_SERVER['REQUEST_URI'], - 3 ) == '.js' ) {
$batcache['max_age'] = 0; // disable batcache
}
if ( substr( $_SERVER['REQUEST_URI'], - 10 ) == 'robots.txt' ) {
$batcache['max_age'] = 0; // disable batcache
}
/* Disable batcache for /feed/ requests where there is no query string set
* We cache these responses in nginx and having multiple layers of caching
* with no batcache invalidations results in stale data being served and
* subsequently cached
* */
if ( $_SERVER['REQUEST_URI'] == '/feed/' && $_SERVER['QUERY_STRING'] == '' ) {
$batcache['max_age'] = 0;
} // disable batcache

if ( $_SERVER['REQUEST_URI'] == '/sitemap.xml' && $_SERVER['QUERY_STRING'] == '' ) {
$batcache['max_age'] = 0;
} // disable batcache

// Mobile batcache
if ( jetpack_is_mobile() && isset( $_COOKIE['akm_mobile'] ) ) {
$batcache['unique']['mobile-force'] = $_COOKIE['akm_mobile'] == 'true' ? 'true' : 'false';
} // the akm_mobile cookie is used to force the desktop version of the site when on a mobile device

if ( Jetpack_User_Agent_Info::is_blackbeberry() ) {
$batcache['unique']['mobile'] = 'blackberry';
} elseif ( Jetpack_User_Agent_Info::is_WindowsPhone7() ) {
$batcache['unique']['mobile'] = 'windows-phone7';
} elseif ( Jetpack_User_Agent_Info::is_S60_OSSBrowser() ) {
$batcache['unique']['mobile'] = 'dumb';
} elseif ( in_array( jetpack_is_mobile( 'smart', true ), array( 'iphone', 'ipod' ) ) ) {
$batcache['unique']['mobile'] = 'iphone';
} elseif ( jetpack_is_mobile( 'smart' ) ) {
$batcache['unique']['mobile'] = 'smart';
} elseif ( jetpack_is_mobile( 'dumb' ) ) {
$batcache['unique']['mobile'] = 'dumb';
}

// iPad
if ( Jetpack_User_Agent_Info::is_ipad() ) {
if ( false !== strpos( strtolower( $_SERVER['HTTP_USER_AGENT'] ), 'safari' ) ) {
$batcache['unique']['ipad'] = 'ipad-safari';
} else {
$batcache['unique']['ipad'] = 'ipad';
}
} elseif ( Jetpack_User_Agent_Info::is_tablet() ) {
// Tablets
// Should be treated differently from mobile and iPad
$batcache['unique']['tablet'] = 'tablet';
}

// UppSite / MySiteApp
if ( ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( strtolower( $_SERVER['HTTP_USER_AGENT'] ), 'mysiteapp' ) ) || isset( $_COOKIE['uppsite_theme_select'] ) ) {
$batcache['max_age'] = 0;
} // disable batcache

// Chrome Frame
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( strtolower( $_SERVER['HTTP_USER_AGENT'] ), 'chromeframe' ) ) {
$batcache['unique']['chromeframe'] = true;
}

// Liveblog
if ( preg_match( '%__liveblog_\d+/\d+/\d+|/liveblog/\d+/\d+/?$%', $_SERVER['REQUEST_URI'] ) ) {
// Liveblog requests include the current time (to the second), so it's important that we don't
// wait and that we cache starting from the first request
$batcache['seconds'] = 0;
$batcache['times'] = 0;
// A cached entry should be requested only for a couple of seconds (+/- clock errors), we don't need the whole 5 minutes
$batcache['max_age'] = 30;
}

// GET param caching: start
// Never cache if there is a query string unless the script is whitelisted here
// But, some sites/features have GET params deeply integrated with their sites for various reasons. Add overrides below to cache them
$_batcache_cache_get_requests = false;

// Finally, should we cache GET requests?
if ( ! empty( $_GET ) && false === $_batcache_cache_get_requests ) {
$batcache['max_age'] = 0;
}
// GET param caching: end

// Finally, blanket exceptions override all other configs

// Never batcache when run from CLI
if ( isset( $_SERVER['argv'] ) ) {
$batcache['max_age'] = 0;
}

// CampTix restricted content token cookie
if ( isset( $_COOKIE['tix_view_token'] ) ) {
$batcache['max_age'] = 0;
}

$batcache['headers']['X-nananana'] = 'Batcache';

unset( $_batcache_script_name );

// VIP Quickstart: Fix module activation
$_GET['module'] = $_module;

// UNCOMMENT THIS LINE TO DISABLE batcache -- KEEP THIS LAST
// $batcache['max_age'] = 0;

+ 1469
- 0
config/is-mobile.php
File diff suppressed because it is too large
View File


+ 132
- 0
config/roles.php View File

@@ -0,0 +1,132 @@
<?php

$wp_user_roles = array(
'administrator' =>
array(
'name' => 'Administrator',
'capabilities' =>
array(
'switch_themes' => true,
'edit_themes' => true,
'edit_theme_options' => true,
'activate_plugins' => true,
'edit_plugins' => true,
'edit_users' => true,
'edit_files' => true,
'manage_options' => true,
'moderate_comments' => true,
'manage_categories' => true,
'manage_links' => true,
'upload_files' => true,
'import' => true,
'edit_posts' => true,
'edit_others_posts' => true,
'edit_published_posts' => true,
'publish_posts' => true,
'edit_pages' => true,
'read' => true,
'level_10' => true,
'level_9' => true,
'level_8' => true,
'level_7' => true,
'level_6' => true,
'level_5' => true,
'level_4' => true,
'level_3' => true,
'level_2' => true,
'level_1' => true,
'level_0' => true,
'list_users' => true,
'edit_others_pages' => true,
'edit_published_pages' => true,
'publish_pages' => true,
'delete_pages' => true,
'delete_others_pages' => true,
'delete_published_pages' => true,
'delete_posts' => true,
'delete_others_posts' => true,
'delete_published_posts' => true,
'delete_private_posts' => true,
'edit_private_posts' => true,
'read_private_posts' => true,
'delete_private_pages' => true,
'edit_private_pages' => true,
'read_private_pages' => true,
'delete_users' => true,
'create_users' => true,
'remove_users' => true,
'add_users' => true,
'promote_users' => true,
'export' => true,
),
),
'editor' =>
array(
'name' => 'Editor',
'capabilities' =>
array(
'moderate_comments' => true,
'manage_categories' => true,
'manage_links' => true,
'upload_files' => true,
'edit_posts' => true,
'edit_others_posts' => true,
'edit_published_posts' => true,
'publish_posts' => true,
'edit_pages' => true,
'read' => true,
'level_7' => true,
'level_6' => true,
'level_5' => true,
'level_4' => true,
'level_3' => true,
'level_2' => true,
'level_1' => true,
'level_0' => true,
'edit_others_pages' => true,
'edit_published_pages' => true,
'publish_pages' => true,
'delete_pages' => true,
'delete_others_pages' => true,
'delete_published_pages' => true,
'delete_posts' => true,
'delete_others_posts' => true,
'delete_published_posts' => true,
'delete_private_posts' => true,
'edit_private_posts' => true,
'read_private_posts' => true,
'delete_private_pages' => true,
'edit_private_pages' => true,
'read_private_pages' => true,
),
),
'author' =>
array(
'name' => 'Author',
'capabilities' =>
array(
'upload_files' => true,
'edit_posts' => true,
'edit_published_posts' => true,
'publish_posts' => true,
'read' => true,
'level_2' => true,
'level_1' => true,
'level_0' => true,
'delete_posts' => true,
'delete_published_posts' => true,
),
),
'contributor' =>
array(
'name' => 'Contributor',
'capabilities' =>
array(
'edit_posts' => true,
'read' => true,
'level_1' => true,
'level_0' => true,
'delete_posts' => true,
),
),
);

+ 23
- 0
config/vip-config.php View File

@@ -0,0 +1,23 @@
<?php

/**
* Disable unfiltered HTML; this a default in Multisite, but we should extra enforce it
*/
define( 'DISALLOW_UNFILTERED_HTML', true );

/**
* Disable file editing and WordPress updates
*/
define( 'DISALLOW_FILE_MODS', true );

/**
* (Sort-of) Compat with wpcom-geo config
*
* On WP.com, this is automatically detected for the visiting user.
* Here, we fake it for performance reasons.
*/
if ( defined( 'GEOIP_COUNTRY_CODE_OVERRIDE' ) ) {
$_SERVER["GEOIP_COUNTRY_CODE"] = GEOIP_COUNTRY_CODE_OVERRIDE;
} else {
$_SERVER["GEOIP_COUNTRY_CODE"] = 'us';
}

+ 4
- 0
index.php View File

@@ -0,0 +1,4 @@
<?php
// WordPress view bootstrapper
define( 'WP_USE_THEMES', true );
require( './wp/wp-blog-header.php' );

+ 10
- 0
local-config.php View File

@@ -0,0 +1,10 @@
<?php
define( 'AUTH_KEY', 'CO}Ah,1O.u{ovw+:.@<p/[>no_mfb0|1Cd^V{kC-X,t>&WZ~)ht1|&:QE3soMvl<' );
define( 'SECURE_AUTH_KEY', 'aE6t3a-J#{7~&#i8uy2_SFQi#MW;@z[e}.N?c[S9ip=k|n-|#kbaktT8>Pm@ 9(2' );
define( 'LOGGED_IN_KEY', 'b<*l+; aUrQ{}Y{])NG>+l;#86.[3LKfb%*~?$JNz~4l1/{o-FN$@Z&]W%go:V%+' );
define( 'NONCE_KEY', '+;qt.HMMf^%9cr5p$.RfeSW5rN&]3-#_/d-%|5AUc>BhqqgK(Si2A,4C)jU`e+lm' );
define( 'AUTH_SALT', 'X3|FjjH-e5x2 sk|U)o!{eL*Tn$t;AejT%:Uwk,zVZ?^ZT%-4v8YacLUy;^a$[:' );
define( 'SECURE_AUTH_SALT', 'LE!WgR-u>lt?oax+QnHH1+:i#ko*iAx0Ug]B7+nwxM{%z+ofm8r-)eAciA$yPIa4' );
define( 'LOGGED_IN_SALT', 'cCRZC/@r<2un8-BPRbL]UG]HE@XK-avZr &sHNSu8P_J $6VYA:Aw~X5j-c{b&eq' );
define( 'NONCE_SALT', '>]T-8(?3Bblg_4oL0iY|e*T{~Fs6 M0Kv/ORV!6v#DcOW!lQz^40%)cTt 79SS=,' );
define( 'JETPACK_DEV_DEBUG', true );

+ 1
- 0
pv-hosts View File

@@ -0,0 +1 @@
vip.wordpress.pv

+ 28
- 0
pv-init.sh View File

@@ -0,0 +1,28 @@
#!/bin/bash

echo 'Good';

# VIP Plugin - VIP Scanner
if [ ! -d "wp-content/plugins/vip-scanner/.git" ]; then
git clone --recursive https://github.com/Automattic/vip-scanner wp-content/plugins/vip-scanner
fi

# VIP Plugin - JetPack
if [ ! -d "wp-content/plugins/jetpack/.git" ]; then
git clone https://github.com/Automattic/jetpack wp-content/plugins/jetpack
fi

# VIP Plugin - Media Explorer
if [ ! -d "wp-content/plugins/media-explorer/.git" ]; then
git clone https://github.com/Automattic/media-explorer wp-content/plugins/media-explorer
fi

# VIP Plugin - Writing Helper
if [ ! -d "wp-content/plugins/writing-helper/.git" ]; then
git clone https://github.com/automattic/writing-helper wp-content/plugins/writing-helper
fi

# VIP Plugin - VIP Scanner
if [ ! -d "wp-content/plugins/amp/.git" ]; then
git clone https://github.com/automattic/amp-wp wp-content/plugins/amp
fi

+ 1
- 0
pv-mappings View File

@@ -0,0 +1 @@
config.vm.synced_folder "www/vip", "/var/www/vip.wordpress.pv", :owner => "www-data", :mount_options => [ "dmode=775", "fmode=774" ]

+ 119
- 0
vip.wordpress.pv.pp View File

@@ -0,0 +1,119 @@
vcsrepo { '/var/www/vip.wordpress.pv/wp':
ensure => present,
provider => svn,
source => 'http://core.svn.wordpress.org/trunk/',
}

vcsrepo { '/var/www/vip.wordpress.pv/wp-tests':
ensure => present,
provider => svn,
source => 'http://develop.svn.wordpress.org/trunk/',
}

vcsrepo { '/var/www/vip.wordpress.pv/wp-content/themes/vip/plugins':
ensure => latest,
source => 'https://vip-svn.wordpress.com/plugins/',
provider => svn,
}

vcsrepo { '/var/www/vip.wordpress.pv/wp-content/themes/pub/twentyfifteen':
ensure => latest,
source => 'https://wpcom-themes.svn.automattic.com/twentyfifteen',
provider => svn,
}

$plugins = [
'log-deprecated-notices',
'monster-widget',
'query-monitor',
'user-switching',
'wordpress-importer',

# WordPress.com
'keyring',
'mrss',
'polldaddy',
'rewrite-rules-inspector',
]

$github_plugins = {
'vip-scanner' => 'https://github.com/Automattic/vip-scanner',

# WordPress.com
'jetpack' => 'https://github.com/Automattic/jetpack',
'media-explorer' => 'https://github.com/Automattic/media-explorer',
'writing-helper' => 'https://github.com/automattic/writing-helper',
'amp' => 'https://github.com/automattic/amp-wp',
}

# Delete broken plugins
file { '/var/www/vip.wordpress.pv/wp-content/plugins/log-viewer':
ensure => 'absent',
force => true,
before => Wp::Site['/var/www/vip.wordpress.pv/wp'],
}

# Install WordPress
wp::site { '/var/www/vip.wordpress.pv/wp':
url => 'vip.wordpress.pv',
sitename => 'vip.wordpress.pv',
admin_user => 'admin',
admin_password => 'password',
network => true,
require => [
Vcsrepo['/var/www/vip.wordpress.pv/wp'],
Class['php'],
]
}

# Install GitHub Plugins
$github_plugin_keys = keys( $github_plugins )
gitplugin { $github_plugin_keys:
git_urls => $github_plugins
}

# Install plugins
wp::plugin { $plugins:
location => '/var/www/vip.wordpress.pv/wp',
networkwide => true,
require => [
Wp::Site['/var/www/vip.wordpress.pv/wp'],
Gitplugin[ $github_plugin_keys ],
]
}

# Symlink db.php for Query Monitor
file { '/var/www/vip.wordpress.pv/wp-content/db.php':
ensure => 'link',
target => 'plugins/query-monitor/wp-content/db.php',
require => Wp::Plugin['query-monitor']
}

wp::option { 'siteurl':
ensure => equal,
value => 'http://vip.wordpress.pv/wp',
location => '/var/www/vip.wordpress.pv/wp',
require => Wp::Site['/var/www/vip.wordpress.pv/wp'],
}

apache::vhost { 'vip.wordpress.pv':
docroot => '/var/www/vip.wordpress.pv',
directory => '/var/www/vip.wordpress.pv',
directory_allow_override => 'All',
ssl => true,
template => '/var/vagrant/lib/conf/vhost.conf.erb',
}

mysql_database { 'tests.vip.wordpress.pv':
ensure => 'present',
charset => 'utf8',
collate => 'utf8_general_ci',
require => Class['mysql::server'],
}

mysql_database { 'vip.wordpress.pv':
ensure => 'present',
charset => 'utf8',
collate => 'utf8_general_ci',
require => Class['mysql::server'],
}

+ 2
- 0
wp-cli.yml View File

@@ -0,0 +1,2 @@
path:/var/www/vip.wordpress.pv/wp
url:vip.wordpress.pv

+ 105
- 0
wp-config.php View File

@@ -0,0 +1,105 @@
<?php

/**
* Local mods
*/
if ( file_exists( __DIR__ . '/local-config.php' ) ) {
require __DIR__ . '/local-config.php';
}

if ( ! defined( 'DB_NAME' ) ) {
define( 'DB_NAME', 'vip.wordpress.pv' );
}

if ( ! defined( 'DB_USER' ) ) {
define( 'DB_USER', 'username' );
}

if ( ! defined( 'DB_PASSWORD' ) ) {
define( 'DB_PASSWORD', 'password' );
}

define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );

/** Disable Automatic core updates. */
define( 'WP_AUTO_UPDATE_CORE', false );

/**#@-*/

/**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each a unique
* prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';

/**
* WordPress Localized Language, defaults to English.
*
* Change this to localize WordPress. A corresponding MO file for the chosen
* language must be installed to wp-content/languages. For example, install
* de_DE.mo to wp-content/languages and set WPLANG to 'de_DE' to enable German
* language support.
*/
if ( ! defined( 'WPLANG' ) ) {
define( 'WPLANG', '' );
}

define( 'WP_DEBUG', true );
define( 'SAVEQUERIES', true );

if ( ! defined( 'JETPACK_DEV_DEBUG' ) ) {
define( 'JETPACK_DEV_DEBUG', true );
}

// Put Keyring into headless mode
define( 'KEYRING__HEADLESS_MODE', true );

/* Content Directory */
define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/wp-content' );
define( 'WP_CONTENT_URL', 'http://' . $_SERVER['HTTP_HOST'] . '/wp-content' );

define( 'MULTISITE', true );
define( 'SUNRISE', true );
define( 'PATH_CURRENT_SITE', '/' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );

if ( ! defined( 'DOMAIN_CURRENT_SITE' ) ) {
define( 'DOMAIN_CURRENT_SITE', $_SERVER['HTTP_HOST'] );
}

if ( ! defined( 'SUBDOMAIN_INSTALL' ) ) {
define( 'SUBDOMAIN_INSTALL', false );
}

if ( ! defined( 'WP_DEFAULT_THEME' ) ) {
define( 'WP_DEFAULT_THEME', 'pub/twentyfifteen' );
}

define( 'WP_MEMORY_LIMIT', '64M' );
define( 'WP_MAX_MEMORY_LIMIT', '256M' );

/* That's all, stop editing! Happy blogging. */

// Use the latest Jetpack user-agent detection if we have it
if ( file_exists( __DIR__ . '/wp-content/plugins/jetpack/class.jetpack-user-agent.php' ) ) {
require_once( __DIR__ . '/wp-content/plugins/jetpack/class.jetpack-user-agent.php' );
} else {
require __DIR__ . '/config/is-mobile.php';
}

require __DIR__ . '/config/batcache-config.php';
require __DIR__ . '/config/roles.php';
require __DIR__ . '/config/vip-config.php';

/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', dirname( __FILE__ ) . '/wp/' );
}

/** Sets up WordPress vars and included files. */
require_once( ABSPATH . 'wp-settings.php' );

+ 523
- 0
wp-content/advanced-cache.php View File

@@ -0,0 +1,523 @@
<?php
if ( is_readable( dirname( __FILE__ ) . '/batcache-stats.php' ) )
require_once dirname( __FILE__ ) . '/batcache-stats.php';

if ( !function_exists( 'batcache_stats' ) ) {
function batcache_stats( $name, $value, $num = 1, $today = FALSE, $hour = FALSE ) { }
}

// nananananananananananananananana BATCACHE!!!

function batcache_cancel() {
global $batcache;

if ( is_object($batcache) )
$batcache->cancel = true;
}

// Variants can be set by functions which use early-set globals like $_SERVER to run simple tests.
// Functions defined in WordPress, plugins, and themes are not available and MUST NOT be used.
// Example: vary_cache_on_function('return preg_match("/feedburner/i", $_SERVER["HTTP_USER_AGENT"]);');
// This will cause batcache to cache a variant for requests from Feedburner.
// Tips for writing $function:
// X_X DO NOT use any functions from your theme or plugins. Those files have not been included. Fatal error.
// X_X DO NOT use any WordPress functions except is_admin() and is_multisite(). Fatal error.
// X_X DO NOT include or require files from anywhere without consulting expensive professionals first. Fatal error.
// X_X DO NOT use $wpdb, $blog_id, $current_user, etc. These have not been initialized.
// ^_^ DO understand how create_function works. This is how your code is used: create_function('', $function);
// ^_^ DO remember to return something. The return value determines the cache variant.
function vary_cache_on_function($function) {
global $batcache;

if ( preg_match('/include|require|echo|print|dump|export|open|sock|unlink|`|eval/i', $function) )
die('Illegal word in variant determiner.');

if ( !preg_match('/\$_/', $function) )
die('Variant determiner should refer to at least one $_ variable.');

$batcache->add_variant($function);
}

class batcache {
// This is the base configuration. You can edit these variables or move them into your wp-config.php file.
var $max_age = 300; // Expire batcache items aged this many seconds (zero to disable batcache)

var $remote = 0; // Zero disables sending buffers to remote datacenters (req/sec is never sent)

var $times = 2; // Only batcache a page after it is accessed this many times... (two or more)
var $seconds = 120; // ...in this many seconds (zero to ignore this and use batcache immediately)

var $group = 'batcache'; // Name of memcached group. You can simulate a cache flush by changing this.

var $unique = array(); // If you conditionally serve different content, put the variable values here.

var $vary = array(); // Array of functions for create_function. The return value is added to $unique above.

var $headers = array(); // Add headers here as name=>value or name=>array(values). These will be sent with every response from the cache.

var $cache_redirects = false; // Set true to enable redirect caching.
var $redirect_status = false; // This is set to the response code during a redirect.
var $redirect_location = false; // This is set to the redirect location.

var $uncached_headers = array('transfer-encoding'); // These headers will never be cached. Apply strtolower.

var $debug = true; // Set false to hide the batcache info <!-- comment -->

var $cache_control = true; // Set false to disable Last-Modified and Cache-Control headers

var $cancel = false; // Change this to cancel the output buffer. Use batcache_cancel();

var $noskip_cookies = array( 'wordpress_test_cookie' ); // Names of cookies - if they exist and the cache would normally be bypassed, don't bypass it

var $genlock = false;
var $do = false;

function batcache( $settings ) {
if ( is_array( $settings ) ) foreach ( $settings as $k => $v )
$this->$k = $v;
}

function is_ssl() {
if ( isset($_SERVER['HTTPS']) ) {
if ( 'on' == strtolower($_SERVER['HTTPS']) )
return true;
if ( '1' == $_SERVER['HTTPS'] )
return true;
} elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
return true;
}
return false;
}

function status_header( $status_header, $status_code ) {
$this->status_header = $status_header;
$this->status_code = $status_code;

return $status_header;
}

function redirect_status( $status, $location ) {
if ( $this->cache_redirects ) {
$this->redirect_status = $status;
$this->redirect_location = $location;
}

return $status;
}

function do_headers( $headers1, $headers2 = array() ) {
// Merge the arrays of headers into one
$headers = array();
$keys = array_unique( array_merge( array_keys( $headers1 ), array_keys( $headers2 ) ) );
foreach ( $keys as $k ) {
$headers[$k] = array();
if ( isset( $headers1[$k] ) && isset( $headers2[$k] ) )
$headers[$k] = array_merge( (array) $headers2[$k], (array) $headers1[$k] );
elseif ( isset( $headers2[$k] ) )
$headers[$k] = (array) $headers2[$k];
else
$headers[$k] = (array) $headers1[$k];
$headers[$k] = array_unique( $headers[$k] );
}
// These headers take precedence over any previously sent with the same names
foreach ( $headers as $k => $values ) {
$clobber = true;
foreach ( $values as $v ) {
header( "$k: $v", $clobber );
$clobber = false;
}
}
}

function configure_groups() {
// Configure the memcached client
if ( ! $this->remote )
if ( function_exists('wp_cache_add_no_remote_groups') )
wp_cache_add_no_remote_groups(array($this->group));
if ( function_exists('wp_cache_add_global_groups') )
wp_cache_add_global_groups(array($this->group));
}

// Defined here because timer_stop() calls number_format_i18n()
function timer_stop($display = 0, $precision = 3) {
global $timestart, $timeend;
$mtime = microtime();
$mtime = explode(' ',$mtime);
$mtime = $mtime[1] + $mtime[0];
$timeend = $mtime;
$timetotal = $timeend-$timestart;
$r = number_format($timetotal, $precision);
if ( $display )
echo $r;
return $r;
}

function ob($output) {
if ( $this->cancel !== false )
return $output;

// PHP5 and objects disappearing before output buffers?
wp_cache_init();

// Remember, $wp_object_cache was clobbered in wp-settings.php so we have to repeat this.
$this->configure_groups();

// Do not batcache blank pages unless they are HTTP redirects
$output = trim($output);
if ( $output === '' && (!$this->redirect_status || !$this->redirect_location) )
return;

// Do not cache 5xx responses
if ( isset( $this->status_code ) && intval($this->status_code / 100) == 5 )
return $output;

$this->do_variants($this->vary);
$this->generate_keys();

// Construct and save the batcache
$this->cache = array(
'output' => $output,
'time' => time(),
'timer' => $this->timer_stop(false, 3),
'headers' => array(),
'status_header' => $this->status_header,
'redirect_status' => $this->redirect_status,
'redirect_location' => $this->redirect_location,
'version' => $this->url_version
);

foreach ( headers_list() as $header ) {
list($k, $v) = array_map('trim', explode(':', $header, 2));
$this->cache['headers'][$k][] = $v;
}

if ( !empty( $this->cache['headers'] ) && !empty( $this->uncached_headers ) ) {
foreach ( $this->uncached_headers as $header )
unset( $this->cache['headers'][$header] );
}

foreach ( $this->cache['headers'] as $header => $values ) {
// Do not cache if cookies were set
if ( strtolower( $header ) === 'set-cookie' )
return $output;

foreach ( (array) $values as $value )
if ( preg_match('/^Cache-Control:.*max-?age=(\d+)/i', "$header: $value", $matches) )
$this->max_age = intval($matches[1]);
}

$this->cache['max_age'] = $this->max_age;

wp_cache_set($this->key, $this->cache, $this->group, $this->max_age + $this->seconds + 30);

// Unlock regeneration
wp_cache_delete("{$this->url_key}_genlock", $this->group);

if ( $this->cache_control ) {
// Don't clobber Last-Modified header if already set, e.g. by WP::send_headers()
if ( !isset($this->cache['headers']['Last-Modified']) )
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $this->cache['time'] ) . ' GMT', true );
if ( !isset($this->cache['headers']['Cache-Control']) )
header("Cache-Control: max-age=$this->max_age, must-revalidate", false);
}

$this->do_headers( $this->headers );

// Add some debug info just before <head
if ( $this->debug ) {
$this->add_debug_just_cached();
}

// Pass output to next ob handler
batcache_stats( 'batcache', 'total_page_views' );
return $this->cache['output'];
}

function add_variant($function) {
$key = md5($function);
$this->vary[$key] = $function;
}

function do_variants($dimensions = false) {
// This function is called without arguments early in the page load, then with arguments during the OB handler.
if ( $dimensions === false )
$dimensions = wp_cache_get("{$this->url_key}_vary", $this->group);
else
wp_cache_set("{$this->url_key}_vary", $dimensions, $this->group, $this->max_age + 10);

if ( is_array($dimensions) ) {
ksort($dimensions);
foreach ( $dimensions as $key => $function ) {
$fun = create_function('', $function);
$value = $fun();
$this->keys[$key] = $value;
}
}
}

function generate_keys() {
// ksort($this->keys); // uncomment this when traffic is slow
$this->key = md5(serialize($this->keys));
$this->req_key = $this->key . '_req';
}

function add_debug_just_cached() {
$generation = $this->cache['timer'];
$bytes = strlen( serialize( $this->cache ) );
$html = <<<HTML
<!--
generated in $generation seconds
$bytes bytes batcached for {$this->max_age} seconds
-->

HTML;
$this->add_debug_html_to_output( $html );
}

function add_debug_from_cache() {
$seconds_ago = time() - $this->cache['time'];
$generation = $this->cache['timer'];
$serving = $this->timer_stop( false, 3 );
$expires = $this->cache['max_age'] - time() + $this->cache['time'];
$html = <<<HTML
<!--
generated $seconds_ago seconds ago
generated in $generation seconds
served from batcache in $serving seconds
expires in $expires seconds
-->

HTML;
$this->add_debug_html_to_output( $html );
}

function add_debug_html_to_output( $debug_html ) {
// Casing on the Content-Type header is inconsistent
foreach ( array( 'Content-Type', 'Content-type' ) as $key ) {
if ( isset( $this->cache['headers'][ $key ][0] ) && 0 !== strpos( $this->cache['headers'][ $key ][0], 'text/html' ) )
return;
}

$head_position = strpos( $this->cache['output'], '<head' );
if ( false === $head_position ) {
return;
}
$this->cache['output'] = substr_replace( $this->cache['output'], $debug_html, $head_position, 0 );
}
}

global $batcache;
// Pass in the global variable which may be an array of settings to override defaults.
$batcache = new batcache($batcache);

if ( ! defined( 'WP_CONTENT_DIR' ) )
return;

// Never batcache interactive scripts or API endpoints.
if ( in_array(
basename( $_SERVER['SCRIPT_FILENAME'] ),
array(
'wp-app.php',
'xmlrpc.php',
) ) )
return;

// Never batcache WP javascript generators
if ( strstr( $_SERVER['SCRIPT_FILENAME'], 'wp-includes/js' ) )
return;

// Never batcache when POST data is present.
if ( ! empty( $GLOBALS['HTTP_RAW_POST_DATA'] ) || ! empty( $_POST ) )
return;

// Never batcache when cookies indicate a cache-exempt visitor.
if ( is_array( $_COOKIE) && ! empty( $_COOKIE ) ) {
foreach ( array_keys( $_COOKIE ) as $batcache->cookie ) {
if ( ! in_array( $batcache->cookie, $batcache->noskip_cookies ) && ( substr( $batcache->cookie, 0, 2 ) == 'wp' || substr( $batcache->cookie, 0, 9 ) == 'wordpress' || substr( $batcache->cookie, 0, 14 ) == 'comment_author' ) ) {
batcache_stats( 'batcache', 'cookie_skip' );
return;
}
}
}

if ( ! include_once( WP_CONTENT_DIR . '/object-cache.php' ) )
return;

wp_cache_init(); // Note: wp-settings.php calls wp_cache_init() which clobbers the object made here.

if ( ! is_object( $wp_object_cache ) )
return;

// Now that the defaults are set, you might want to use different settings under certain conditions.

/* Example: if your documents have a mobile variant (a different document served by the same URL) you must tell batcache about the variance. Otherwise you might accidentally cache the mobile version and serve it to desktop users, or vice versa.
$batcache->unique['mobile'] = is_mobile_user_agent();
*/

/* Example: never batcache for this host
if ( $_SERVER['HTTP_HOST'] == 'do-not-batcache-me.com' )
return;
*/

/* Example: batcache everything on this host regardless of traffic level
if ( $_SERVER['HTTP_HOST'] == 'always-batcache-me.com' )
return;
*/

/* Example: If you sometimes serve variants dynamically (e.g. referrer search term highlighting) you probably don't want to batcache those variants. Remember this code is run very early in wp-settings.php so plugins are not yet loaded. You will get a fatal error if you try to call an undefined function. Either include your plugin now or define a test function in this file.
if ( include_once( 'plugins/searchterm-highlighter.php') && referrer_has_search_terms() )
return;
*/

// Disabled
if ( $batcache->max_age < 1 )
return;

// Make sure we can increment. If not, turn off the traffic sensor.
if ( ! method_exists( $GLOBALS['wp_object_cache'], 'incr' ) )
$batcache->times = 0;

// Necessary to prevent clients using cached version after login cookies set. If this is a problem, comment it out and remove all Last-Modified headers.
header('Vary: Cookie', false);

// Things that define a unique page.
if ( isset( $_SERVER['QUERY_STRING'] ) )
parse_str($_SERVER['QUERY_STRING'], $batcache->query);

$batcache->keys = array(
'host' => $_SERVER['HTTP_HOST'],
'method' => $_SERVER['REQUEST_METHOD'],
'path' => ( $batcache->pos = strpos($_SERVER['REQUEST_URI'], '?') ) ? substr($_SERVER['REQUEST_URI'], 0, $batcache->pos) : $_SERVER['REQUEST_URI'],
'query' => $batcache->query,
'extra' => $batcache->unique
);

if ( $batcache->is_ssl() )
$batcache->keys['ssl'] = true;

// Recreate the permalink from the URL
$batcache->permalink = 'http://' . $batcache->keys['host'] . $batcache->keys['path'] . ( isset($batcache->keys['query']['p']) ? "?p=" . $batcache->keys['query']['p'] : '' );
$batcache->url_key = md5($batcache->permalink);
$batcache->url_version = (int) wp_cache_get("{$batcache->url_key}_version", $batcache->group);
$batcache->configure_groups();
$batcache->do_variants();
$batcache->generate_keys();

// Get the batcache
$batcache->cache = wp_cache_get($batcache->key, $batcache->group);

// Are we only caching frequently-requested pages?
if ( $batcache->seconds < 1 || $batcache->times < 2 ) {
$batcache->do = true;
} else {
// No batcache item found, or ready to sample traffic again at the end of the batcache life?
if ( !is_array($batcache->cache) || time() >= $batcache->cache['time'] + $batcache->max_age - $batcache->seconds ) {
wp_cache_add($batcache->req_key, 0, $batcache->group);
$batcache->requests = wp_cache_incr($batcache->req_key, 1, $batcache->group);

if ( $batcache->requests >= $batcache->times )
$batcache->do = true;
else
$batcache->do = false;
}
}

// If the document has been updated and we are the first to notice, regenerate it.
if ( $batcache->do !== false && isset($batcache->cache['version']) && $batcache->cache['version'] < $batcache->url_version )
$batcache->genlock = wp_cache_add("{$batcache->url_key}_genlock", 1, $batcache->group, 10);

// Temporary: remove after 2010-11-12. I added max_age to the cache. This upgrades older caches on the fly.
if ( !isset($batcache->cache['max_age']) )
$batcache->cache['max_age'] = $batcache->max_age;


// Did we find a batcached page that hasn't expired?
if ( isset($batcache->cache['time']) && ! $batcache->genlock && time() < $batcache->cache['time'] + $batcache->cache['max_age'] ) {
// Issue redirect if cached and enabled
if ( $batcache->cache['redirect_status'] && $batcache->cache['redirect_location'] && $batcache->cache_redirects ) {
$status = $batcache->cache['redirect_status'];
$location = $batcache->cache['redirect_location'];
// From vars.php
$is_IIS = (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false);

$batcache->do_headers( $batcache->headers );
if ( $is_IIS ) {
header("Refresh: 0;url=$location");
} else {
if ( php_sapi_name() != 'cgi-fcgi' ) {
$texts = array(
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Reserved',
307 => 'Temporary Redirect',
);
$protocol = $_SERVER["SERVER_PROTOCOL"];
if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
$protocol = 'HTTP/1.0';
if ( isset($texts[$status]) )
header("$protocol $status " . $texts[$status]);
else
header("$protocol 302 Found");
}
header("Location: $location");
}
exit;
}

// Respect ETags served with feeds.
$three04 = false;
if ( isset( $SERVER['HTTP_IF_NONE_MATCH'] ) && isset( $batcache->cache['headers']['ETag'][0] ) && $_SERVER['HTTP_IF_NONE_MATCH'] == $batcache->cache['headers']['ETag'][0] )
$three04 = true;

// Respect If-Modified-Since.
elseif ( $batcache->cache_control && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) {
$client_time = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
if ( isset($batcache->cache['headers']['Last-Modified'][0]) )
$cache_time = strtotime($batcache->cache['headers']['Last-Modified'][0]);
else
$cache_time = $batcache->cache['time'];

if ( $client_time >= $cache_time )
$three04 = true;
}

// Use the batcache save time for Last-Modified so we can issue "304 Not Modified" but don't clobber a cached Last-Modified header.
if ( $batcache->cache_control && !isset($batcache->cache['headers']['Last-Modified'][0]) ) {
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $batcache->cache['time'] ) . ' GMT', true );
header('Cache-Control: max-age=' . ($batcache->cache['max_age'] - time() + $batcache->cache['time']) . ', must-revalidate', true);
}

// Add some debug info just before </head>
if ( $batcache->debug ) {
$batcache->add_debug_from_cache();
}

$batcache->do_headers( $batcache->headers, $batcache->cache['headers'] );

if ( $three04 ) {
header("HTTP/1.1 304 Not Modified", true, 304);
die;
}

if ( !empty($batcache->cache['status_header']) )
header($batcache->cache['status_header'], true);

// Have you ever heard a death rattle before?
die($batcache->cache['output']);
}

// Didn't meet the minimum condition?
if ( !$batcache->do && !$batcache->genlock )
return;

$wp_filter['status_header'][10]['batcache'] = array( 'function' => array(&$batcache, 'status_header'), 'accepted_args' => 2 );
$wp_filter['wp_redirect_status'][10]['batcache'] = array( 'function' => array(&$batcache, 'redirect_status'), 'accepted_args' => 2 );

ob_start(array(&$batcache, 'ob'));

// It is safer to omit the final PHP closing tag.


+ 2
- 0
wp-content/index.php View File

@@ -0,0 +1,2 @@
<?php
// Silence is golden.

+ 97
- 0
wp-content/mu-plugins/alloptions-limit.php View File

@@ -0,0 +1,97 @@
<?php

function sanity_check_alloptions( $alloptions ) {

if ( defined( 'WP_CLI' ) && WP_CLI )
return $alloptions;

// Warn should *always* be =< die
$alloptions_size_warn = 750000;
$alloptions_size_die = 1000000; // 1000000 ~ 1MB, too big for memcache
static $alloptions_size = null; // Avoids repeated cache requests
if ( !$alloptions_size )
$alloptions_size = wp_cache_get( 'alloptions_size' );
if ( $alloptions_size > $alloptions_size_die ) {
sanity_check_alloptions_die( $alloptions_size, $alloptions );
}
if ( !$alloptions_size ) {
$alloptions_size = strlen( serialize( $alloptions ) );
wp_cache_add( 'alloptions_size', $alloptions_size, '', 60 );
if ( $alloptions_size > $alloptions_size_warn ) {
if ( $alloptions_size > $alloptions_size_die )
sanity_check_alloptions_die( $alloptions_size, $alloptions );
// Warn if we haven't died already
sanity_check_alloptions_notify( $alloptions_size, $alloptions );
}
}
return $alloptions;
}
add_filter( 'alloptions', 'sanity_check_alloptions' );

function sanity_check_alloptions_die( $size, $alloptions ) {
sanity_check_alloptions_notify( $size, $alloptions, true );
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head profile="http://gmpg.org/xfn/11">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Something went wrong &#8212; Option Error</title>
<style type="text/css">
h1 {
font-weight: normal;
font-size: 40px;
}
body {
line-height: 1.6em; font-family: Georgia, serif; width: 390px; margin: auto;
text-align: center;
}
.message {
font-size: 22px;
width: 400px;
margin: 10px auto;
}
</style>
<script type="text/javascript" src="//stats.wp.com/wpcom.js?22"></script>
<script type="text/javascript">
_error = 'big-alloptions';
fzd();
</script>
</head>
<body>
<h1>Uh Oh!</h1>

<div class="message">

<p>Something has gone wrong with our servers. It&#8217;s probably Matt&#8217;s fault.</p>

<p>We&#8217;ve just been notified of the problem.</p>

<p>Hopefully this should be fixed ASAP, so kindly reload in a few minutes and things should be back to normal.</p>

</div>
</body>
</html>
<?php
exit;
}

function sanity_check_alloptions_notify( $size, $alloptions, $blocked = false ) {
global $wpdb, $current_blog;

// Rate limit the alerts to avoid flooding
if ( false !== wp_cache_get( 'alloptions', 'throttle' ) )
return;

wp_cache_add( 'alloptions', 1, 'throttle', 5 * MINUTE_IN_SECONDS );

if ( $blocked )
$msg = "This site is now BLOCKED from loading until option sizes are under control.";
else
$msg = "Site will be blocked from loading if option sizes get too much bigger.";

$log = esc_url( $_SERVER['HTTP_HOST'] ) . " - {$wpdb->blogid} options is up to " . number_format( $size ) . ' ' . $msg . ' #vipoptions';
error_log( $log );
}

+ 46
- 0
wp-content/mu-plugins/batcache.php View File

@@ -0,0 +1,46 @@
<?php
/*
Plugin name: Batcache Manager
Plugin URI: http://wordpress.org/extend/plugins/batcache/
Description: This optional plugin improves Batcache.
Author: Andy Skelton
Author URI: http://andyskelton.com/
Version: 1.2
*/

// Do not load if our advanced-cache.php isn't loaded
if ( ! isset( $batcache ) || ! is_object( $batcache ) || ! method_exists( $wp_object_cache, 'incr' ) ) {
return;
}

$batcache->configure_groups();

// Regen home and permalink on posts and pages
#add_action('clean_post_cache', 'batcache_post');

// Regen permalink on comments (TODO)
//add_action('comment_post', 'batcache_comment');
//add_action('wp_set_comment_status', 'batcache_comment');
//add_action('edit_comment', 'batcache_comment');

function batcache_post($post_id) {
global $batcache;

$post = get_post($post_id);
if ( empty( $post ) || $post->post_type == 'revision' || get_post_status($post_id) != 'publish' )
return;

batcache_clear_url( get_option('home') );
batcache_clear_url( trailingslashit( get_option('home') ) );
batcache_clear_url( get_permalink($post_id) );
}

function batcache_clear_url($url) {
global $batcache;
if ( empty($url) )
return false;
$url_key = md5($url);
wp_cache_add("{$url_key}_version", 0, $batcache->group);
return wp_cache_incr("{$url_key}_version", 1, $batcache->group);
}


+ 193
- 0
wp-content/mu-plugins/http-concat/cssconcat.php View File

@@ -0,0 +1,193 @@
<?php
/*
Plugin Name: CSS Concat
Plugin URI: http://wp-plugins.org/#
Description: Concatenates CSS
Author: Automattic
Version: 0.01
Author URI: http://automattic.com/
*/

if ( ! defined( 'ALLOW_GZIP_COMPRESSION' ) )
define( 'ALLOW_GZIP_COMPRESSION', true );

class WPcom_CSS_Concat extends WP_Styles {
private $old_styles;
public $allow_gzip_compression;

function __construct( $styles ) {
$this->old_styles = $styles;

// Unset all the object properties except our private copy of the styles object.
// We have to unset everything so that the overload methods talk to $this->old_styles->whatever
// instead of $this->whatever.
foreach ( array_keys( get_object_vars( $this ) ) as $key ) {
if ( 'old_styles' === $key ) {
continue;
}
unset( $this->$key );
}

parent::__construct();
}

function do_items( $handles = false, $group = false ) {
$handles = false === $handles ? $this->queue : (array) $handles;
$stylesheets = array();
$siteurl = site_url();

$this->all_deps( $handles );

$stylesheet_group_index = 0;
foreach( $this->to_do as $key => $handle ) {
$obj = $this->registered[$handle];
$obj->src = apply_filters( 'style_loader_src', $obj->src, $obj->handle );

// Core is kind of broken and returns "true" for src of "colors" handle
// http://core.trac.wordpress.org/attachment/ticket/16827/colors-hacked-fixed.diff
// http://core.trac.wordpress.org/ticket/20729
if ( 'colors' == $obj->handle && true === $obj->src ) {
$css_url = parse_url( wp_style_loader_src( $obj->src, $obj->handle ) );
} else {
$css_url = parse_url( $obj->src );
}
$extra = $obj->extra;

// Don't concat by default
$do_concat = false;

// Only try to concat static css files
if ( false !== strpos( $css_url['path'], '.css' ) )
$do_concat = true;

// Don't try to concat styles which are loaded conditionally (like IE stuff)
if ( isset( $extra['conditional'] ) )
$do_concat = false;

// Don't concat rtl stuff for now until concat supports it correctly
if ( 'rtl' === $this->text_direction && ! empty( $extra['rtl'] ) )
$do_concat = false;

// Don't try to concat externally hosted scripts
if ( ( isset( $css_url['host'] ) && ( preg_replace( '/https?:\/\//', '', $siteurl ) != $css_url['host'] ) ) )
$do_concat = false;

// Concat and canonicalize the paths only for
// existing scripts that aren't outside ABSPATH
$css_realpath = realpath( ABSPATH . $css_url['path'] );
if ( ! $css_realpath || 0 !== strpos( $css_realpath, ABSPATH ) )
$do_concat = false;
else
$css_url['path'] = substr( $css_realpath, strlen( ABSPATH ) - 1 );

// Allow plugins to disable concatenation of certain stylesheets.
$do_concat = apply_filters( 'css_do_concat', $do_concat, $handle );

if ( true === $do_concat ) {
$media = $obj->args;
if ( empty( $media ) ) {
$media = 'all';
}
if ( empty( $stylesheets[ $stylesheet_group_index ] ) || ! is_array( $stylesheets[ $stylesheet_group_index ] ) ) {
$stylesheets[ $stylesheet_group_index ] = array();
}

$stylesheets[ $stylesheet_group_index ][ $media ][ $handle ] = $css_url['path'];
$this->done[] = $handle;
} else {
$stylesheet_group_index++;
$stylesheets[ $stylesheet_group_index ][ 'noconcat' ][] = $handle;
$stylesheet_group_index++;
}
unset( $this->to_do[$key] );
}

foreach( $stylesheets as $idx => $stylesheets_group ) {
foreach( $stylesheets_group as $media => $css ) {
if ( 'noconcat' == $media ) {

foreach( $css as $handle ) {
if ( $this->do_item( $handle, $group ) )
$this->done[] = $handle;
}
continue;
} elseif ( count( $css ) > 1) {
$paths = array_map( function( $url ) { return ABSPATH . $url; }, $css );
$mtime = max( array_map( 'filemtime', $paths ) );
$path_str = implode( $css, ',' ) . "?m=${mtime}j";

if ( $this->allow_gzip_compression ) {
$path_64 = base64_encode( gzcompress( $path_str ) );
if ( strlen( $path_str ) > ( strlen( $path_64 ) + 1 ) )
$path_str = '-' . $path_64;
}

$href = $siteurl . "/_static/??" . $path_str;
} else {
$href = $this->cache_bust_mtime( $siteurl . current( $css ) );
}

echo apply_filters( 'style_loader_tag', "<link rel='stylesheet' id='$media-css-$idx' href='$href' type='text/css' media='$media' />\n", $handle );
array_map( array( $this, 'print_inline_style' ), array_keys( $css ) );
}
}
return $this->done;
}

function cache_bust_mtime( $url ) {
if ( strpos( $url, '?m=' ) )
return $url;

$parts = parse_url( $url );
if ( ! isset( $parts['path'] ) || empty( $parts['path'] ) )
return $url;

$file = ABSPATH . ltrim( $parts['path'], '/' );

$mtime = false;
if ( file_exists( $file ) )
$mtime = filemtime( $file );

if ( ! $mtime )
return $url;

if ( false === strpos( $url, '?' ) ) {
$q = '';
} else {
list( $url, $q ) = explode( '?', $url, 2 );
if ( strlen( $q ) )
$q = '&amp;' . $q;
}

return "$url?m={$mtime}g{$q}";
}

function __isset( $key ) {
return isset( $this->old_styles->$key );
}

function __unset( $key ) {
unset( $this->old_styles->$key );
}

function &__get( $key ) {
return $this->old_styles->$key;
}

function __set( $key, $value ) {
$this->old_styles->$key = $value;
}
}

function css_concat_init() {
global $wp_styles;

if ( ! ( $wp_styles instanceof WP_Styles ) ) {
$wp_styles = new WP_Styles();
}

$wp_styles = new WPcom_CSS_Concat( $wp_styles );
$wp_styles->allow_gzip_compression = ALLOW_GZIP_COMPRESSION;
}

add_action( 'init', 'css_concat_init' );

+ 777
- 0
wp-content/mu-plugins/http-concat/cssmin.php View File

@@ -0,0 +1,777 @@
<?php

/*!
* cssmin.php v2.4.8-4
* Author: Tubal Martin - http://tubalmartin.me/
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
*
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
* Permission is hereby granted to use the PHP version under the same
* conditions as the YUICompressor.
*/

/*!
* YUI Compressor
* http://developer.yahoo.com/yui/compressor/
* Author: Julien Lecomte - http://www.julienlecomte.net/
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
* The copyrights embodied in the content of this file are licensed
* by Yahoo! Inc. under the BSD (revised) open source license.
*/

class CSSmin
{
const NL = '___YUICSSMIN_PRESERVED_NL___';
const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';

private $comments;
private $preserved_tokens;
private $memory_limit;
private $max_execution_time;
private $pcre_backtrack_limit;
private $pcre_recursion_limit;
private $raise_php_limits;

/**
* @param bool|int $raise_php_limits
* If true, PHP settings will be raised if needed
*/
public function __construct($raise_php_limits = TRUE)
{
// Set suggested PHP limits
$this->memory_limit = 128 * 1048576; // 128MB in bytes
$this->max_execution_time = 60; // 1 min
$this->pcre_backtrack_limit = 1000 * 1000;
$this->pcre_recursion_limit = 500 * 1000;

$this->raise_php_limits = (bool) $raise_php_limits;
}

/**
* Minify a string of CSS
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
public function run($css = '', $linebreak_pos = FALSE)
{
if (empty($css)) {
return '';
}

if ($this->raise_php_limits) {
$this->do_raise_php_limits();
}

$this->comments = array();
$this->preserved_tokens = array();

$start_index = 0;
$length = strlen($css);

$css = $this->extract_data_urls($css);

// collect all comment blocks...
while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
$end_index = $this->index_of($css, '*/', $start_index + 2);
if ($end_index < 0) {
$end_index = $length;
}
$comment_found = $this->str_slice($css, $start_index + 2, $end_index);
$this->comments[] = $comment_found;
$comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
$css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
// Set correct start_index: Fixes issue #2528130
$start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
}

// preserve strings so their content doesn't get accidentally minified
$css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);

// Let's divide css code in chunks of 5.000 chars aprox.
// Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
// of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
// long strings and a (sub)pattern matches a number of chars greater than
// the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
// returning NULL and $css would be empty.
$charset = '';
$charset_regexp = '/(@charset)( [^;]+;)/i';
$css_chunks = array();
$css_chunk_length = 5000; // aprox size, not exact
$start_index = 0;
$i = $css_chunk_length; // save initial iterations
$l = strlen($css);


// if the number of characters is 5000 or less, do not chunk
if ($l <= $css_chunk_length) {
$css_chunks[] = $css;
} else {
// chunk css code securely
while ($i < $l) {
$i += 50; // save iterations
if ($l - $start_index <= $css_chunk_length || $i >= $l) {
$css_chunks[] = $this->str_slice($css, $start_index);
break;
}
if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
// If there are two ending curly braces }} separated or not by spaces,
// join them in the same chunk (i.e. @media blocks)
$next_chunk = substr($css, $i);
if (preg_match('/^\s*\}/', $next_chunk)) {
$i = $i + $this->index_of($next_chunk, '}') + 1;
}

$css_chunks[] = $this->str_slice($css, $start_index, $i);
$start_index = $i;
}
}
}

// Minify each chunk
for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
$css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
// Keep the first @charset at-rule found
if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
$charset = strtolower($matches[1]) . $matches[2];
}
// Delete all @charset at-rules
$css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
}

// Update the first chunk and push the charset to the top of the file.
$css_chunks[0] = $charset . $css_chunks[0];

return implode('', $css_chunks);
}

/**
* Sets the memory limit for this script
* @param int|string $limit
*/
public function set_memory_limit($limit)
{
$this->memory_limit = $this->normalize_int($limit);
}

/**
* Sets the maximum execution time for this script
* @param int|string $seconds
*/
public function set_max_execution_time($seconds)
{
$this->max_execution_time = (int) $seconds;
}

/**
* Sets the PCRE backtrack limit for this script
* @param int $limit
*/
public function set_pcre_backtrack_limit($limit)
{
$this->pcre_backtrack_limit = (int) $limit;
}

/**
* Sets the PCRE recursion limit for this script
* @param int $limit
*/
public function set_pcre_recursion_limit($limit)
{
$this->pcre_recursion_limit = (int) $limit;
}

/**
* Try to configure PHP to use at least the suggested minimum settings
*/
private function do_raise_php_limits()
{
$php_limits = array(
'memory_limit' => $this->memory_limit,
'max_execution_time' => $this->max_execution_time,
'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
'pcre.recursion_limit' => $this->pcre_recursion_limit
);

// If current settings are higher respect them.
foreach ($php_limits as $name => $suggested) {
$current = $this->normalize_int(ini_get($name));
// memory_limit exception: allow -1 for "no memory limit".
if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
ini_set($name, $suggested);
}
}
}

/**
* Does bulk of the minification
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
private function minify($css, $linebreak_pos)
{
// strings are safe, now wrestle the comments
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {

$token = $this->comments[$i];
$placeholder = '/' . self::COMMENT . $i . '___/';

// ! in the first position of the comment means preserve
// so push to the preserved tokens keeping the !
if (substr($token, 0, 1) === '!') {
$this->preserved_tokens[] = $token;
$token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
$css = preg_replace($placeholder, $token_tring, $css, 1);
// Preserve new lines for /*! important comments
$css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
$css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
continue;
}

// \ in the last position looks like hack for Mac/IE5
// shorten that to /*\*/ and the next one to /**/
if (substr($token, (strlen($token) - 1), 1) === '\\') {
$this->preserved_tokens[] = '\\';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
$i = $i + 1; // attn: advancing the loop
$this->preserved_tokens[] = '';
$css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
continue;
}

// keep empty comments after child selectors (IE7 hack)
// e.g. html >/**/ body
if (strlen($token) === 0) {
$start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
if ($start_index > 2) {
if (substr($css, $start_index - 3, 1) === '>') {
$this->preserved_tokens[] = '';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
}
}
}

// in all other cases kill the comment
$css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
}


// Normalize all whitespace strings to single spaces. Easier to work with that way.
$css = preg_replace('/\s+/', ' ', $css);

// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);

// Shorten & preserve calculations calc(...) since spaces are important
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);

// Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
$css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);

// Remove leading zeros from integer and float numbers preceded by : or a white-space
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);

// Remove trailing zeros from float numbers preceded by : or a white-space
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);

// Remove trailing .0 -> -9.0 to -9
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);

// Replace 0 length numbers with 0
$css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);

// Remove the spaces before the things that should not have spaces before them.
// But, be careful not to turn "p :link {...}" into "p:link{...}"
// Swap out any pseudo-class colons with the token, and then swap back.
$css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);

// Remove spaces before the things that should not have spaces before them.
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);

// Restore spaces for !important
$css = preg_replace('/\!important/i', ' !important', $css);

// bring back the colon
$css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);

// retain space for special IE6 cases
$css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);

// no space after the end of a preserved comment
$css = preg_replace('/\*\/ /', '*/', $css);

// lowercase some popular @directives
$css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);

// lowercase some more common pseudo-elements
$css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);

// lowercase some more common functions
$css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);

// lower case some common function that can be values
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
$css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);

// Put the space back in some cases, to support stuff like
// @media screen and (-webkit-min-device-pixel-ratio:0){
$css = preg_replace('/\band\(/i', 'and (', $css);

// Remove the spaces after the things that should not have spaces after them.
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);

// remove unnecessary semicolons
$css = preg_replace('/;+\}/', '}', $css);

// Fix for issue: #2528146
// Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
// to avoid issues on Symbian S60 3.x browsers.
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);

// Replace 0 <length> and 0 <percentage> values with 0.
// <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
// <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
$css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);

// 0% step in a keyframe? restore the % unit
$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);

// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);

// Fix for issue: #2528142
// Replace text-shadow:0; with text-shadow:0 0 0;
$css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);

// Replace background-position:0; with background-position:0 0;
// same for transform-origin
// Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
$css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);

// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
// This makes it more likely that it'll get further compressed in the next step.
$css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
$css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);

// Shorten colors from #AABBCC to #ABC or short color name.
$css = $this->compress_hex_colors($css);

// border: none to border:0, outline: none to outline:0
$css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);

// shorter opacity IE filter
$css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);

// Find a fraction that is used for Opera's -o-device-pixel-ratio query
// Add token to add the "\" back in later
$css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);

// Remove empty rules.
$css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);

// Add "/" back to fix Opera -o-device-pixel-ratio query
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);

// Replace multiple semi-colons in a row by a single one
// See SF bug #1980989
$css = preg_replace('/;;+/', ';', $css);

// Restore new lines for /*! important comments
$css = preg_replace('/'. self::NL .'/', "\n", $css);

// Lowercase all uppercase properties
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);

// Some source control tools don't like it when files containing lines longer
// than, say 8000 characters, are checked in. The linebreak option is used in
// that case to split long lines after a specific column.
if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
$linebreak_pos = (int) $linebreak_pos;
$start_index = $i = 0;
while ($i < strlen($css)) {
$i++;
if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
$css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
$start_index = $i;
}
}
}

// restore preserved comments and strings in reverse order
for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
}

// Trim the final string (for any leading or trailing white spaces)
return trim($css);
}

/**
* Utility method to replace all data urls with tokens before we start
* compressing, to avoid performance issues running some of the subsequent
* regexes against large strings chunks.
*
* @param string $css
* @return string
*/
private function extract_data_urls($css)
{
// Leave data urls alone to increase parse performance.
$max_index = strlen($css) - 1;
$append_index = $index = $last_index = $offset = 0;
$sb = array();
$pattern = '/url\(\s*(["\']?)data\:/i';

// Since we need to account for non-base64 data urls, we need to handle
// ' and ) being part of the data string. Hence switching to indexOf,
// to determine whether or not we have matching string terminators and
// handling sb appends directly, instead of using matcher.append* methods.

while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$start_index = $index + 4; // "url(".length()
$end_index = $last_index - 1;
$terminator = $m[1]; // ', " or empty (not quoted)
$found_terminator = FALSE;

if (strlen($terminator) === 0) {
$terminator = ')';
}

while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
$end_index = $this->index_of($css, $terminator, $end_index + 1);

// endIndex == 0 doesn't really apply here
if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
$found_terminator = TRUE;
if (')' != $terminator) {
$end_index = $this->index_of($css, ')', $end_index);
}
}
}

// Enough searching, start moving stuff over to the buffer
$sb[] = $this->str_slice($css, $append_index, $index);

if ($found_terminator) {
$token = $this->str_slice($css, $start_index, $end_index);
$token = preg_replace('/\s+/', '', $token);
$this->preserved_tokens[] = $token;

$preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
$sb[] = $preserver;

$append_index = $end_index + 1;
} else {
// No end terminator found, re-add the whole match. Should we throw/warn here?
$sb[] = $this->str_slice($css, $index, $last_index);
$append_index = $last_index;
}

$offset = $last_index;
}

$sb[] = $this->str_slice($css, $append_index);

return implode('', $sb);
}

/**
* Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
*
* DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
* e.g. #AddressForm { ... }
*
* DOES NOT compress IE filters, which have hex color values (which would break things).
* e.g. filter: chroma(color="#FFFFFF");
*
* DOES NOT compress invalid hex values.
* e.g. background-color: #aabbccdd
*
* @param string $css
* @return string
*/
private function compress_hex_colors($css)
{
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
$pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
$_index = $index = $last_index = $offset = 0;
$sb = array();
// See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
$short_safe = array(
'#808080' => 'gray',
'#008000' => 'green',
'#800000' => 'maroon',
'#000080' => 'navy',
'#808000' => 'olive',
'#ffa500' => 'orange',
'#800080' => 'purple',
'#c0c0c0' => 'silver',
'#008080' => 'teal',
'#f00' => 'red'
);

while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$is_filter = $m[1] !== null && $m[1] !== '';

$sb[] = $this->str_slice($css, $_index, $index);

if ($is_filter) {
// Restore, maintain case, otherwise filter will break
$sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
} else {
if (strtolower($m[2]) == strtolower($m[3]) &&
strtolower($m[4]) == strtolower($m[5]) &&
strtolower($m[6]) == strtolower($m[7])) {
// Compress.
$hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
} else {
// Non compressible color, restore but lower case.
$hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
}
// replace Hex colors to short safe color names
$sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
}

$_index = $offset = $last_index - strlen($m[8]);
}

$sb[] = $this->str_slice($css, $_index);

return implode('', $sb);
}

/* CALLBACKS
* ---------------------------------------------------------------------------------------------
*/

private function replace_string($matches)
{
$match = $matches[0];
$quote = substr($match, 0, 1);
// Must use addcslashes in PHP to avoid parsing of backslashes
$match = addcslashes($this->str_slice($match, 1, -1), '\\');

// maybe the string contains a comment-like substring?
// one, maybe more? put'em back then
if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
}
}

// minify alpha opacity in filter strings
$match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);

$this->preserved_tokens[] = $match;
return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
}

private function replace_colon($matches)
{
return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
}

private function replace_calc($matches)
{
$this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}

private function preserve_old_IE_specific_matrix_definition($matches)
{
$this->preserved_tokens[] = $matches[1];