index-wp-redis.php 10.7 KB
Newer Older
Benjamin Adams's avatar
Benjamin Adams committed
1
<?php
2 3 4 5 6 7 8
/**
 * WP REDIS CACHE
 */

/**
 * GLOBAL CONFIGURATION
 */
9 10 11
global $wp_redis_cache_config;

$wp_redis_cache_config = array(
12 13 14 15 16 17 18 19
	'debug'          => false,
	'debug_messages' => '',
	'cache'          => false,
	'server_ip'      => '127.0.0.1',
	'redis_server'   => '127.0.0.1',
	'redis_port'     => 6379,
	'redis_db'       => 0,
	'secret_string'  => 'changeme',
20 21
);

22
// Uncomment either option below to fix the values here and disable the admin UI
23 24
// $wp_redis_cache_config['cache_duration'] = 43200;
// $wp_redis_cache_config['unlimited']      = false;
25

26 27 28 29 30
// Modify this function to introduce custom handling when exceptions occur
function wp_redis_cache_exception_handler( $exception ) {
	return;
}

31
/**
32 33
 * END GLOBAL CONFIGURATION
 *
34 35
 * DO NOT EDIT BELOW THIS LINE!
 */
36 37
$wp_redis_cache_config['current_url'] = wp_redis_cache_get_clean_url( $wp_redis_cache_config['secret_string'] );
$wp_redis_cache_config['redis_key']   = md5( $wp_redis_cache_config['current_url'] );
Benjamin Adams's avatar
Benjamin Adams committed
38

Ulrich Block's avatar
Ulrich Block committed
39
// Start the timer so we can track the page load time
40
if ( $wp_redis_cache_config['debug'] ) {
41 42
	$start = microtime();
}
Ulrich Block's avatar
Ulrich Block committed
43

44
/**
45
 * SET SEPARATE CACHES FOR BROAD DEVICE TYPES
46
 */
47
$wp_redis_cache_config['redis_key'] = wp_redis_cache_set_device_key( $wp_redis_cache_config['redis_key'] );
48

49 50 51 52 53 54 55 56 57
/**
 * UTILITY FUNCTIONS
 */

/**
 * Compute microtime from a timestamp
 *
 * @return float
 */
Erick Hitter's avatar
Erick Hitter committed
58
function wp_redis_cache_get_micro_time( $time ) {
Erick Hitter's avatar
Erick Hitter committed
59 60
	list( $usec, $sec ) = explode( " ", $time );
	return ( (float) $usec + (float) $sec );
Ulrich Block's avatar
Ulrich Block committed
61 62
}

63 64 65 66 67
/**
 * Is the current request a refresh request with the correct secret key?
 *
 * @return bool
 */
Erick Hitter's avatar
Erick Hitter committed
68
function wp_redis_cache_refresh_has_secret( $secret ) {
Erick Hitter's avatar
Erick Hitter committed
69
	return isset( $_GET['refresh'] ) && $secret == $_GET['refresh'];
Benjamin Adams's avatar
merged  
Benjamin Adams committed
70
}
Ulrich Block's avatar
Ulrich Block committed
71

72 73 74 75 76
/**
 * Does current request include a refresh request?
 *
 * @return bool
 */
Erick Hitter's avatar
Erick Hitter committed
77
function wp_redis_cache_request_has_secret( $secret ) {
Erick Hitter's avatar
Erick Hitter committed
78
	return false !== strpos( $_SERVER['REQUEST_URI'], "refresh=${secret}" );
Benjamin Adams's avatar
merged  
Benjamin Adams committed
79
}
Hendrik Klemp's avatar
Hendrik Klemp committed
80

81 82 83 84 85
/**
 * Determine if request is from a server other than the one running this code
 *
 * @return bool
 */
Erick Hitter's avatar
Erick Hitter committed
86
function wp_redis_cache_is_remote_page_load( $current_url, $server_ip ) {
Erick Hitter's avatar
Erick Hitter committed
87
	return ( isset( $_SERVER['HTTP_REFERER'] )
88
			&& $_SERVER['HTTP_REFERER'] == $current_url
Erick Hitter's avatar
Erick Hitter committed
89
			&& $_SERVER['REQUEST_URI'] != '/'
90
			&& $_SERVER['REMOTE_ADDR'] != $server_ip );
Benjamin Adams's avatar
merged  
Benjamin Adams committed
91
}
Hendrik Klemp's avatar
Hendrik Klemp committed
92

