tagging version 0.4

git-svn-id: http://plugins.svn.wordpress.org/wp-ffpc/tags/0.4@582238 b8457f37-d9ea-0310-8a92-e5e31aec5664
This commit is contained in:
cadeyrn 2012-08-06 12:18:48 +00:00
parent 0c00c7edab
commit 20bb48be44
6 changed files with 1377 additions and 0 deletions

268
advanced-cache.php Normal file
View file

@ -0,0 +1,268 @@
<?php
/**
* part of WordPress plugin WP-FFPC
*/
/* check for WP cache enabled*/
if ( !WP_CACHE )
return false;
/* check for config */
if (!isset($wp_ffpc_config))
return false;
/* request uri */
$wp_ffpc_uri = $_SERVER['REQUEST_URI'];
/* query string */
$wp_ffpc_qs = strpos($wp_ffpc_uri, '?');
/* no cache for uri with query strings, things usually go bad that way */
if ($wp_ffpc_qs !== false)
return false;
/* no cache for post request (comments, plugins and so on) */
if ($_SERVER["REQUEST_METHOD"] == 'POST')
return false;
/**
* Try to avoid enabling the cache if sessions are managed
* with request parameters and a session is active
*/
if (defined('SID') && SID != '')
return false;
/* no cache for pages starting with /wp- like WP admin */
if (strpos($wp_ffpc_uri, '/wp-') !== false)
return false;
/* no cache for robots.txt */
if ( strpos($wp_ffpc_uri, 'robots.txt') )
return false;
/* multisite files can be too large for memcached */
if (function_exists('is_multisite') && is_multisite() && strpos($wp_ffpc_uri, '/files/') )
return false;
/* no cache for logged in users */
if (!$wp_ffpc_config['cache_loggedin']) {
foreach ($_COOKIE as $n=>$v) {
// test cookie makes to cache not work!!!
if ($n == 'wordpress_test_cookie') continue;
// wp 2.5 and wp 2.3 have different cookie prefix, skip cache if a post password cookie is present, also
if ( (substr($n, 0, 14) == 'wordpressuser_' || substr($n, 0, 10) == 'wordpress_' || substr($n, 0, 12) == 'wp-postpass_') && !$wp_ffpc_config['cache_loggedin'] ) {
return false;
}
}
}
global $wp_ffpc_backend_status;
$wp_ffpc_backend_status = wp_ffpc_init( );
/* check alive status of backend */
if ( !$wp_ffpc_backend_status )
return false;
/* use the full accessed URL string as key, same will be generated by nginx as well
we need a data and a meta key: data is string only with content, meta is not used in nginx */
global $wp_ffpc_data_key;
$wp_ffpc_data_key = $wp_ffpc_config['prefix_data'] . $_SERVER['HTTP_HOST'] . $wp_ffpc_uri;
global $wp_ffpc_meta_key;
$wp_ffpc_meta_key = $wp_ffpc_config['prefix_meta'] . $_SERVER['HTTP_HOST'] . $wp_ffpc_uri;
/* search for valid meta entry */
global $wp_ffpc_meta;
$wp_ffpc_meta = wp_ffpc_get ( $wp_ffpc_meta_key );
/* meta is corrupted or empty */
if ( !$wp_ffpc_meta ) {
wp_ffpc_start();
return;
}
/* search for valid data entry */
global $wp_ffpc_data;
$uncompress = ( isset($wp_ffpc_meta['compressed']) ) ? $wp_ffpc_meta['compressed'] : false;
$wp_ffpc_data = wp_ffpc_get ( $wp_ffpc_data_key , $uncompress );
/* data is corrupted or empty */
if ( !$wp_ffpc_data ) {
wp_ffpc_start();
return;
}
/* 404 status cache */
if ($wp_ffpc_meta['status'] == 404) {
header("HTTP/1.1 404 Not Found");
flush();
die();
}
/* server redirect cache */
if ($wp_ffpc_meta['redirect_location']) {
header('Location: ' . $wp_ffpc_meta['redirect_location']);
flush();
die();
}
/* page is already cached on client side (chrome likes to do this, anyway, it's quite efficient) */
if (array_key_exists("HTTP_IF_MODIFIED_SINCE", $_SERVER) && !empty($wp_ffpc_meta['lastmodified']) ) {
$if_modified_since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]));
/* check is cache is still valid */
if ( $if_modified_since >= $wp_ffpc_meta['lastmodified'] ) {
header("HTTP/1.0 304 Not Modified");
flush();
die();
}
}
/* data found & correct, serve it */
header('Content-Type: ' . $wp_ffpc_meta['mime']);
/* don't allow browser caching of page */
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0');
header('Pragma: no-cache');
/* expire at this very moment */
header('Expires: ' . gmdate("D, d M Y H:i:s", time() ) . " GMT");
/* if shortlinks were set */
if (!empty ( $wp_ffpc_meta['shortlink'] ) )
header('Link:<'. $wp_ffpc_meta['shortlink'] .'>; rel=shortlink');
/* if last modifications were set (for posts & pages) */
if ( !empty($wp_ffpc_meta['lastmodified']) )
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $wp_ffpc_meta['lastmodified'] ). " GMT");
/* only set when not multisite, fallback to HTTP HOST */
$wp_ffpc_pingback_url = (empty( $wp_ffpc_config['url'] )) ? $_SERVER['HTTP_HOST'] : $wp_ffpc_config['url'];
/* pingback additional header */
if ($wp_ffpc_config['pingback_status'])
header('X-Pingback: ' . $wp_ffpc_pingback_url . '/xmlrpc.php' );
/* for debugging */
if ($wp_ffpc_config['debug'])
header('X-Cache-Engine: WP-FFPC with ' . $wp_ffpc_config['cache_type']);
/* HTML data */
echo $wp_ffpc_data;
flush();
die();
/**
* FUNCTIONS
*/
/**
* starts caching
*
*/
function wp_ffpc_start( ) {
ob_start('wp_ffpc_callback');
}
/**
* write cache function, called when page generation ended
*/
function wp_ffpc_callback($buffer) {
global $wp_ffpc_config;
global $wp_ffpc_data;
global $wp_ffpc_meta;
global $wp_ffpc_redirect;
global $wp_ffpc_meta_key;
global $wp_ffpc_data_key;
/* no is_home = error */
if (!function_exists('is_home'))
return $buffer;
/* no <body> close tag = not HTML, don't cache */
if (strpos($buffer, '</body>') === false)
return $buffer;
/* reset meta to solve conflicts */
$wp_ffpc_meta = array();
/* WP is sending a redirect */
if ($wp_ffpc_redirect) {
$wp_ffpc_meta['redirect_location'] = $wp_ffpc_redirect;
wp_ffpc_write();
return $buffer;
}
/* trim unneeded whitespace from beginning / ending of buffer */
$buffer = trim($buffer);
/* Can be a trackback or other things without a body.
We do not cache them, WP needs to get those calls. */
if (strlen($buffer) == 0)
return '';
if ( is_home() )
$wp_ffpc_meta['type'] = 'home';
elseif (is_feed() )
$wp_ffpc_meta['type'] = 'feed';
elseif ( is_archive() )
$wp_ffpc_meta['type'] = 'archive';
elseif ( is_single() )
$wp_ffpc_meta['type'] = 'single';
else if ( is_page() )
$wp_ffpc_meta['type'] = 'page';
else
$wp_ffpc_meta['type'] = 'unknown';
/* check if caching is disabled for page type */
$nocache_key = 'nocache_'. $wp_ffpc_meta['type'];
if ( $wp_ffpc_config[$nocache_key] == 1 ) {
return $buffer;
}
if ( is_404() )
$wp_ffpc_meta['status'] = 404;
/* feed is xml, all others forced to be HTML */
if ( is_feed() )
$wp_ffpc_meta['mime'] = 'text/xml;charset=';
else
$wp_ffpc_meta['mime'] = 'text/html;charset=';
/* set mimetype */
$wp_ffpc_meta['mime'] = $wp_ffpc_meta['mime'] . $wp_ffpc_config['charset'];
/* get shortlink, if possible */
if (function_exists('wp_get_shortlink'))
{
$shortlink = wp_get_shortlink( );
if (!empty ( $shortlink ) )
$wp_ffpc_meta['shortlink'] = $shortlink;
}
/* try if post is available
if made with archieve, last listed post can make this go bad
*/
global $post;
if ( !empty($post) && ( $wp_ffpc_meta['type'] == 'single' || $wp_ffpc_meta['type'] == 'page' ) )
{
/* get last modification data */
if (!empty ( $post->post_modified_gmt ) )
$wp_ffpc_meta['lastmodified'] = strtotime ( $post->post_modified_gmt );
}
/* APC compression */
$compress = ( ($wp_ffpc_config['cache_type'] == 'apc') && $wp_ffpc_config['apc_compress'] ) ? true : false;
$wp_ffpc_meta['compressed'] = $compress;
/* set meta */
wp_ffpc_set ( $wp_ffpc_meta_key, $wp_ffpc_meta );
/* set data */
$data = $buffer;
wp_ffpc_set ( $wp_ffpc_data_key, $data , $compress );
/* vital for nginx version */
header("HTTP/1.1 200 OK");
/* echoes HTML out */
return $buffer;
}
?>