93 94 95 96 97
/**
 * Set proper IP address for proxied requests
 *
 * @return null
 */
Erick Hitter's avatar
Erick Hitter committed
98
function wp_redis_cache_handle_cdn_remote_addressing() {
Erick Hitter's avatar
Erick Hitter committed
99
	// so we don't confuse the cloudflare server
Erick Hitter's avatar
Erick Hitter committed
100
	if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
Erick Hitter's avatar
Erick Hitter committed
101 102
		$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
	}
Benjamin Adams's avatar
merged  
Benjamin Adams committed
103
}
104

105 106 107 108 109 110 111 112
/**
 * Prepare a URL for use as a cache key
 *
 * Strips secret key from URL
 *
 * @param string
 * @return string
 */
Erick Hitter's avatar
Erick Hitter committed
113
function wp_redis_cache_get_clean_url( $secret ) {
114
	$replace_keys = array( "?refresh=${secret}","&refresh=${secret}" );
115 116
	$url          = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
	return str_replace( $replace_keys, '', $url );
Benjamin Adams's avatar
Benjamin Adams committed
117
}
Benjamin Adams's avatar
merged  
Benjamin Adams committed
118

119
/**
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
 * Prefix cache key if device calls for separate caching
 *
 * @param string $key
 * @return $string
 */
function wp_redis_cache_set_device_key( $key ) {
	switch ( wp_redis_cache_get_device_type() ) {
		case 'tablet' :
			$prefix = 'T-';
			break;
		case 'mobile' :
			$prefix = 'M-';
			break;
		default :
		case 'desktop' :
			$prefix = '';
			break;
	}

	return $prefix . $key;
}

/**
 * Determine the current device type from its user agent
 * Allows for separate caches for tablet, mobile, and desktop visitors
145
 *
146
 * @return string
147
 */
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
function wp_redis_cache_get_device_type() {
	$ua = $_SERVER['HTTP_USER_AGENT'];

	if ( empty( $ua ) ) {
		return 'desktop';
	}

	// Tablet user agents
	if (
		false !== stripos( $ua, 'ipad'       ) ||
		( false !== stripos( $ua, 'Android'  ) && false === stripos( $ua, 'mobile' ) ) ||
		false !== stripos( $ua, 'tablet '    ) ||
		false !== stripos( $ua, 'Silk/'      ) ||
		false !== stripos( $ua, 'Kindle'     ) ||
		false !== stripos( $ua, 'PlayBook'   ) ||
		false !== stripos( $ua, 'RIM Tablet' ) ||
	) {
		return 'tablet';
	}

	// Mobile user agents
	if (
		false !== stripos( $ua, 'Mobile'     ) || // many mobile devices (all iPhone, iPad, etc.)
		false !== stripos( $ua, 'Android'    ) ||
		false !== stripos( $ua, 'BlackBerry' ) ||
		false !== stripos( $ua, 'Opera Mini' ) ||
		false !== stripos( $ua, 'Opera Mobi' )
	) {
		return 'mobile';
	}

	return 'desktop';
180 181
}

182
/**
183 184 185 186 187
 * Establish a connection to the Redis server
 *
 * Will try the PECL module first, then fall back to PRedis
 *
 * @return object
188
 */