53
css/wp-ffpc.admin.css Normal file
View file

@ -0,0 +1,53 @@
dt {
margin-top: 2em;
font-weight:bold;
}
dd {
}
fieldset {
display:block;
margin:0 1% 0 1%;
clear:none;
}
legend {
font-weight:bold;
padding:0;
margin:0;
font-size:130%;
padding-top:3em;
}
.default {
display:block;
padding-left: 1em;
font-size:90%;
color:#666;
}
.description {
display:block;
}
.grid50 {
float:left;
display:block;
text-align:left;
margin:0 1% 0 1%;
width:48%;
overflow:auto;
}
.clearcolumns {
clear:both;
}
.error-msg {
color: #990000;
}
.ok-msg {
color: #009900;
}

27
nginx-sample.conf Normal file
View file

@ -0,0 +1,27 @@
http {
...
server {
...
location / {
try_files $uri $uri/ @memcached;
}
location @memcached {
default_type text/html;
set $memcached_key DATAPREFIX$host$request_uri;
memcached_pass MEMCACHEDHOST:MEMCACHEDPORT;
error_page 404 = @rewrites;
}
location @rewrites {
rewrite ^(.*)$ /index.php?q=$1 last;
}
...
}
}
...

113
readme.txt Normal file
View file

@ -0,0 +1,113 @@
=== WP-FFPC ===
Contributors: cadeyrn
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=8LZ66LGFLMKJW&lc=HU&item_name=Peter%20Molnar%20photographer%2fdeveloper&item_number=petermolnar%2dpaypal%2ddonation&currency_code=USD&bn=PP%2dDonationsBF%3acredit%2epng%3aNonHosted
Tags: cache, APC, memcached, full page cache
Requires at least: 3.0
Tested up to: 3.4.1
Stable tag: 0.4
Fast Full Page Cache, backend can be memcached or APC
== Description ==
WP-FFPC is a full page cache plugin for WordPress. It can use APC or a memcached server as backend. The naming stands for Fast Full Page Cache.
PHP has two extension for communication with a memcached server, named Memcache and Memcached. The plugin can utilize both.
= Features: =
* exclude possibility of home, feeds, archieves, pages, singles
* use APC or memcached as backend
* 404 caching
* redirects caching
* Last Modified HTTP header compatibility with 304 responses
* shortlink HTTP header preservation
* pingback HTTP header preservation(1)
* fallback to no caching if any error or problem occurs
* Wordpress Network compatible(2)
* nginx compatible(3)
* (optional) syslog messages of sets-gets-flushes
(1) pingback hostname will always be generated from the accessed domain, otherwise speed would get highly compromised
(2) If used in WordPress Network, the configuration will only be available for network admins at the network admin panel, and will be system-wide and will be applied for every blog.
(3) nginx compatility means that if used with PHP Memcache or PHP Memcached extension, the created memcached entries can be read and served directly from nginx, making the cache insanely fast.
If used with APC, this feature is not available (no APC module for nginx), although, naturally, the cache modul is functional and working, but it will be done by PHP instead of nginx.
Short nginx example configuration is generated on the plugin settings page if Memcache or Memcached is selected as cache type.
NOTE: some features ( like pingback link in HTTP header ) will not be available with this solution! ( yet )
Some parts were based on [Hyper Cache](http://wordpress.org/extend/plugins/hyper-cache "Hyper Cache") plugin by Satollo (info@satollo.net).
== Installation ==
1. Upload contents of `wp-ffpc.zip` to the `/wp-content/plugins/` directory
2. Enable WordPress cache by adding `define('WP_CACHE',true);` in wp-config.php
3. Activate the plugin through the `Plugins` menu in WordPress (please use network wide activation if used in a WordPress Network)
4. Fine tune the settings in `Settings` -> `wp-ffpc` menu in WordPress. For WordPress Network, please visit the Network Admin panel, the options will be available at WP-FFPC menu entry.
== Frequently Asked Questions ==
= Known bugs =
1. '%3C' character on home page load
**Description**: When the page address is entered by hand, it gets redirected to `page.address/%3C`.
**Solution**: only occurs with memcached, the reason is yet unknown. The bug has emerged once for me as well, setting up everything and restarting the memcached server solved it.
2. random-like characters instead of page
***SOLVED, description below is outdated***
**Description**: when nginx is used with memcached, characters like `xœí}ksÛ8²èg»` shows up instead of the page.
**Solution**: this is the zlib compression of the page text. If PHP uses Memcached (with the 'd' at the ending), the compression cannot be turned off (it should, but it does not) and nginx is unable to read out the entries.
Please use only the Memcache extension. You also need to select it on the settings site, this is because some hosts may provide both PHP extensions.
= How to install memcache PHP extension? =
You need to have PECL on your machine. If it's ready, type `pecl install memcache` as root.
Some additional libraries can also be needed, but that varies by linux distributions.
= How to use the plugin on Amazon Linux? =
You have to remove the default yum package, named `php-pecl-memcache` and install `Memcache` with PECL.
== Changelog ==
= 0.4 =
2012.08.06
* tested against new WordPress versions
* added lines to "memcached" storage to be able to work with nginx as well
* added lines to "memcached" to use binary protocol ( tested with PHP Memcached version 2.0.1 )
KNOWN ISSUES
* "memcache" extension fails in binary mode; the reason is under investigation
= 0.3.2 =
2012.02.27
* apc_cache_info replaced with apc_sma_info, makes plugin faster
= 0.3 =
2012.02.21
* added syslog debug messages possibility
* bugfix: removed (accidently used) short_open_tags
= 0.2.3 =
2012.02.21
* nginx-sample.conf file added, nginx config is created from here
= 0.2.2 =
2012.02.21
* memcache types bugfix, reported in forum, thanks!
= 0.2.1 =
2012.02.21
* bugfix, duplicated inclusion could emerge, fix added, thanks for Géza Kuti for reporting!
= 0.2 =
2012.02.19
* added APC compression option ( requires PHP ZLIB ). Useful is output pages are large. Compression is on lowest level, therefore size/CPU load is more or less optimal.
= 0.1 =
2012.02.16
* first public release

248
wp-ffpc-common.php Normal file
View file

@ -0,0 +1,248 @@
<?php
/**
* part of WordPress plugin WP-FFPC
*/
/**
* function to test if selected backend is available & alive
*/
global $wp_ffpc_backend;
global $wp_nmc_redirect;
/* wp ffpc prefic */
if (!defined('WP_FFPC_PARAM'))
define ( 'WP_FFPC_PARAM' , 'wp-ffpc' );
/* log level */
define ('WP_FFPC_LOG_LEVEL' , LOG_INFO);
/* define log ending message */
define ('WP_FFPC_LOG_TYPE_MSG' , '; cache type: '. $wp_ffpc_config['cache_type'] );
/* safety rules if file has been already included */
if ( function_exists('wp_ffpc_init') || function_exists('wp_ffpc_clear') || function_exists('wp_ffpc_set') || function_exists('wp_ffpc_get') )
return false;
/**
* init and backend alive check function
*
* @param $type [optional] if set, alive will be tested against this
* if false, backend will be globally initiated
* when set, backend will not become global, just tested if alive
*/
function wp_ffpc_init( $wp_ffpc_config ) {
global $wp_ffpc_backend;
$wp_ffpc_backend_status = false;
if ( empty ( $wp_ffpc_config ))
global $wp_ffpc_config;
/* verify selected storage is available */
switch ( $wp_ffpc_config['cache_type'] )
{
/* in case of apc */
case 'apc':
/* verify apc functions exist, apc ext is loaded */
if (!function_exists('apc_sma_info'))
return false;
/* verify apc is working */
if ( !apc_sma_info() )
return false;
$wp_ffpc_backend_status = true;
break;
/* in case of Memcache */
case 'memcache':
/* Memcache class does not exist, Memcache extension is not available */
if (!class_exists('Memcache'))
return false;
if ( $wp_ffpc_backend == NULL )
{
$wp_ffpc_backend = new Memcache();
$wp_ffpc_backend->addServer( $wp_ffpc_config['host'] , $wp_ffpc_config['port'] );
}
$wp_ffpc_backend_status = $wp_ffpc_backend->getServerStatus( $wp_ffpc_config['host'] , $wp_ffpc_config['port'] );
break;
/* in case of Memcached */
case 'memcached':
/* Memcached class does not exist, Memcached extension is not available */
if (!class_exists('Memcached'))
return false;
if ( $wp_ffpc_backend == NULL )
{
$wp_ffpc_backend = new Memcached();
$wp_ffpc_backend->setOption( Memcached::OPT_COMPRESSION , false );
$wp_ffpc_backend->setOption( Memcached::OPT_BINARY_PROTOCOL , true );
$wp_ffpc_backend->addServer( $wp_ffpc_config['host'] , $wp_ffpc_config['port'] );
}
$wp_ffpc_backend_status = array_key_exists( $wp_ffpc_config['host'] . ':' . $wp_ffpc_config['port'] , $wp_ffpc_backend->getStats() );
break;
/* cache type is invalid */
default:
return false;
}
return $wp_ffpc_backend_status;
}
/**
* clear cache element or flush cache
*
* @param $post_id [optional] : if registered with invalidation hook, post_id will be passed
*/
function wp_ffpc_clear ( $post_id = false ) {
global $wp_ffpc_config;
global $post;
$post_only = ( $post_id === 'system_flush' ) ? false : $wp_ffpc_config['invalidation_method'];
/* post invalidation enabled */
if ( $post_only )
{
$path = substr ( get_permalink($post_id) , 7 );
if (empty($path))
return false;
$meta = $wp_ffpc_config['prefix-meta'] . $path;
$data = $wp_ffpc_config['prefix-data'] . $path;
}
switch ($wp_ffpc_config['cache_type'])
{
/* in case of apc */
case 'apc':
if ( $post_only )
{
apc_delete ( $meta );
wp_ffpc_log ( ' clearing key: "'. $meta . '"' );
apc_delete ( $data );
wp_ffpc_log ( ' clearing key: "'. $data . '"' );
}
else
{
apc_clear_cache('user');
wp_ffpc_log ( ' flushing user cache' );
apc_clear_cache('system');
wp_ffpc_log ( ' flushing system cache' );
}
break;
/* in case of Memcache */
case 'memcache':
case 'memcached':
global $wp_ffpc_backend;
if ( $post_only )
{
$wp_ffpc_backend->delete( $meta );
wp_ffpc_log ( ' clearing key: "'. $meta . '"' );
$wp_ffpc_backend->delete( $data );
wp_ffpc_log ( ' clearing key: "'. $data . '"' );
}
else
{
$wp_ffpc_backend->flush();
wp_ffpc_log ( ' flushing cache' );
}
break;
/* cache type is invalid */
default:
return false;
}
return true;
}
/**
* sets a key-value pair in backend
*
* @param &$key store key, passed by reference for speed
* @param &$data store value, passed by reference for speed
*
*/
function wp_ffpc_set ( &$key, &$data, $compress = false ) {
global $wp_ffpc_config;
global $wp_ffpc_backend;
$exp = $wp_ffpc_config['user_logged_in'] ? $wp_ffpc_config['expire_member'] : $wp_ffpc_config['expire_visitor'];
/* syslog */
if ($wp_ffpc_config['syslog'])
{
if ( @is_array( $data ) )
$string = serialize($data);
elseif ( @is_string( $data ))
$string = $data;
$size = strlen($string);
wp_ffpc_log ( ' set key: "'. $key . '", size: '. $size . ' byte(s)' );
}
switch ($wp_ffpc_config['cache_type'])
{
case 'apc':
/* use apc_store to overwrite data is existed */
if ( $compress )
$data = gzdeflate ( $data , 1 );
return apc_store( $key , $data , $exp );
break;
case 'memcache':
if ( $wp_ffpc_backend != NULL )
/* false to disable compression, vital for nginx */
$wp_ffpc_backend->set ( $key, $data , false, $exp );
else
return false;
break;
case 'memcached':
if ( $wp_ffpc_backend != NULL )
$wp_ffpc_backend->set ( $key, $data , $exp );
else
return false;
break;
}
}
/**
* gets cached element by key
*
* @param &$key: key of needed cache element
*
*/
function wp_ffpc_get( &$key , $uncompress = false ) {
global $wp_ffpc_config;
global $wp_ffpc_backend;
/* syslog */
wp_ffpc_log ( ' get key: "'.$key . '"' );
switch ($wp_ffpc_config['cache_type'])
{
case 'apc':
$value = apc_fetch($key);
if ( $uncompress )
$value = gzinflate ( $value );
return $value;
case 'memcache':
case 'memcached':
if ( $wp_ffpc_backend != NULL )
return $wp_ffpc_backend->get($key);
else
return false;
default:
return false;
}
}
/**
* handles log messages
*
* @param $string log messagr
*/
function wp_ffpc_log ( $string ) {
global $wp_ffpc_config;
/* syslog */
if ($wp_ffpc_config['syslog'] && function_exists('syslog') )
syslog( WP_FFPC_LOG_LEVEL , WP_FFPC_PARAM . $string . WP_FFPC_LOG_TYPE_MSG );
}
?>

668
wp-ffpc.php Normal file
View file

@ -0,0 +1,668 @@
<?php
/*
Plugin Name: WP-FFPC
Version: 0.4
Plugin URI: http://petermolnar.eu/wordpress/wp-ffpc
Description: Fast Full Page Cache, backend can be memcached or APC
Author: Peter Molnar
Author URI: http://petermolnar.eu/
License: GPL2
*/
/* Copyright 2010-2011 Peter Molnar (email : hello@petermolnar.eu )
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* checks for SSL connection
*/
if ( ! function_exists ( 'replace_if_ssl' ) ) {
function replace_if_ssl ( $string ) {
if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) )
return str_replace ( 'http://' , 'https://' , $string );
else
return $string;
}
}
/* fix */
if ( ! defined( 'WP_PLUGIN_URL_' ) )
{
if ( defined( 'WP_PLUGIN_URL' ) )
define( 'WP_PLUGIN_URL_' , replace_if_ssl ( WP_PLUGIN_URL ) );
else
define( 'WP_PLUGIN_URL_', replace_if_ssl ( get_option( 'siteurl' ) ) . '/wp-content/plugins' );
}
if ( ! defined( 'WP_PLUGIN_DIR' ) )
define( 'WP_PLUGIN_DIR', ABSPATH . 'wp-content/plugins' );
/* constants */
define ( 'WP_FFPC_PARAM' , 'wp-ffpc' );
define ( 'WP_FFPC_OPTION_GROUP' , 'wp-ffpcparams' );
define ( 'WP_FFPC_OPTIONS_PAGE' , 'wp-ffpcoptions' );
define ( 'WP_FFPC_URL' , WP_PLUGIN_URL_ . '/' . WP_FFPC_PARAM );
define ( 'WP_FFPC_DIR' , WP_PLUGIN_DIR . '/' . WP_FFPC_PARAM );
define ( 'WP_FFPC_CONF_DIR' , WP_PLUGIN_DIR . '/' . WP_FFPC_PARAM .'/config' );
define ( 'WP_FFPC_ACACHE_MAIN_FILE' , ABSPATH . 'wp-content/advanced-cache.php' );
define ( 'WP_FFPC_ACACHE_INC_FILE' , WP_FFPC_DIR. '/advanced-cache.php' );
define ( 'WP_FFPC_ACACHE_COMMON_FILE' , WP_FFPC_DIR. '/wp-ffpc-common.php' );
define ( 'WP_FFPC_CONFIG_VAR' , '$wp_ffpc_config' );
include_once (WP_FFPC_DIR .'/wp-ffpc-common.php');
if (!class_exists('WPFFPC')) {
/**
* main class
*
*/
class WPFFPC {
/* for options array */
var $options = array();
/* for default options array */
var $defaults = array();
/* memcached server object */
var $memcached = NULL;
var $memcached_string = '';
/* status, 0 = nothing happened*/
var $status = 0;
/**
* constructor
*
*/
function __construct() {
/* register options */
$this->get_options();
/* check is backend is available */
$alive = wp_ffpc_init( $this->options );
/* don't register hooks if backend is dead */
if ($alive)
{
/* init inactivation hooks */
add_action('switch_theme', array( $this , 'invalidate'), 0);
add_action('edit_post', array( $this , 'invalidate'), 0);
add_action('publish_post', array( $this , 'invalidate'), 0);
add_action('delete_post', array( $this , 'invalidate'), 0);
/* Capture and register if a redirect is sent back from WP, so the cache
can cache (or ignore) it. Redirects were source of problems for blogs
with more than one host name (eg. domain.com and www.domain.com) comined
with the use of Hyper Cache.*/
add_filter('redirect_canonical', array( $this , 'redirect_canonical') , 10, 2);
}
/* add admin styling */
if( is_admin() )
{
wp_enqueue_style( WP_FFPC_PARAM . '.admin.css' , WP_FFPC_URL . '/css/'. WP_FFPC_PARAM .'.admin.css', false, '0.1');
}
/* on activation */
register_activation_hook(__FILE__ , array( $this , 'activate') );
/* on deactivation */
register_deactivation_hook(__FILE__ , array( $this , 'deactivate') );
/* on uninstall */
register_uninstall_hook(__FILE__ , array( $this , 'uninstall') );
/* init plugin in the admin section */
/* if multisite, admin page will be on network admin section */
if ( MULTISITE )
add_action('network_admin_menu', array( $this , 'admin_init') );
/* not network, will be in simple admin menu */
else
add_action('admin_menu', array( $this , 'admin_init') );
}
/**
* activation hook: save default settings in order to eliminate bugs.
*
*/
function activate ( ) {
$this->save_settings( true );
}
/**
* init function for admin section
*
*/
function admin_init () {
/* register options */
add_site_option( WP_FFPC_PARAM, $this->options , '' , 'no');
/* save parameter updates, if there are any */
if ( isset($_POST[WP_FFPC_PARAM . '-save']) )
{
$this->save_settings ();
$this->status = 1;
header("Location: admin.php?page=" . WP_FFPC_OPTIONS_PAGE . "&saved=true");
}
add_menu_page('Edit WP-FFPC options', __('WP-FFPC', WP_FFPC_PARAM ), 10, WP_FFPC_OPTIONS_PAGE , array ( $this , 'admin_panel' ) );
}
/**
* settings panel at admin section
*
*/
function admin_panel ( ) {
/**
* security
*/
if( ! function_exists( 'current_user_can' ) || ! current_user_can( 'manage_options' ) ){
die( );
}
/**
* if options were saved
*/
if ($_GET['saved']=='true' || $this->status == 1) : ?>
<div id='setting-error-settings_updated' class='updated settings-error'><p><strong>Settings saved.</strong></p></div>
<?php endif;
/**
* the admin panel itself
*/
?>
<?php if ( !WP_CACHE ) : ?>
<div class="updated settings-error"><p><strong>WARNING: WP_CACHE is disabled, plugin will not work that way. Please add define( 'WP_CACHE', true ); into the beginning of wp-config.php</strong></p></div>
<?php endif; ?>
<div class="wrap">
<h2><?php _e( 'WP-FFPC settings', WP_FFPC_PARAM ) ; ?></h2>
<form method="post" action="#">
<fieldset>
<legend><?php _e( 'Global settings', WP_FFPC_PARAM ); ?></legend>
<dl>
<div class="grid50">
<dt>
<label for="cache_type"><?php _e('Cache invalidation method', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<select name="cache_type" id="cache_type">
<?php $this->cache_type ( $this->options['cache_type'] ) ?>
</select>
<span class="description"><?php _e('Select cache invalidation method.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->cache_type( $this->defaults['cache_type'] , true ) ; ?></span>
</dd>
<dt>
<label for="expire"><?php _e('Entry invalidation time', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="number" name="expire" id="expire" value="<?php echo $this->options['expire']; ?>" />
<span class="description"><?php _e('How long will an entry be valid', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php echo $this->defaults['expire']; ?></span>
</dd>
<dt>
<label for="charset"><?php _e('Charset to send data with.', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="text" name="charset" id="charset" value="<?php echo $this->options['charset']; ?>" />
<span class="description"><?php _e('Charset of HTML and XML (pages and feeds) data.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php echo $this->defaults['charset']; ?></span>
</dd>
<dt>
<label for="invalidation_method"><?php _e('Cache invalidation method', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<select name="invalidation_method" id="invalidation_method">
<?php $this->invalidation_method ( $this->options['invalidation_method'] ) ?>
</select>
<span class="description"><?php _e('Select cache invalidation method. <p><strong>WARNING! When selection "all", the cache will be fully flushed, including elements that were set by other applications.</strong></p>', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->invalidation_method( $this->defaults['invalidation_method'] , true ) ; ?></span>
</dd>
<dt>
<label for="prefix_data"><?php _e('Data prefix', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="text" name="prefix_data" id="prefix_data" value="<?php echo $this->options['prefix_data']; ?>" />
<span class="description"><?php _e('Prefix for HTML content keys, can be used in nginx.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php echo $this->defaults['prefix_data']; ?></span>
</dd>
<dt>
<label for="prefix_meta"><?php _e('Meta prefix', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="text" name="prefix_meta" id="prefix_meta" value="<?php echo $this->options['prefix_meta']; ?>" />
<span class="description"><?php _e('Prefix for meta content keys, used only with PHP processing.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php echo $this->defaults['prefix_meta']; ?></span>
</dd>
<dt>
<label for="debug"><?php _e("Enable debug mode", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="debug" id="debug" value="1" <?php checked($this->options['debug'],true); ?> />
<span class="description"><?php _e('An additional header, "X-Cache-Engine" will be added when pages are served through WP-FFPC.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['debug']); ?></span>
</dd>
<dt>
<label for="syslog"><?php _e("Enable syslog messages", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="syslog" id="syslog" value="1" <?php checked($this->options['syslog'],true); ?> />
<span class="description"><?php _e('Writes sets, gets and flushes at INFO level into syslog, using "syslog" function of PHP.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['syslog']); ?></span>
</dd>
<dt>
<label for="pingback_status"><?php _e("Enable pingback links", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="pingback_status" id="pingback_status" value="1" <?php checked($this->options['pingback_status'],true); ?> />
<span class="description"><?php _e('Enable "X-Pingback" headers in cached pages; will always use accessed hostname as host!', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['pingback_status']); ?></span>
</dd>
</div>
<div class="grid50">
<dt>
<label for="cache_loggedin"><?php _e('Enable cache for logged in users', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="cache_loggedin" id="cache_loggedin" value="1" <?php checked($this->options['cache_loggedin'],true); ?> />
<span class="description"><?php _e('Cache pages even if user is logged in.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['cache_loggedin']); ?></span>
</dd>
<dt>
<label for="nocache_home"><?php _e("Don't cache home", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_home" id="nocache_home" value="1" <?php checked($this->options['nocache_home'],true); ?> />
<span class="description"><?php _e('Exclude home page from caching', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['nocache_home']); ?></span>
</dd>
<dt>
<label for="nocache_feed"><?php _e("Don't cache feeds", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_feed" id="nocache_feed" value="1" <?php checked($this->options['nocache_feed'],true); ?> />
<span class="description"><?php _e('Exclude feeds from caching.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['nocache_feed']); ?></span>
</dd>
<dt>
<label for="nocache_archive"><?php _e("Don't cache archives", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_archive" id="nocache_archive" value="1" <?php checked($this->options['nocache_archive'],true); ?> />
<span class="description"><?php _e('Exclude archives from caching.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['nocache_archive']); ?></span>
</dd>
<dt>
<label for="nocache_single"><?php _e("Don't cache posts (and single-type entries)", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_single" id="nocache_single" value="1" <?php checked($this->options['nocache_single'],true); ?> />
<span class="description"><?php _e('Exclude singles from caching.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['nocache_single']); ?></span>
</dd>
<dt>
<label for="nocache_page"><?php _e("Don't cache pages", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="nocache_page" id="nocache_page" value="1" <?php checked($this->options['nocache_page'],true); ?> />
<span class="description"><?php _e('Exclude pages from caching.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['nocache_page']); ?></span>
</dd>
</div>
</dl>
</fieldset>
<fieldset class="grid50">
<legend><?php _e('Settings for memcached backend', WP_FFPC_PARAM); ?></legend>
<?php if ( !class_exists('Memcache') && !class_exists('Memcached') ) : ?>
<h1 class="error">No PHP memcached extension was found. To use memcached, you need PHP Memcache or PHP Memcached extension.</h1>
<?php endif; ?>
<?php if ( $this->options['cache_type'] == 'memcached' || $this->options['cache_type'] == 'memcache' ) : ?>
<div>
<strong>
<?php
_e( 'Backend status: ', WP_FFPC_PARAM );
$server_status = wp_ffpc_init( $this->options);
$server_status = ( empty($server_status) || $server_status == 0 ) ? '<span class="error-msg">down</span>' : '<span class="ok-msg">up & running</span>' ;
echo $server_status;
?>
</strong>
</div>
<?php endif; ?>
<dl>
<dt>
<label for="host"><?php _e('Host', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="text" name="host" id="host" value="<?php echo $this->options['host']; ?>" />
<span class="description"><?php _e('Hostname for memcached server', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php echo $this->defaults['host']; ?></span>
</dd>
<dt>
<label for="port"><?php _e('Host', WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="number" name="port" id="port" value="<?php echo $this->options['port']; ?>" />
<span class="description"><?php _e('Port for memcached server', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php echo $this->defaults['port']; ?></span>
</dd>
</dl>
</fieldset>
<?php if ( $this->options['cache_type'] == 'memcache' || $this->options['cache_type'] == 'memcached' ) : ?>
<fieldset class="grid50">
<legend><?php _e('Sample config for nginx to utilize the data entries', WP_FFPC_PARAM); ?></legend>
<?php
$search = array( 'DATAPREFIX', 'MEMCACHEDHOST', 'MEMCACHEDPORT');
$replace = array ( $this->options['prefix_data'], $this->options['host'], $this->options['port'] );
$nginx = file_get_contents ( WP_FFPC_DIR .'/nginx-sample.conf' );
$nginx = str_replace ( $search , $replace , $nginx );
?>
<pre><?php echo $nginx; ?></pre>
</fieldset>
<?php endif; ?>
<fieldset class="grid50">
<legend><?php _e('Settings for APC', WP_FFPC_PARAM); ?></legend>
<dl>
<dt>
<label for="apc_compress"><?php _e("Compress entries", WP_FFPC_PARAM); ?></label>
</dt>
<dd>
<input type="checkbox" name="apc_compress" id="apc_compress" value="1" <?php checked($this->options['apc_compress'],true); ?> />
<span class="description"><?php _e('Try to compress APC entries. Requires PHP ZLIB.', WP_FFPC_PARAM); ?></span>
<span class="default"><?php _e('Default ', WP_FFPC_PARAM); ?>: <?php $this->print_bool( $this->defaults['apc_compress']); ?></span>
</dd>
</dl>
</fieldset>
<p class="clearcolumns"><input class="button-primary" type="submit" name="<?php echo WP_FFPC_PARAM; ?>-save" id="<?php echo WP_FFPC_PARAM; ?>-save" value="Save Changes" /></p>
</form>
<?php
}
/**
* generates cache type select box
*
* @param $current
* the active or required size's identifier
*
* @param $returntext
* boolean: is true, the description will be returned of $current size
*
* @return
* prints either description of $current
* or option list for a <select> input field with $current set as active
*
*/
function cache_type ( $current , $returntext = false ) {
$e = array (
'apc' => 'use APC as store',
'memcache' => 'use memcached server with Memcache extension',
'memcached' => 'use memcached server with Memcached extension',
);
$this->print_select_options ( $e , $current , $returntext );
}
/**
* deactivation hook: clear advanced-cache config file
*
*/
function deactivate ( ) {
if (@file_exists (WP_FFPC_ACACHE_MAIN_FILE))
@unlink (WP_FFPC_ACACHE_MAIN_FILE);
}
/**
* invalidate cache
*/
function invalidate ( $post_id ) {
wp_ffpc_clear ( $post_id );
}
/**
* generates invalidation method select box
*
* @param $current
* the active or required size's identifier
*
* @param $returntext
* boolean: is true, the description will be returned of $current size
*
* @return
* prints either description of $current
* or option list for a <select> input field with $current set as active
*
*/
function invalidation_method ( $current , $returntext = false ) {
$e = array (
0 => 'all cached pages (WARNING! Flushes _all_ cached entrys! )',
1 => 'only modified post',
);
$this->print_select_options ( $e , $current , $returntext );
}
/**
* generates main advanced-cache system-wide config file
*
*/
function generate_config() {
$acache = WP_FFPC_ACACHE_MAIN_FILE;
/* is file currently exists, delete it*/
if ( @file_exists( $acache ))
unlink ($acache);
/* is deletion was unsuccessful, die, we have no rights to do that */
if ( @file_exists( $acache ))
return false;
$string = '<?php'. "\n" .
'global '. WP_FFPC_CONFIG_VAR .' ;' . "\n";
foreach($this->options as $key => $val) {
if (is_string($val))
$val = "'" . $val . "'";
$string .= WP_FFPC_CONFIG_VAR . '[\'' . $key . '\']=' . $val . ";\n";
}
$string .= "\n\ninclude_once ('" . WP_FFPC_ACACHE_COMMON_FILE . "');\ninclude_once ('" . WP_FFPC_ACACHE_INC_FILE . "');\n";
file_put_contents($acache, $string);
return true;
}
/**
* parameters array with default values;
*
*/
function get_options ( ) {
$defaults = array (
'port'=>11211,
'host'=>'127.0.0.1',
'expire'=>300,
'invalidation_method'=>0,
'prefix_meta' =>'meta-',
'prefix_data' =>'data-',
'charset' => 'utf-8',
'pingback_status'=> false,
'debug' => true,
'syslog' => false,
'cache_type' => 'memcache',
'cache_loggedin' => false,
'nocache_home' => false,
'nocache_feed' => false,
'nocache_archive' => false,
'nocache_single' => false,
'nocache_page' => false,
'apc_compress' => false,
);
$this->defaults = $defaults;
$this->options = get_site_option( WP_FFPC_PARAM , $defaults, false );
}
/**
* prints `true` or `false` depending on a bool variable.
*
* @param $val
* The boolen variable to print status of.
*
*/
function print_bool ( $val ) {
$bool = $val? 'true' : 'false';
echo $bool;
}
/**
* select field processor
*
* @param sizes
* array to build <option> values of
*
* @param $current
* the current resize type
*
* @param $returntext
* boolean: is true, the description will be returned of $current type
*
* @return
* prints either description of $current
* or option list for a <select> input field with $current set as active
*
*/
function print_select_options ( $sizes, $current, $returntext=false ) {
if ( $returntext )
{
_e( $sizes[ $current ] , WP_FFPC_PARAM);
return;
}
foreach ($sizes as $ext=>$name)
{
?>
<option value="<?php echo $ext ?>" <?php selected( $ext , $current ); ?>>
<?php _e( $name , WP_FFPC_PARAM); ?>
</option>
<?php
}
}
/**
* function to be able to store redirects
*/
function redirect_canonical($redirect_url, $requested_url) {
global $wp_nmc_redirect;
$wp_nmc_redirect = $redirect_url;
return $redirect_url;
}
/**
* save settings function
*
*/
function save_settings ( $firstrun = false ) {
/**
* update params from $_POST
*/
foreach ($this->options as $name=>$optionvalue)
{
if (!empty($_POST[$name]))
{
$update = $_POST[$name];
if (strlen($update)!=0 && !is_numeric($update))
$update = stripslashes($update);
}
elseif ( ( empty($_POST[$name]) && is_bool ($this->defaults[$name]) ) || is_numeric( $update ) )
{
$update = 0;
}
else
{
$update = $this->defaults[$name];
}
$this->options[$name] = $update;
}
update_site_option( WP_FFPC_PARAM , $this->options );
$this->invalidate('system_flush');
if ( ! $firstrun )
$this->generate_config();
}
/**
* clean up at uninstall
*
*/
function uninstall ( ) {
delete_site_option( WP_FFPC_PARAM );
}
}
}
/**
* instantiate the class
*/
$wp_nmc = new WPFFPC();
?>