189
function wp_redis_cache_connect_redis() {
190 191
	global $wp_redis_cache_config;

Erick Hitter's avatar
Erick Hitter committed
192
	// check if PECL Extension is available
Erick Hitter's avatar
Erick Hitter committed
193
	if ( class_exists( 'Redis' ) ) {
194 195
		if ( $wp_redis_cache_config['debug'] ) {
			$wp_redis_cache_config['debug_messages'] .= "<!-- Redis PECL module found -->\n";
Erick Hitter's avatar
Erick Hitter committed
196
		}
Erick Hitter's avatar
Erick Hitter committed
197

Erick Hitter's avatar
Erick Hitter committed
198 199
		$redis = new Redis();

200 201
		$redis->connect( $wp_redis_cache_config['redis_server'], $wp_redis_cache_config['redis_port'] );
		$redis->select( $wp_redis_cache_config['redis_db'] );
202 203
	// Fallback to predis5.2.php
	} else {
204 205
		if ( $wp_redis_cache_config['debug'] ) {
			$wp_redis_cache_config['debug_messages'] .= "<!-- using predis as a backup -->\n";
Erick Hitter's avatar
Erick Hitter committed
206
		}
Erick Hitter's avatar
Erick Hitter committed
207

208
		include_once dirname( __FILE__ ) . '/wp-content/plugins/wp-redis-cache/predis5.2.php'; //we need this to use Redis inside of PHP
209
		$redis = new Predis_Client( array(
210 211 212
			'host'     => $wp_redis_cache_config['redis_server'],
			'port'     => $wp_redis_cache_config['redis_port'],
			'database' => $wp_redis_cache_config['redis_db'],
213
		) );
Erick Hitter's avatar
Erick Hitter committed
214 215
	}

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	return $redis;
}

/**
 * BEGIN CACHING LOGIC
 */

// Set proper IP for proxied requests
wp_redis_cache_handle_cdn_remote_addressing();

// Ensure WP uses a theme (this is normally set in index.php)
if ( ! defined( 'WP_USE_THEMES' ) ) {
	define( 'WP_USE_THEMES', true );
}

try {
	// Establish connection with Redis server
	$redis = wp_redis_cache_connect_redis();

235 236 237
	// Whether we need to load WP
	$load_wp = true;

238 239 240 241
	// Relevant details on the current request
	$is_post   = (bool) 'POST' === $_SERVER['REQUEST_METHOD'];
	$logged_in = (bool) preg_match( "#(wordpress_(logged|sec)|comment_author)#", var_export( $_COOKIE, true ) );

242 243 244
	if ( $wp_redis_cache_config['debug'] ) {
		$wp_redis_cache_config['debug_messages'] .= "<!-- POST request: . " . ( $is_post ? 'yes' : 'no' ) . "-->\n";
		$wp_redis_cache_config['debug_messages'] .= "<!-- Logged in: . " . ( $logged_in ? 'yes' : 'no' ) . "-->\n";
245 246 247
	}

	// Refresh request, deletes cache: either manual refresh cache by adding ?refresh=secret_string after the URL or somebody posting a comment
248 249 250
	if ( wp_redis_cache_refresh_has_secret( $wp_redis_cache_config['secret_string'] ) || wp_redis_cache_request_has_secret( $wp_redis_cache_config['secret_string'] ) || wp_redis_cache_is_remote_page_load( $wp_redis_cache_config['current_url'], $wp_redis_cache_config['server_ip'] ) ) {
		if ( $wp_redis_cache_config['debug'] ) {
			$wp_redis_cache_config['debug_messages'] .= "<!-- manual refresh was required -->\n";
Erick Hitter's avatar
Erick Hitter committed
251
		}
Erick Hitter's avatar
Erick Hitter committed
252

253
		$redis->del( $wp_redis_cache_config['redis_key'] );
254
	// This page is cached, the user isn't logged in, and it isn't a POST request, so let's use the cache
255 256 257
	} elseif ( ! $is_post && ! $logged_in && $redis->exists( $wp_redis_cache_config['redis_key'] ) ) {
		if ( $wp_redis_cache_config['debug'] ) {
			$wp_redis_cache_config['debug_messages'] .= "<!-- serving page from cache: key: " . $wp_redis_cache_config['redis_key'] . " -->\n";
258 259
		}

260 261
		// Page is served from cache, so we don't need WP
		$load_wp = false;
262
		$wp_redis_cache_config['cached'] = true;
263

264
		echo trim( $redis->get( $wp_redis_cache_config['redis_key'] ) );
Erick Hitter's avatar
Erick Hitter committed
265
	// If the cache does not exist lets display the user the normal page without cache, and then fetch a new cache page
266 267 268 269
	} elseif ( $_SERVER['REMOTE_ADDR'] != $wp_redis_cache_config['server_ip'] ) {
		if ( false === strstr( $wp_redis_cache_config['current_url'], 'preview=true' ) ) {
			if ( $wp_redis_cache_config['debug'] ) {
				$wp_redis_cache_config['debug_messages'] .= "<!-- displaying page without cache -->\n";
270
			}
271

272 273 274 275 276 277 278
			// If user isn't logged in and this isn't a post request, render the requested page and cache if appropriate.
			if ( ! $is_post && ! $logged_in ) {
				// We load WP to generate the cached output, so no need to load again
				$load_wp = false;

				// Render page into an output buffer and display
				ob_start();
279
				require_once dirname( __FILE__ ) . '/wp-blog-header.php';
280 281 282 283 284 285
				$markup_to_cache = trim( ob_get_clean() );
				echo $markup_to_cache;

				// Cache rendered page if appropriate
				if ( ! is_404() && ! is_search() ) {
					// Is unlimited cache life requested?
286 287
					if ( isset( $wp_redis_cache_config['unlimited'] ) ) {
						$unlimited = $wp_redis_cache_config['unlimited'];
288
					} else {
289
						$unlimited = (bool) get_option( 'wp-redis-cache-debug', false );
290
						$wp_redis_cache_config['unlimited'] = $unlimited;
291 292
					}

293 294
					// Cache the page for the chosen duration
					if ( $unlimited ) {
295
						$redis->set( $wp_redis_cache_config['redis_key'], $markup_to_cache );
296
					} else {
297 298
						if ( isset( $wp_redis_cache_config['cache_duration'] ) ) {
							$cache_duration = $wp_redis_cache_config['cache_duration'];
299 300
						} else {
							$cache_duration = (int) get_option( 'wp-redis-cache-seconds', 43200 );
301
							$wp_redis_cache_config['cache_duration'] = $cache_duration;
302 303 304
						}

						if ( ! is_numeric( $cache_duration ) ) {
305
							$cache_duration = $wp_redis_cache_config['cache_duration'] = 43200;
306 307
						}

308
						$redis->setex( $wp_redis_cache_config['redis_key'], $cache_duration, $markup_to_cache );
309
					}
Erick Hitter's avatar
Erick Hitter committed
310
				}
311 312 313
			}
		}
	}
314 315 316

	// The current request wasn't served from cache or isn't cacheable, so we pass off to WP
	if ( $load_wp ) {
317
		require_once dirname( __FILE__ ) . '/wp-blog-header.php';
Erick Hitter's avatar
Erick Hitter committed
318
	}
Erick Hitter's avatar
Erick Hitter committed
319
} catch ( Exception $e ) {
320
	require_once dirname( __FILE__ ) . '/wp-blog-header.php';
321
	wp_redis_cache_exception_handler( $e );
Benjamin Adams's avatar
Benjamin Adams committed
322 323
}

324 325 326
/**
 * DEBUGGING OUTPUT
 */
327
if ( $wp_redis_cache_config['debug'] ) {
328 329
	$end  = microtime();
	$time = @wp_redis_cache_get_micro_time( $end ) - @wp_redis_cache_get_micro_time( $start );
330
	$wp_redis_cache_config['debug_messages'] .= "<!-- Cache system by Erick Hitter. Page generated in " . round($time, 5) . " seconds. -->\n";
331 332 333
	$wp_redis_cache_config['debug_messages'] .= "<!-- Site was cached = " . $wp_redis_cache_config['cached'] . " -->\n";
	if ( isset( $wp_redis_cache_config['cache_duration'] ) ) {
		$wp_redis_cache_config['debug_messages'] .= "<!-- wp-redis-cache-seconds = " . $wp_redis_cache_config['cache_duration'] . " -->\n";
Erick Hitter's avatar
Erick Hitter committed
334
	}
335 336 337
	$wp_redis_cache_config['debug_messages'] .= "<!-- wp-redis-cache-ip = " . $wp_redis_cache_config['server_ip'] . "-->\n";
	if ( isset( $wp_redis_cache_config['unlimited'] ) ) {
		$wp_redis_cache_config['debug_messages'] .= "<!-- wp-redis-cache-unlimited = " . $wp_redis_cache_config['unlimited'] . "-->\n";
Erick Hitter's avatar
Erick Hitter committed
338
	}
339
	$wp_redis_cache_config['debug_messages'] .= "<!-- wp-redis-cache-debug = " . $wp_redis_cache_config['debug'] . "-->\n";
340

341
	echo $wp_redis_cache_config['debug_messages'];
Benjamin Adams's avatar
Benjamin Adams committed
342
}