diff --git a/wp-common b/wp-common index 2749265..d12b345 160000 --- a/wp-common +++ b/wp-common @@ -1 +1 @@ -Subproject commit 2749265f11b2c2e9d34cc35aa74aca55b2b1b93b +Subproject commit d12b345560c77634b25fc8ee7571c19730794c69 diff --git a/wp-ffpc-backend.php b/wp-ffpc-backend.php index c26a783..9923c33 100644 --- a/wp-ffpc-backend.php +++ b/wp-ffpc-backend.php @@ -1,992 +1,973 @@ plugin_constant ) ); + } + + /* set config */ + $this->options = $config; + + /* set network flag */ + $this->network = $network; + + /* these are the list of the cookies to look for when looking for logged in user */ + $this->cookies = array ( 'comment_author_' , 'wordpressuser_' , 'wp-postpass_', 'wordpress_logged_in_' ); + + /* make utilities singleton */ + $this->utilities = PluginUtils::S(); + + /* map the key with the predefined schemes */ + $ruser = isset ( $_SERVER['REMOTE_USER'] ) ? $_SERVER['REMOTE_USER'] : ''; + $ruri = isset ( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : ''; + $rhost = isset ( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : ''; + $scookie = isset ( $_COOKIE['PHPSESSID'] ) ? $_COOKIE['PHPSESSID'] : ''; + + $this->urimap = array( + '$scheme' => str_replace ( '://', '', $this->utilities->replace_if_ssl ( 'http://' ) ), + '$host' => $rhost, + '$request_uri' => $ruri, + '$remote_user' => $ruser, + '$cookie_PHPSESSID' => $scookie, + ); + + /* split hosts entry to servers */ + $this->set_servers(); + + /* call backend initiator based on cache type */ + $init = $this->proxy( 'init' ); + + /* info level */ + $this->log ( __translate__('init starting', $this->plugin_constant )); + $this->$init(); - /* __ only availabe if we're running from the inside of wordpress, not in advanced-cache.php phase */ - if ( function_exists ( '__' ) ) { - function __translate__ ( $text, $domain ) { return __($text, $domain); } } - else { - function __translate__ ( $text, $domain ) { return $text; } + + /*********************** PUBLIC / PROXY FUNCTIONS ***********************/ + + /** + * build key to make requests with + * + * @param string $prefix prefix to add to prefix + * + */ + public function key ( &$prefix ) { + /* data is string only with content, meta is not used in nginx */ + $key = $prefix . str_replace ( array_keys( $this->urimap ), $this->urimap, $this->options['key'] ); + $this->log ( sprintf( __translate__( 'original key configuration: %s', $this->plugin_constant ), $this->options['key'] ) ); + $this->log ( sprintf( __translate__( 'setting key to: %s', $this->plugin_constant ), $key ) ); + return $key; + } + + + /** + * public get function, transparent proxy to internal function based on backend + * + * @param string $key Cache key to get value for + * + * @return mixed False when entry not found or entry value on success + */ + public function get ( &$key ) { + + /* look for backend aliveness, exit on inactive backend */ + if ( ! $this->is_alive() ) + return false; + + /* log the current action */ + $this->log ( sprintf( __translate__( 'get %s', $this->plugin_constant ), $key ) ); + + /* proxy to internal function */ + $internal = $this->proxy( 'get' ); + $result = $this->$internal( $key ); + + if ( $result === false ) + $this->log ( sprintf( __translate__( 'failed to get entry: %s', $this->plugin_constant ), $key ) ); + + return $result; + } + + /** + * public set function, transparent proxy to internal function based on backend + * + * @param string $key Cache key to set with ( reference only, for speed ) + * @param mixed $data Data to set ( reference only, for speed ) + * + * @return mixed $result status of set function + */ + public function set ( &$key, &$data ) { + + /* look for backend aliveness, exit on inactive backend */ + if ( ! $this->is_alive() ) + return false; + + /* log the current action */ + $this->log ( sprintf( __translate__( 'set %s expiration time: %s', $this->plugin_constant ), $key, $this->options['expire'] ) ); + + /* proxy to internal function */ + $internal = $this->proxy( 'set' ); + $result = $this->$internal( $key, $data ); + + /* check result validity */ + if ( $result === false ) + $this->log ( sprintf( __translate__( 'failed to set entry: %s', $this->plugin_constant ), $key ), LOG_WARNING ); + + return $result; + } + + /** + * public get function, transparent proxy to internal function based on backend + * + * @param string $post_id ID of post to invalidate + * @param boolean $force Force flush cache + * @param boolean $comment Clear a single page based on comment trigger + * + */ + public function clear ( $post_id = false, $force = false ) { + + /* look for backend aliveness, exit on inactive backend */ + if ( ! $this->is_alive() ) + return false; + + /* exit if no post_id is specified */ + if ( empty ( $post_id ) && $force === false ) { + $this->log ( __translate__('not clearing unidentified post ', $this->plugin_constant ), LOG_WARNING ); + return false; + } + + /* if invalidation method is set to full, flush cache */ + if ( ( $this->options['invalidation_method'] === 0 || $force === true ) ) { + /* log action */ + $this->log ( __translate__('flushing cache', $this->plugin_constant ) ); + + /* proxy to internal function */ + $internal = $this->proxy ( 'flush' ); + $result = $this->$internal(); + + if ( $result === false ) + $this->log ( __translate__('failed to flush cache', $this->plugin_constant ), LOG_WARNING ); + + return $result; + } + + /* storage for entries to clear */ + $to_clear = array(); + + /* clear taxonomies if settings requires it */ + if ( $this->options['invalidation_method'] == 2 ) { + /* this will only clear the current blog's entries */ + $this->taxonomy_links( $to_clear ); + } + + /* if there's a post id pushed, it needs to be invalidated in all cases */ + if ( !empty ( $post_id ) ) { + + /* need permalink functions */ + if ( !function_exists('get_permalink') ) + include_once ( ABSPATH . 'wp-includes/link-template.php' ); + + /* get path from permalink */ + $path = substr ( get_permalink( $post_id ) , 7 ); + + /* no path, don't do anything */ + if ( empty( $path ) ) { + $this->log ( sprintf( __translate__( 'unable to determine path from Post Permalink, post ID: %s', $this->plugin_constant ), $post_id ), LOG_WARNING ); + return false; + } + + if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) ) + $protocol = 'https://'; + else + $protocol = 'http://'; + + /* elements to clear + values are keys, faster to process and eliminates duplicates + */ + $to_clear[ $protocol . $path ] = true; + } + + foreach ( $to_clear as $link => $dummy ) { + /* clear all feeds as well */ + $to_clear[ $link. 'feed' ] = true; + } + + /* add data & meta prefixes */ + foreach ( $to_clear as $link => $dummy ) { + unset ( $to_clear [ $link ]); + $to_clear[ $this->options[ 'prefix_meta' ] . $link ] = true; + $to_clear[ $this->options[ 'prefix_data' ] . $link ] = true; + } + + /* run clear */ + $internal = $this->proxy ( 'clear' ); + $this->$internal ( $to_clear ); + } + + /** + * clear cache triggered by new comment + * + * @param $comment_id Comment ID + * @param $comment_object The whole comment object ? + */ + public function clear_by_comment ( $comment_id, $comment_object ) { + if ( empty( $comment_id ) ) + return false; + + $comment = get_comment( $comment_id ); + $post_id = $comment->comment_post_ID; + if ( !empty( $post_id ) ) + $this->clear ( $post_id ); + + unset ( $comment ); + unset ( $post_id ); + } + + /** + * to collect all permalinks of all taxonomy terms used in invalidation & precache + * + * @param array &$links Passed by reference array that has to be filled up with the links + * @param mixed $site Site ID or false; used in WordPress Network + * + */ + public function taxonomy_links ( &$links, $site = false ) { + + if ( $site !== false ) { + $current_blog = get_current_blog_id(); + switch_to_blog( $site ); + + $url = get_blog_option ( $site, 'siteurl' ); + if ( substr( $url, -1) !== '/' ) + $url = $url . '/'; + + $links[ $url ] = true; + } + + /* we're only interested in public taxonomies */ + $args = array( + 'public' => true, + ); + + /* get taxonomies as objects */ + $taxonomies = get_taxonomies( $args, 'objects' ); + + if ( !empty( $taxonomies ) ) { + foreach ( $taxonomies as $taxonomy ) { + /* reset array, just in case */ + $terms = array(); + + /* get all the terms for this taxonomy, only if not empty */ + $sargs = array( + 'hide_empty' => true, + 'fields' => 'all', + 'hierarchical' =>false, + ); + $terms = get_terms ( $taxonomy->name , $sargs ); + + if ( !empty ( $terms ) ) { + foreach ( $terms as $term ) { + /* get the permalink for the term */ + $link = get_term_link ( $term->slug, $taxonomy->name ); + /* add to container */ + $links[ $link ] = true; + /* remove the taxonomy name from the link, lots of plugins remove this for SEO, it's better to include them than leave them out + in worst case, we cache some 404 as well + */ + $link = str_replace ( '/'.$taxonomy->rewrite['slug'], '', $link ); + /* add to container */ + $links[ $link ] = true; + } + } + } + } + + /* switch back to original site if we navigated away */ + if ( $site !== false ) { + switch_to_blog( $current_blog ); + } + + } + + /** + * get backend aliveness + * + * @return array Array of configured servers with aliveness value + * + */ + public function status () { + + /* look for backend aliveness, exit on inactive backend */ + if ( ! $this->is_alive() ) + return false; + + $internal = $this->proxy ( 'status' ); + $this->$internal(); + return $this->status; + } + + /** + * backend proxy function name generator + * + * @return string Name of internal function based on cache_type + * + */ + private function proxy ( $method ) { + return $this->options['cache_type'] . '_' . $method; + } + + /** + * function to check backend aliveness + * + * @return boolean true if backend is alive, false if not + * + */ + private function is_alive() { + if ( ! $this->alive ) { + $this->log ( __translate__("backend is not active, exiting function ", $this->plugin_constant ) . __FUNCTION__, LOG_WARNING ); + return false; + } + + return true; + } + + /** + * split hosts string to backend servers + * + * + */ + private function set_servers () { + /* replace servers array in config according to hosts field */ + $servers = explode( self::host_separator , $this->options['hosts']); + + $options['servers'] = array(); + + foreach ( $servers as $snum => $sstring ) { + + $separator = strpos( $sstring , self::port_separator ); + $host = substr( $sstring, 0, $separator ); + $port = substr( $sstring, $separator + 1 ); + // unix socket failsafe + if ( empty ($port) ) $port = 0; + + $this->options['servers'][$sstring] = array ( + 'host' => $host, + 'port' => $port + ); + } + + } + + /** + * get current array of servers + * + * @return array Server list in current config + * + */ + public function get_servers () { + $r = isset ( $this->options['servers'] ) ? $this->options['servers'] : ''; + return $r; + } + + /** + * log wrapper to include options + * + * @var mixed $message Message to log + * @var int $log_level Log level + */ + private function log ( $message, $log_level = LOG_WARNING ) { + if ( !isset ( $this->options['log'] ) || $this->options['log'] != 1 ) + return false; + else + $this->utilities->log ( $this->plugin_constant , $message, $log_level ); + } + + /*********************** END PUBLIC FUNCTIONS ***********************/ + /*********************** APC FUNCTIONS ***********************/ + /** + * init apc backend: test APC availability and set alive status + */ + private function apc_init () { + /* verify apc functions exist, apc extension is loaded */ + if ( ! function_exists( 'apc_cache_info' ) ) { + $this->log ( __translate__('APC extension missing', $this->plugin_constant ) ); + return false; + } + + /* verify apc is working */ + if ( apc_cache_info("user",true) ) { + $this->log ( __translate__('backend OK', $this->plugin_constant ) ); + $this->alive = true; + } + } + + /** + * health checker for APC + * + * @return boolean Aliveness status + * + */ + private function apc_status () { + $this->status = true; + return $this->alive; + } + + /** + * get function for APC backend + * + * @param string $key Key to get values for + * + * @return mixed Fetched data based on key + * + */ + private function apc_get ( &$key ) { + return apc_fetch( $key ); + } + + /** + * Set function for APC backend + * + * @param string $key Key to set with + * @param mixed $data Data to set + * + * @return boolean APC store outcome + */ + private function apc_set ( &$key, &$data ) { + return apc_store( $key , $data , $this->options['expire'] ); + } + + + /** + * Flushes APC user entry storage + * + * @return boolean APC flush outcome status + * + */ + private function apc_flush ( ) { + return apc_clear_cache('user'); + } + + /** + * Removes entry from APC or flushes APC user entry storage + * + * @param mixed $keys Keys to clear, string or array + */ + private function apc_clear ( &$keys ) { + /* make an array if only one string is present, easier processing */ + if ( !is_array ( $keys ) ) + $keys = array ( $keys => true ); + + foreach ( $keys as $key => $dummy ) { + if ( ! apc_delete ( $key ) ) { + $this->log ( sprintf( __translate__( 'Failed to delete APC entry: %s', $this->plugin_constant ), $key ), LOG_ERR ); + //throw new Exception ( __translate__('Deleting APC entry failed with key ', $this->plugin_constant ) . $key ); + } + else { + $this->log ( sprintf( __translate__( 'APC entry delete: %s', $this->plugin_constant ), $key ) ); + } + } + } + + /*********************** END APC FUNCTIONS ***********************/ + /*********************** APCu FUNCTIONS ***********************/ + /** + * init apcu backend: test APCu availability and set alive status + */ + private function apcu_init () { + /* verify apcu functions exist, apcu extension is loaded */ + if ( ! function_exists( 'apcu_cache_info' ) ) { + $this->log ( __translate__('APCu extension missing', $this->plugin_constant ) ); + return false; + } + + /* verify apcu is working */ + if ( apcu_cache_info("user",true) ) { + $this->log ( __translate__('backend OK', $this->plugin_constant ) ); + $this->alive = true; + } + } + + /** + * health checker for APC + * + * @return boolean Aliveness status + * + */ + private function apcu_status () { + $this->status = true; + return $this->alive; + } + + /** + * get function for APC backend + * + * @param string $key Key to get values for + * + * @return mixed Fetched data based on key + * + */ + private function apcu_get ( &$key ) { + return apcu_fetch( $key ); + } + + /** + * Set function for APC backend + * + * @param string $key Key to set with + * @param mixed $data Data to set + * + * @return boolean APC store outcome + */ + private function apcu_set ( &$key, &$data ) { + return apcu_store( $key , $data , $this->options['expire'] ); + } + + + /** + * Flushes APC user entry storage + * + * @return boolean APC flush outcome status + * + */ + private function apcu_flush ( ) { + return apcu_clear_cache(); + } + + /** + * Removes entry from APC or flushes APC user entry storage + * + * @param mixed $keys Keys to clear, string or array + */ + private function apcu_clear ( &$keys ) { + /* make an array if only one string is present, easier processing */ + if ( !is_array ( $keys ) ) + $keys = array ( $keys => true ); + + foreach ( $keys as $key => $dummy ) { + if ( ! apcu_delete ( $key ) ) { + $this->log ( sprintf( __translate__( 'Failed to delete APC entry: %s', $this->plugin_constant ), $key ), LOG_ERR ); + //throw new Exception ( __translate__('Deleting APC entry failed with key ', $this->plugin_constant ) . $key ); + } + else { + $this->log ( sprintf( __translate__( 'APC entry delete: %s', $this->plugin_constant ), $key ) ); + } + } + } + + /*********************** END APC FUNCTIONS ***********************/ + + /*********************** XCACHE FUNCTIONS ***********************/ + /** + * init apc backend: test APC availability and set alive status + */ + private function xcache_init () { + /* verify apc functions exist, apc extension is loaded */ + if ( ! function_exists( 'xcache_info' ) ) { + $this->log ( __translate__('XCACHE extension missing', $this->plugin_constant ) ); + return false; + } + + /* verify apc is working */ + $info = xcache_info(); + if ( !empty( $info )) { + $this->log ( __translate__('backend OK', $this->plugin_constant ) ); + $this->alive = true; + } + } + + /** + * health checker for Xcache + * + * @return boolean Aliveness status + * + */ + private function xcache_status () { + $this->status = true; + return $this->alive; + } + + /** + * get function for APC backend + * + * @param string $key Key to get values for + * + * @return mixed Fetched data based on key + * + */ + private function xcache_get ( &$key ) { + if ( xcache_isset ( $key ) ) + return xcache_get( $key ); + else + return false; + } + + /** + * Set function for APC backend + * + * @param string $key Key to set with + * @param mixed $data Data to set + * + * @return boolean APC store outcome + */ + private function xcache_set ( &$key, &$data ) { + return xcache_set( $key , $data , $this->options['expire'] ); + } + + + /** + * Flushes APC user entry storage + * + * @return boolean APC flush outcome status + * + */ + private function xcache_flush ( ) { + return xcache_clear_cache(XC_TYPE_VAR); + } + + /** + * Removes entry from APC or flushes APC user entry storage + * + * @param mixed $keys Keys to clear, string or array + */ + private function xcache_clear ( &$keys ) { + /* make an array if only one string is present, easier processing */ + if ( !is_array ( $keys ) ) + $keys = array ( $keys => true ); + + foreach ( $keys as $key => $dummy ) { + if ( ! xcache_unset ( $key ) ) { + $this->log ( sprintf( __translate__( 'Failed to delete XCACHE entry: %s', $this->plugin_constant ), $key ), LOG_ERR ); + //throw new Exception ( __translate__('Deleting APC entry failed with key ', $this->plugin_constant ) . $key ); + } + else { + $this->log ( sprintf( __translate__( 'XCACHE entry delete: %s', $this->plugin_constant ), $key ) ); + } + } + } + + /*********************** END XCACHE FUNCTIONS ***********************/ + + + /*********************** MEMCACHED FUNCTIONS ***********************/ + /** + * init memcached backend + */ + private function memcached_init () { + /* Memcached class does not exist, Memcached extension is not available */ + if (!class_exists('Memcached')) { + $this->log ( __translate__(' Memcached extension missing', $this->plugin_constant ), LOG_ERR ); + return false; + } + + /* check for existing server list, otherwise we cannot add backends */ + if ( empty ( $this->options['servers'] ) && ! $this->alive ) { + $this->log ( __translate__("Memcached servers list is empty, init failed", $this->plugin_constant ), LOG_WARNING ); + return false; + } + + /* check is there's no backend connection yet */ + if ( $this->connection === NULL ) { + /* persistent backend needs an identifier */ + if ( $this->options['persistent'] == '1' ) + $this->connection = new Memcached( $this->plugin_constant ); + else + $this->connection = new Memcached(); + + /* use binary and not compressed format, good for nginx and still fast */ + $this->connection->setOption( Memcached::OPT_COMPRESSION , false ); + $this->connection->setOption( Memcached::OPT_BINARY_PROTOCOL , true ); + + if ( version_compare( phpversion( 'memcached' ) , '2.0.0', '>=' ) && ini_get( 'memcached.use_sasl' ) == 1 ) { + $this->connection->setSaslAuthData ( $this->options['authuser'], $this->options['authpass']); + } + } + + /* check if initialization was success or not */ + if ( $this->connection === NULL ) { + $this->log ( __translate__( 'error initializing Memcached PHP extension, exiting', $this->plugin_constant ) ); + return false; + } + + /* check if we already have list of servers, only add server(s) if it's not already connected */ + $servers_alive = array(); + if ( !empty ( $this->status ) ) { + $servers_alive = $this->connection->getServerList(); + /* create check array if backend servers are already connected */ + if ( !empty ( $servers ) ) { + foreach ( $servers_alive as $skey => $server ) { + $skey = $server['host'] . ":" . $server['port']; + $servers_alive[ $skey ] = true; + } + } + } + + /* adding servers */ + foreach ( $this->options['servers'] as $server_id => $server ) { + /* reset server status to unknown */ + $this->status[$server_id] = -1; + + /* only add servers that does not exists already in connection pool */ + if ( !@array_key_exists($server_id , $servers_alive ) ) { + $this->connection->addServer( $server['host'], $server['port'] ); + $this->log ( sprintf( __translate__( '%s added, persistent mode: %s', $this->plugin_constant ), $server_id, $this->options['persistent'] ) ); + } + } + + /* backend is now alive */ + $this->alive = true; + $this->memcached_status(); + } + + /** + * sets current backend alive status for Memcached servers + * + */ + private function memcached_status () { + /* server status will be calculated by getting server stats */ + $this->log ( __translate__("checking server statuses", $this->plugin_constant )); + /* get servers statistic from connection */ + $report = $this->connection->getStats(); + + foreach ( $report as $server_id => $details ) { + /* reset server status to offline */ + $this->status[$server_id] = 0; + /* if server uptime is not empty, it's most probably up & running */ + if ( !empty($details['uptime']) ) { + $this->log ( sprintf( __translate__( '%s server is up & running', $this->plugin_constant ), $server_id ) ); + $this->status[$server_id] = 1; + } + } + + } + + /** + * get function for Memcached backend + * + * @param string $key Key to get values for + * + */ + private function memcached_get ( &$key ) { + return $this->connection->get($key); + } + + /** + * Set function for Memcached backend + * + * @param string $key Key to set with + * @param mixed $data Data to set + * + */ + private function memcached_set ( &$key, &$data ) { + $result = $this->connection->set ( $key, $data , $this->options['expire'] ); + + /* if storing failed, log the error code */ + if ( $result === false ) { + $code = $this->connection->getResultCode(); + $this->log ( sprintf( __translate__( 'unable to set entry: %s', $this->plugin_constant ), $key ) ); + $this->log ( sprintf( __translate__( 'Memcached error code: %s', $this->plugin_constant ), $code ) ); + //throw new Exception ( __translate__('Unable to store Memcached entry ', $this->plugin_constant ) . $key . __translate__( ', error code: ', $this->plugin_constant ) . $code ); + } + + return $result; } /** * - * @var string $plugin_constant Namespace of the plugin - * @var mixed $connection Backend object storage variable - * @var boolean $alive Alive flag of backend connection - * @var boolean $network WordPress Network flag - * @var array $options Configuration settings array - * @var array $status Backends status storage - * @var array $cookies Logged in cookies to search for - * @var array $urimap Map to render key with - * @var object $utilities Utilities singleton + * Flush memcached entries + */ + private function memcached_flush ( ) { + return $this->connection->flush(); + } + + + /** + * Removes entry from Memcached or flushes Memcached storage + * + * @param mixed $keys String / array of string of keys to delete entries with + */ + private function memcached_clear ( &$keys ) { + + /* make an array if only one string is present, easier processing */ + if ( !is_array ( $keys ) ) + $keys = array ( $keys => true ); + + foreach ( $keys as $key => $dummy ) { + $kresult = $this->connection->delete( $key ); + + if ( $kresult === false ) { + $code = $this->connection->getResultCode(); + $this->log ( sprintf( __translate__( 'unable to delete entry: %s', $this->plugin_constant ), $key ) ); + $this->log ( sprintf( __translate__( 'Memcached error code: %s', $this->plugin_constant ), $code ) ); + } + else { + $this->log ( sprintf( __translate__( 'entry deleted: %s', $this->plugin_constant ), $key ) ); + } + } + } + /*********************** END MEMCACHED FUNCTIONS ***********************/ + + /*********************** MEMCACHE FUNCTIONS ***********************/ + /** + * init memcache backend + */ + private function memcache_init () { + /* Memcached class does not exist, Memcache extension is not available */ + if (!class_exists('Memcache')) { + $this->log ( __translate__('PHP Memcache extension missing', $this->plugin_constant ), LOG_ERR ); + return false; + } + + /* check for existing server list, otherwise we cannot add backends */ + if ( empty ( $this->options['servers'] ) && ! $this->alive ) { + $this->log ( __translate__("servers list is empty, init failed", $this->plugin_constant ), LOG_WARNING ); + return false; + } + + /* check is there's no backend connection yet */ + if ( $this->connection === NULL ) + $this->connection = new Memcache(); + + /* check if initialization was success or not */ + if ( $this->connection === NULL ) { + $this->log ( __translate__( 'error initializing Memcache PHP extension, exiting', $this->plugin_constant ) ); + return false; + } + + /* adding servers */ + foreach ( $this->options['servers'] as $server_id => $server ) { + if ( $this->options['persistent'] == '1' ) + $conn = 'pconnect'; + else + $conn = 'connect'; + + /* in case of unix socket */ + if ( $server['port'] === 0 ) + $this->status[$server_id] = $this->connection->$conn ( 'unix:/' . $server['host'] ); + else + $this->status[$server_id] = $this->connection->$conn ( $server['host'] , $server['port'] ); + + $this->log ( sprintf( __translate__( '%s added, persistent mode: %s', $this->plugin_constant ), $server_id, $this->options['persistent'] ) ); + } + + /* backend is now alive */ + $this->alive = true; + $this->memcache_status(); + } + + /** + * check current backend alive status for Memcached * */ - class WP_FFPC_Backend { - - const network_key = 'network'; - const host_separator = ','; - const port_separator = ':'; - - private $plugin_constant = 'wp-ffpc'; - private $connection = NULL; - private $alive = false; - private $network = false; - private $options = array(); - private $status = array(); - public $cookies = array(); - private $urimap = array(); - private $utilities; - - /** - * constructor - * - * @param mixed $config Configuration options - * @param boolean $network WordPress Network indicator flah - * - */ - public function __construct( $config, $network = false ) { - - /* no config, nothing is going to work */ - if ( empty ( $config ) ) { - return false; - //die ( __translate__ ( 'WP-FFPC Backend class received empty configuration array, the plugin will not work this way', $this->plugin_constant ) ); - } - - /* set config */ - $this->options = $config; - - /* set network flag */ - $this->network = $network; - - /* these are the list of the cookies to look for when looking for logged in user */ - $this->cookies = array ( 'comment_author_' , 'wordpressuser_' , 'wp-postpass_', 'wordpress_logged_in_' ); - - /* make utilities singleton */ - $this->utilities = WP_Plugins_Utilities_v1::Utility(); - - /* map the key with the predefined schemes */ - $ruser = isset ( $_SERVER['REMOTE_USER'] ) ? $_SERVER['REMOTE_USER'] : ''; - $ruri = isset ( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : ''; - $rhost = isset ( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : ''; - $scookie = isset ( $_COOKIE['PHPSESSID'] ) ? $_COOKIE['PHPSESSID'] : ''; - - $this->urimap = array( - '$scheme' => str_replace ( '://', '', $this->utilities->replace_if_ssl ( 'http://' ) ), - '$host' => $rhost, - '$request_uri' => $ruri, - '$remote_user' => $ruser, - '$cookie_PHPSESSID' => $scookie, - ); - - /* split hosts entry to servers */ - $this->set_servers(); - - /* call backend initiator based on cache type */ - $init = $this->proxy( 'init' ); - - /* info level */ - $this->log ( __translate__('init starting', $this->plugin_constant )); - $this->$init(); - - } - - /*********************** PUBLIC / PROXY FUNCTIONS ***********************/ - - /** - * build key to make requests with - * - * @param string $prefix prefix to add to prefix - * - */ - public function key ( &$prefix ) { - /* data is string only with content, meta is not used in nginx */ - $key = $prefix . str_replace ( array_keys( $this->urimap ), $this->urimap, $this->options['key'] ); - $this->log ( sprintf( __translate__( 'original key configuration: %s', $this->plugin_constant ), $this->options['key'] ) ); - $this->log ( sprintf( __translate__( 'setting key to: %s', $this->plugin_constant ), $key ) ); - return $key; - } - - - /** - * public get function, transparent proxy to internal function based on backend - * - * @param string $key Cache key to get value for - * - * @return mixed False when entry not found or entry value on success - */ - public function get ( &$key ) { - - /* look for backend aliveness, exit on inactive backend */ - if ( ! $this->is_alive() ) - return false; - - /* log the current action */ - $this->log ( sprintf( __translate__( 'get %s', $this->plugin_constant ), $key ) ); - - /* proxy to internal function */ - $internal = $this->proxy( 'get' ); - $result = $this->$internal( $key ); - - if ( $result === false ) - $this->log ( sprintf( __translate__( 'failed to get entry: %s', $this->plugin_constant ), $key ) ); - - return $result; - } - - /** - * public set function, transparent proxy to internal function based on backend - * - * @param string $key Cache key to set with ( reference only, for speed ) - * @param mixed $data Data to set ( reference only, for speed ) - * - * @return mixed $result status of set function - */ - public function set ( &$key, &$data ) { - - /* look for backend aliveness, exit on inactive backend */ - if ( ! $this->is_alive() ) - return false; - - /* log the current action */ - $this->log ( sprintf( __translate__( 'set %s expiration time: %s', $this->plugin_constant ), $key, $this->options['expire'] ) ); - - /* proxy to internal function */ - $internal = $this->proxy( 'set' ); - $result = $this->$internal( $key, $data ); - - /* check result validity */ - if ( $result === false ) - $this->log ( sprintf( __translate__( 'failed to set entry: %s', $this->plugin_constant ), $key ), LOG_WARNING ); - - return $result; - } - - /** - * public get function, transparent proxy to internal function based on backend - * - * @param string $post_id ID of post to invalidate - * @param boolean $force Force flush cache - * @param boolean $comment Clear a single page based on comment trigger - * - */ - public function clear ( $post_id = false, $force = false ) { - - /* look for backend aliveness, exit on inactive backend */ - if ( ! $this->is_alive() ) - return false; - - /* exit if no post_id is specified */ - if ( empty ( $post_id ) && $force === false ) { - $this->log ( __translate__('not clearing unidentified post ', $this->plugin_constant ), LOG_WARNING ); - return false; - } - - /* if invalidation method is set to full, flush cache */ - if ( ( $this->options['invalidation_method'] === 0 || $force === true ) ) { - /* log action */ - $this->log ( __translate__('flushing cache', $this->plugin_constant ) ); - - /* proxy to internal function */ - $internal = $this->proxy ( 'flush' ); - $result = $this->$internal(); - - if ( $result === false ) - $this->log ( __translate__('failed to flush cache', $this->plugin_constant ), LOG_WARNING ); - - return $result; - } - - /* storage for entries to clear */ - $to_clear = array(); - - /* clear taxonomies if settings requires it */ - if ( $this->options['invalidation_method'] == 2 ) { - /* this will only clear the current blog's entries */ - $this->taxonomy_links( $to_clear ); - } - - /* if there's a post id pushed, it needs to be invalidated in all cases */ - if ( !empty ( $post_id ) ) { - - /* need permalink functions */ - if ( !function_exists('get_permalink') ) - include_once ( ABSPATH . 'wp-includes/link-template.php' ); - - /* get path from permalink */ - $path = substr ( get_permalink( $post_id ) , 7 ); - - /* no path, don't do anything */ - if ( empty( $path ) ) { - $this->log ( sprintf( __translate__( 'unable to determine path from Post Permalink, post ID: %s', $this->plugin_constant ), $post_id ), LOG_WARNING ); - return false; - } - - if ( isset($_SERVER['HTTPS']) && ( ( strtolower($_SERVER['HTTPS']) == 'on' ) || ( $_SERVER['HTTPS'] == '1' ) ) ) - $protocol = 'https://'; - else - $protocol = 'http://'; - - /* elements to clear - values are keys, faster to process and eliminates duplicates - */ - $to_clear[ $protocol . $path ] = true; - } - - foreach ( $to_clear as $link => $dummy ) { - /* clear all feeds as well */ - $to_clear[ $link. 'feed' ] = true; - } - - /* add data & meta prefixes */ - foreach ( $to_clear as $link => $dummy ) { - unset ( $to_clear [ $link ]); - $to_clear[ $this->options[ 'prefix_meta' ] . $link ] = true; - $to_clear[ $this->options[ 'prefix_data' ] . $link ] = true; - } - - /* run clear */ - $internal = $this->proxy ( 'clear' ); - $this->$internal ( $to_clear ); - } - - /** - * clear cache triggered by new comment - * - * @param $comment_id Comment ID - * @param $comment_object The whole comment object ? - */ - public function clear_by_comment ( $comment_id, $comment_object ) { - if ( empty( $comment_id ) ) - return false; - - $comment = get_comment( $comment_id ); - $post_id = $comment->comment_post_ID; - if ( !empty( $post_id ) ) - $this->clear ( $post_id ); - - unset ( $comment ); - unset ( $post_id ); - } - - /** - * to collect all permalinks of all taxonomy terms used in invalidation & precache - * - * @param array &$links Passed by reference array that has to be filled up with the links - * @param mixed $site Site ID or false; used in WordPress Network - * - */ - public function taxonomy_links ( &$links, $site = false ) { - - if ( $site !== false ) { - $current_blog = get_current_blog_id(); - switch_to_blog( $site ); - - $url = get_blog_option ( $site, 'siteurl' ); - if ( substr( $url, -1) !== '/' ) - $url = $url . '/'; - - $links[ $url ] = true; - } - - /* we're only interested in public taxonomies */ - $args = array( - 'public' => true, - ); - - /* get taxonomies as objects */ - $taxonomies = get_taxonomies( $args, 'objects' ); - - if ( !empty( $taxonomies ) ) { - foreach ( $taxonomies as $taxonomy ) { - /* reset array, just in case */ - $terms = array(); - - /* get all the terms for this taxonomy, only if not empty */ - $sargs = array( - 'hide_empty' => true, - 'fields' => 'all', - 'hierarchical' =>false, - ); - $terms = get_terms ( $taxonomy->name , $sargs ); - - if ( !empty ( $terms ) ) { - foreach ( $terms as $term ) { - /* get the permalink for the term */ - $link = get_term_link ( $term->slug, $taxonomy->name ); - /* add to container */ - $links[ $link ] = true; - /* remove the taxonomy name from the link, lots of plugins remove this for SEO, it's better to include them than leave them out - in worst case, we cache some 404 as well - */ - $link = str_replace ( '/'.$taxonomy->rewrite['slug'], '', $link ); - /* add to container */ - $links[ $link ] = true; - } - } - } - } - - /* switch back to original site if we navigated away */ - if ( $site !== false ) { - switch_to_blog( $current_blog ); - } - - } - - /** - * get backend aliveness - * - * @return array Array of configured servers with aliveness value - * - */ - public function status () { - - /* look for backend aliveness, exit on inactive backend */ - if ( ! $this->is_alive() ) - return false; - - $internal = $this->proxy ( 'status' ); - $this->$internal(); - return $this->status; - } - - /** - * backend proxy function name generator - * - * @return string Name of internal function based on cache_type - * - */ - private function proxy ( $method ) { - return $this->options['cache_type'] . '_' . $method; - } - - /** - * function to check backend aliveness - * - * @return boolean true if backend is alive, false if not - * - */ - private function is_alive() { - if ( ! $this->alive ) { - $this->log ( __translate__("backend is not active, exiting function ", $this->plugin_constant ) . __FUNCTION__, LOG_WARNING ); - return false; - } - - return true; - } - - /** - * split hosts string to backend servers - * - * - */ - private function set_servers () { - /* replace servers array in config according to hosts field */ - $servers = explode( self::host_separator , $this->options['hosts']); - - $options['servers'] = array(); - - foreach ( $servers as $snum => $sstring ) { - - $separator = strpos( $sstring , self::port_separator ); - $host = substr( $sstring, 0, $separator ); - $port = substr( $sstring, $separator + 1 ); - // unix socket failsafe - if ( empty ($port) ) $port = 0; - - $this->options['servers'][$sstring] = array ( - 'host' => $host, - 'port' => $port - ); - } - - } - - /** - * get current array of servers - * - * @return array Server list in current config - * - */ - public function get_servers () { - $r = isset ( $this->options['servers'] ) ? $this->options['servers'] : ''; - return $r; - } - - /** - * log wrapper to include options - * - * @var mixed $message Message to log - * @var int $log_level Log level - */ - private function log ( $message, $log_level = LOG_WARNING ) { - if ( !isset ( $this->options['log'] ) || $this->options['log'] != 1 ) - return false; + private function memcache_status () { + /* server status will be calculated by getting server stats */ + $this->log ( __translate__("checking server statuses", $this->plugin_constant )); + /* get servers statistic from connection */ + foreach ( $this->options['servers'] as $server_id => $server ) { + if ( $server['port'] === 0 ) + $this->status[$server_id] = $this->connection->getServerStatus( $server['host'] ); else - $this->utilities->log ( $this->plugin_constant , $message, $log_level ); - } - - /*********************** END PUBLIC FUNCTIONS ***********************/ - /*********************** APC FUNCTIONS ***********************/ - /** - * init apc backend: test APC availability and set alive status - */ - private function apc_init () { - /* verify apc functions exist, apc extension is loaded */ - if ( ! function_exists( 'apc_cache_info' ) ) { - $this->log ( __translate__('APC extension missing', $this->plugin_constant ) ); - return false; - } - - /* verify apc is working */ - if ( apc_cache_info("user",true) ) { - $this->log ( __translate__('backend OK', $this->plugin_constant ) ); - $this->alive = true; - } - } - - /** - * health checker for APC - * - * @return boolean Aliveness status - * - */ - private function apc_status () { - $this->status = true; - return $this->alive; - } - - /** - * get function for APC backend - * - * @param string $key Key to get values for - * - * @return mixed Fetched data based on key - * - */ - private function apc_get ( &$key ) { - return apc_fetch( $key ); - } - - /** - * Set function for APC backend - * - * @param string $key Key to set with - * @param mixed $data Data to set - * - * @return boolean APC store outcome - */ - private function apc_set ( &$key, &$data ) { - return apc_store( $key , $data , $this->options['expire'] ); - } - - - /** - * Flushes APC user entry storage - * - * @return boolean APC flush outcome status - * - */ - private function apc_flush ( ) { - return apc_clear_cache('user'); - } - - /** - * Removes entry from APC or flushes APC user entry storage - * - * @param mixed $keys Keys to clear, string or array - */ - private function apc_clear ( &$keys ) { - /* make an array if only one string is present, easier processing */ - if ( !is_array ( $keys ) ) - $keys = array ( $keys => true ); - - foreach ( $keys as $key => $dummy ) { - if ( ! apc_delete ( $key ) ) { - $this->log ( sprintf( __translate__( 'Failed to delete APC entry: %s', $this->plugin_constant ), $key ), LOG_ERR ); - //throw new Exception ( __translate__('Deleting APC entry failed with key ', $this->plugin_constant ) . $key ); - } - else { - $this->log ( sprintf( __translate__( 'APC entry delete: %s', $this->plugin_constant ), $key ) ); - } - } - } - - /*********************** END APC FUNCTIONS ***********************/ - /*********************** APCu FUNCTIONS ***********************/ - /** - * init apcu backend: test APCu availability and set alive status - */ - private function apcu_init () { - /* verify apcu functions exist, apcu extension is loaded */ - if ( ! function_exists( 'apcu_cache_info' ) ) { - $this->log ( __translate__('APCu extension missing', $this->plugin_constant ) ); - return false; - } - - /* verify apcu is working */ - if ( apcu_cache_info("user",true) ) { - $this->log ( __translate__('backend OK', $this->plugin_constant ) ); - $this->alive = true; - } - } - - /** - * health checker for APC - * - * @return boolean Aliveness status - * - */ - private function apcu_status () { - $this->status = true; - return $this->alive; - } - - /** - * get function for APC backend - * - * @param string $key Key to get values for - * - * @return mixed Fetched data based on key - * - */ - private function apcu_get ( &$key ) { - return apcu_fetch( $key ); - } - - /** - * Set function for APC backend - * - * @param string $key Key to set with - * @param mixed $data Data to set - * - * @return boolean APC store outcome - */ - private function apcu_set ( &$key, &$data ) { - return apcu_store( $key , $data , $this->options['expire'] ); - } - - - /** - * Flushes APC user entry storage - * - * @return boolean APC flush outcome status - * - */ - private function apcu_flush ( ) { - return apcu_clear_cache(); - } - - /** - * Removes entry from APC or flushes APC user entry storage - * - * @param mixed $keys Keys to clear, string or array - */ - private function apcu_clear ( &$keys ) { - /* make an array if only one string is present, easier processing */ - if ( !is_array ( $keys ) ) - $keys = array ( $keys => true ); - - foreach ( $keys as $key => $dummy ) { - if ( ! apcu_delete ( $key ) ) { - $this->log ( sprintf( __translate__( 'Failed to delete APC entry: %s', $this->plugin_constant ), $key ), LOG_ERR ); - //throw new Exception ( __translate__('Deleting APC entry failed with key ', $this->plugin_constant ) . $key ); - } - else { - $this->log ( sprintf( __translate__( 'APC entry delete: %s', $this->plugin_constant ), $key ) ); - } - } - } - - /*********************** END APC FUNCTIONS ***********************/ - - /*********************** XCACHE FUNCTIONS ***********************/ - /** - * init apc backend: test APC availability and set alive status - */ - private function xcache_init () { - /* verify apc functions exist, apc extension is loaded */ - if ( ! function_exists( 'xcache_info' ) ) { - $this->log ( __translate__('XCACHE extension missing', $this->plugin_constant ) ); - return false; - } - - /* verify apc is working */ - $info = xcache_info(); - if ( !empty( $info )) { - $this->log ( __translate__('backend OK', $this->plugin_constant ) ); - $this->alive = true; - } - } - - /** - * health checker for Xcache - * - * @return boolean Aliveness status - * - */ - private function xcache_status () { - $this->status = true; - return $this->alive; - } - - /** - * get function for APC backend - * - * @param string $key Key to get values for - * - * @return mixed Fetched data based on key - * - */ - private function xcache_get ( &$key ) { - if ( xcache_isset ( $key ) ) - return xcache_get( $key ); - else - return false; - } - - /** - * Set function for APC backend - * - * @param string $key Key to set with - * @param mixed $data Data to set - * - * @return boolean APC store outcome - */ - private function xcache_set ( &$key, &$data ) { - return xcache_set( $key , $data , $this->options['expire'] ); - } - - - /** - * Flushes APC user entry storage - * - * @return boolean APC flush outcome status - * - */ - private function xcache_flush ( ) { - return xcache_clear_cache(XC_TYPE_VAR); - } - - /** - * Removes entry from APC or flushes APC user entry storage - * - * @param mixed $keys Keys to clear, string or array - */ - private function xcache_clear ( &$keys ) { - /* make an array if only one string is present, easier processing */ - if ( !is_array ( $keys ) ) - $keys = array ( $keys => true ); - - foreach ( $keys as $key => $dummy ) { - if ( ! xcache_unset ( $key ) ) { - $this->log ( sprintf( __translate__( 'Failed to delete XCACHE entry: %s', $this->plugin_constant ), $key ), LOG_ERR ); - //throw new Exception ( __translate__('Deleting APC entry failed with key ', $this->plugin_constant ) . $key ); - } - else { - $this->log ( sprintf( __translate__( 'XCACHE entry delete: %s', $this->plugin_constant ), $key ) ); - } - } - } - - /*********************** END XCACHE FUNCTIONS ***********************/ - - - /*********************** MEMCACHED FUNCTIONS ***********************/ - /** - * init memcached backend - */ - private function memcached_init () { - /* Memcached class does not exist, Memcached extension is not available */ - if (!class_exists('Memcached')) { - $this->log ( __translate__(' Memcached extension missing', $this->plugin_constant ), LOG_ERR ); - return false; - } - - /* check for existing server list, otherwise we cannot add backends */ - if ( empty ( $this->options['servers'] ) && ! $this->alive ) { - $this->log ( __translate__("Memcached servers list is empty, init failed", $this->plugin_constant ), LOG_WARNING ); - return false; - } - - /* check is there's no backend connection yet */ - if ( $this->connection === NULL ) { - /* persistent backend needs an identifier */ - if ( $this->options['persistent'] == '1' ) - $this->connection = new Memcached( $this->plugin_constant ); - else - $this->connection = new Memcached(); - - /* use binary and not compressed format, good for nginx and still fast */ - $this->connection->setOption( Memcached::OPT_COMPRESSION , false ); - $this->connection->setOption( Memcached::OPT_BINARY_PROTOCOL , true ); - - if ( version_compare( phpversion( 'memcached' ) , '2.0.0', '>=' ) && ini_get( 'memcached.use_sasl' ) == 1 ) { - $this->connection->setSaslAuthData ( $this->options['authuser'], $this->options['authpass']); - } - } - - /* check if initialization was success or not */ - if ( $this->connection === NULL ) { - $this->log ( __translate__( 'error initializing Memcached PHP extension, exiting', $this->plugin_constant ) ); - return false; - } - - /* check if we already have list of servers, only add server(s) if it's not already connected */ - $servers_alive = array(); - if ( !empty ( $this->status ) ) { - $servers_alive = $this->connection->getServerList(); - /* create check array if backend servers are already connected */ - if ( !empty ( $servers ) ) { - foreach ( $servers_alive as $skey => $server ) { - $skey = $server['host'] . ":" . $server['port']; - $servers_alive[ $skey ] = true; - } - } - } - - /* adding servers */ - foreach ( $this->options['servers'] as $server_id => $server ) { - /* reset server status to unknown */ - $this->status[$server_id] = -1; - - /* only add servers that does not exists already in connection pool */ - if ( !@array_key_exists($server_id , $servers_alive ) ) { - $this->connection->addServer( $server['host'], $server['port'] ); - $this->log ( sprintf( __translate__( '%s added, persistent mode: %s', $this->plugin_constant ), $server_id, $this->options['persistent'] ) ); - } - } - - /* backend is now alive */ - $this->alive = true; - $this->memcached_status(); - } - - /** - * sets current backend alive status for Memcached servers - * - */ - private function memcached_status () { - /* server status will be calculated by getting server stats */ - $this->log ( __translate__("checking server statuses", $this->plugin_constant )); - /* get servers statistic from connection */ - $report = $this->connection->getStats(); - - foreach ( $report as $server_id => $details ) { - /* reset server status to offline */ - $this->status[$server_id] = 0; - /* if server uptime is not empty, it's most probably up & running */ - if ( !empty($details['uptime']) ) { - $this->log ( sprintf( __translate__( '%s server is up & running', $this->plugin_constant ), $server_id ) ); - $this->status[$server_id] = 1; - } - } - - } - - /** - * get function for Memcached backend - * - * @param string $key Key to get values for - * - */ - private function memcached_get ( &$key ) { - return $this->connection->get($key); - } - - /** - * Set function for Memcached backend - * - * @param string $key Key to set with - * @param mixed $data Data to set - * - */ - private function memcached_set ( &$key, &$data ) { - $result = $this->connection->set ( $key, $data , $this->options['expire'] ); - - /* if storing failed, log the error code */ - if ( $result === false ) { - $code = $this->connection->getResultCode(); - $this->log ( sprintf( __translate__( 'unable to set entry: %s', $this->plugin_constant ), $key ) ); - $this->log ( sprintf( __translate__( 'Memcached error code: %s', $this->plugin_constant ), $code ) ); - //throw new Exception ( __translate__('Unable to store Memcached entry ', $this->plugin_constant ) . $key . __translate__( ', error code: ', $this->plugin_constant ) . $code ); - } - - return $result; - } - - /** - * - * Flush memcached entries - */ - private function memcached_flush ( ) { - return $this->connection->flush(); - } - - - /** - * Removes entry from Memcached or flushes Memcached storage - * - * @param mixed $keys String / array of string of keys to delete entries with - */ - private function memcached_clear ( &$keys ) { - - /* make an array if only one string is present, easier processing */ - if ( !is_array ( $keys ) ) - $keys = array ( $keys => true ); - - foreach ( $keys as $key => $dummy ) { - $kresult = $this->connection->delete( $key ); - - if ( $kresult === false ) { - $code = $this->connection->getResultCode(); - $this->log ( sprintf( __translate__( 'unable to delete entry: %s', $this->plugin_constant ), $key ) ); - $this->log ( sprintf( __translate__( 'Memcached error code: %s', $this->plugin_constant ), $code ) ); - } - else { - $this->log ( sprintf( __translate__( 'entry deleted: %s', $this->plugin_constant ), $key ) ); - } - } - } - /*********************** END MEMCACHED FUNCTIONS ***********************/ - - /*********************** MEMCACHE FUNCTIONS ***********************/ - /** - * init memcache backend - */ - private function memcache_init () { - /* Memcached class does not exist, Memcache extension is not available */ - if (!class_exists('Memcache')) { - $this->log ( __translate__('PHP Memcache extension missing', $this->plugin_constant ), LOG_ERR ); - return false; - } - - /* check for existing server list, otherwise we cannot add backends */ - if ( empty ( $this->options['servers'] ) && ! $this->alive ) { - $this->log ( __translate__("servers list is empty, init failed", $this->plugin_constant ), LOG_WARNING ); - return false; - } - - /* check is there's no backend connection yet */ - if ( $this->connection === NULL ) - $this->connection = new Memcache(); - - /* check if initialization was success or not */ - if ( $this->connection === NULL ) { - $this->log ( __translate__( 'error initializing Memcache PHP extension, exiting', $this->plugin_constant ) ); - return false; - } - - /* adding servers */ - foreach ( $this->options['servers'] as $server_id => $server ) { - if ( $this->options['persistent'] == '1' ) - $conn = 'pconnect'; - else - $conn = 'connect'; - - /* in case of unix socket */ - if ( $server['port'] === 0 ) - $this->status[$server_id] = $this->connection->$conn ( 'unix:/' . $server['host'] ); - else - $this->status[$server_id] = $this->connection->$conn ( $server['host'] , $server['port'] ); - - $this->log ( sprintf( __translate__( '%s added, persistent mode: %s', $this->plugin_constant ), $server_id, $this->options['persistent'] ) ); - } - - /* backend is now alive */ - $this->alive = true; - $this->memcache_status(); - } - - /** - * check current backend alive status for Memcached - * - */ - private function memcache_status () { - /* server status will be calculated by getting server stats */ - $this->log ( __translate__("checking server statuses", $this->plugin_constant )); - /* get servers statistic from connection */ - foreach ( $this->options['servers'] as $server_id => $server ) { $this->status[$server_id] = $this->connection->getServerStatus( $server['host'], $server['port'] ); - if ( $this->status[$server_id] == 0 ) - $this->log ( sprintf( __translate__( '%s server is down', $this->plugin_constant ), $server_id ) ); - else - $this->log ( sprintf( __translate__( '%s server is up & running', $this->plugin_constant ), $server_id ) ); - } + if ( $this->status[$server_id] == 0 ) + $this->log ( sprintf( __translate__( '%s server is down', $this->plugin_constant ), $server_id ) ); + else + $this->log ( sprintf( __translate__( '%s server is up & running', $this->plugin_constant ), $server_id ) ); } - - /** - * get function for Memcached backend - * - * @param string $key Key to get values for - * - */ - private function memcache_get ( &$key ) { - return $this->connection->get($key); - } - - /** - * Set function for Memcached backend - * - * @param string $key Key to set with - * @param mixed $data Data to set - * - */ - private function memcache_set ( &$key, &$data ) { - $result = $this->connection->set ( $key, $data , 0 , $this->options['expire'] ); - return $result; - } - - /** - * - * Flush memcached entries - */ - private function memcache_flush ( ) { - return $this->connection->flush(); - } - - - /** - * Removes entry from Memcached or flushes Memcached storage - * - * @param mixed $keys String / array of string of keys to delete entries with - */ - private function memcache_clear ( &$keys ) { - /* make an array if only one string is present, easier processing */ - if ( !is_array ( $keys ) ) - $keys = array ( $keys => true ); - - foreach ( $keys as $key => $dummy ) { - $kresult = $this->connection->delete( $key ); - - if ( $kresult === false ) { - $this->log ( sprintf( __translate__( 'unable to delete entry: %s', $this->plugin_constant ), $key ) ); - } - else { - $this->log ( sprintf( __translate__( 'entry deleted: %s', $this->plugin_constant ), $key ) ); - } - } - } - - /*********************** END MEMCACHE FUNCTIONS ***********************/ - } + /** + * get function for Memcached backend + * + * @param string $key Key to get values for + * + */ + private function memcache_get ( &$key ) { + return $this->connection->get($key); + } + + /** + * Set function for Memcached backend + * + * @param string $key Key to set with + * @param mixed $data Data to set + * + */ + private function memcache_set ( &$key, &$data ) { + $result = $this->connection->set ( $key, $data , 0 , $this->options['expire'] ); + return $result; + } + + /** + * + * Flush memcached entries + */ + private function memcache_flush ( ) { + return $this->connection->flush(); + } + + + /** + * Removes entry from Memcached or flushes Memcached storage + * + * @param mixed $keys String / array of string of keys to delete entries with + */ + private function memcache_clear ( &$keys ) { + /* make an array if only one string is present, easier processing */ + if ( !is_array ( $keys ) ) + $keys = array ( $keys => true ); + + foreach ( $keys as $key => $dummy ) { + $kresult = $this->connection->delete( $key ); + + if ( $kresult === false ) { + $this->log ( sprintf( __translate__( 'unable to delete entry: %s', $this->plugin_constant ), $key ) ); + } + else { + $this->log ( sprintf( __translate__( 'entry deleted: %s', $this->plugin_constant ), $key ) ); + } + } + } + + /*********************** END MEMCACHE FUNCTIONS ***********************/ + } -?> +endif; ?> diff --git a/wp-ffpc-class.php b/wp-ffpc-class.php index 7d71713..3961d77 100644 --- a/wp-ffpc-class.php +++ b/wp-ffpc-class.php @@ -1,1202 +1,1183 @@ acache_worker = $this->plugin_dir . $this->plugin_constant . '-acache.php'; + /* WordPress advanced-cache.php file location */ + $this->acache = WP_CONTENT_DIR . '/advanced-cache.php'; + /* nginx sample config file */ + $this->nginx_sample = $this->plugin_dir . $this->plugin_constant . '-nginx-sample.conf'; + /* backend driver file */ + $this->acache_backend = $this->plugin_dir . $this->plugin_constant . '-backend.php'; + /* flush button identifier */ + $this->button_flush = $this->plugin_constant . '-flush'; + /* precache button identifier */ + $this->button_precache = $this->plugin_constant . '-precache'; + /* global options identifier */ + $this->global_option = $this->plugin_constant . '-global'; + /* precache log */ + $this->precache_logfile = sys_get_temp_dir() . '/' . self::precache_log; + /* this is the precacher php worker file */ + $this->precache_phpfile = sys_get_temp_dir() . '/' . self::precache_php; + /* search for a system function */ + $this->shell_possibilities = array ( 'shell_exec', 'exec', 'system', 'passthru' ); + /* get disabled functions list */ + $disabled_functions = array_map('trim', explode(',', ini_get('disable_functions') ) ); - /** - * init hook function runs before admin panel hook, themeing and options read - */ - public function plugin_init() { - /* advanced cache "worker" file */ - $this->acache_worker = $this->plugin_dir . $this->plugin_constant . '-acache.php'; - /* WordPress advanced-cache.php file location */ - $this->acache = WP_CONTENT_DIR . '/advanced-cache.php'; - /* nginx sample config file */ - $this->nginx_sample = $this->plugin_dir . $this->plugin_constant . '-nginx-sample.conf'; - /* backend driver file */ - $this->acache_backend = $this->plugin_dir . $this->plugin_constant . '-backend.php'; - /* flush button identifier */ - $this->button_flush = $this->plugin_constant . '-flush'; - /* precache button identifier */ - $this->button_precache = $this->plugin_constant . '-precache'; - /* global options identifier */ - $this->global_option = $this->plugin_constant . '-global'; - /* precache log */ - $this->precache_logfile = sys_get_temp_dir() . '/' . self::precache_log; - /* this is the precacher php worker file */ - $this->precache_phpfile = sys_get_temp_dir() . '/' . self::precache_php; - /* search for a system function */ - $this->shell_possibilities = array ( 'shell_exec', 'exec', 'system', 'passthru' ); - /* get disabled functions list */ - $disabled_functions = array_map('trim', explode(',', ini_get('disable_functions') ) ); - - foreach ( $this->shell_possibilities as $possible ) { - if ( function_exists ($possible) && ! ( ini_get('safe_mode') || in_array( $possible, $disabled_functions ) ) ) { - /* set shell function */ - $this->shell_function = $possible; - break; - } - } - - /* set global config key; here, because it's needed for migration */ - if ( $this->network ) - $this->global_config_key = 'network'; - else - $this->global_config_key = $_SERVER['HTTP_HOST']; - - /* cache type possible values array */ - $this->select_cache_type = array ( - 'apc' => __( 'APC' , $this->plugin_constant ), - 'apcu' => __( 'APCu' , $this->plugin_constant ), - 'xcache' => __( 'XCache' , $this->plugin_constant ), - 'memcache' => __( 'PHP Memcache' , $this->plugin_constant ), - 'memcached' => __( 'PHP Memcached' , $this->plugin_constant ), - ); - /* check for required functions / classes for the cache types */ - $this->valid_cache_type = array ( - 'apc' => function_exists( 'apc_cache_info' ) ? true : false, - 'apcu' => function_exists( 'apcu_cache_info' ) ? true : false, - 'xcache' => function_exists( 'xcache_info' ) ? true : false, - 'memcache' => class_exists ( 'Memcache') ? true : false, - 'memcached' => class_exists ( 'Memcached') ? true : false, - ); - - /* invalidation method possible values array */ - $this->select_invalidation_method = array ( - 0 => __( 'flush cache' , $this->plugin_constant ), - 1 => __( 'only modified post' , $this->plugin_constant ), - 2 => __( 'modified post and all taxonomies' , $this->plugin_constant ), - ); - - /* map of possible key masks */ - $this->list_uri_vars = array ( - '$scheme' => __('The HTTP scheme (i.e. http, https).', $this->plugin_constant ), - '$host' => __('Host in the header of request or name of the server processing the request if the Host header is not available.', $this->plugin_constant ), - '$request_uri' => __('The *original* request URI as received from the client including the args', $this->plugin_constant ), - '$remote_user' => __('Name of user, authenticated by the Auth Basic Module', $this->plugin_constant ), - '$cookie_PHPSESSID' => __('PHP Session Cookie ID, if set ( empty if not )', $this->plugin_constant ), - //'$cookie_COOKnginy IE' => __('Value of COOKIE', $this->plugin_constant ), - //'$http_HEADER' => __('Value of HTTP request header HEADER ( lowercase, dashes converted to underscore )', $this->plugin_constant ), - //'$query_string' => __('Full request URI after rewrites', $this->plugin_constant ), - //'' => __('', $this->plugin_constant ), - ); - - /* get current wp_cron schedules */ - $wp_schedules = wp_get_schedules(); - /* add 'null' to switch off timed precache */ - $schedules['null'] = __( 'do not use timed precache' ); - foreach ( $wp_schedules as $interval=>$details ) { - $schedules[ $interval ] = $details['display']; - } - $this->select_schedules = $schedules; - - } - - /** - * additional init, steps that needs the plugin options - * - */ - public function plugin_setup () { - - /* initiate backend */ - $this->backend = new WP_FFPC_Backend ( $this->options, $this->network ); - - /* get all available post types */ - $post_types = get_post_types( ); - - /* cache invalidation hooks */ - foreach ( $post_types as $post_type ) { - add_action( 'new_to_publish_' .$post_type , array( &$this->backend , 'clear' ), 0 ); - add_action( 'draft_to_publish' .$post_type , array( &$this->backend , 'clear' ), 0 ); - add_action( 'pending_to_publish' .$post_type , array( &$this->backend , 'clear' ), 0 ); - add_action( 'private_to_publish' .$post_type , array( &$this->backend , 'clear' ), 0 ); - add_action( 'publish_' . $post_type , array( &$this->backend , 'clear' ), 0 ); - } - - /* comments invalidation hooks */ - if ( $this->options['comments_invalidate'] ) { - add_action( 'comment_post', array( &$this->backend , 'clear' ), 0 ); - add_action( 'edit_comment', array( &$this->backend , 'clear' ), 0 ); - add_action( 'trashed_comment', array( &$this->backend , 'clear' ), 0 ); - add_action( 'pingback_post', array( &$this->backend , 'clear' ), 0 ); - add_action( 'trackback_post', array( &$this->backend , 'clear' ), 0 ); - add_action( 'wp_insert_comment', array( &$this->backend , 'clear' ), 0 ); - add_action( '', array( &$this->backend , 'clear' ), 0 ); - } - - /* invalidation on some other ocasions as well */ - add_action( 'switch_theme', array( &$this->backend , 'clear' ), 0 ); - add_action( 'deleted_post', array( &$this->backend , 'clear' ), 0 ); - add_action( 'edit_post', array( &$this->backend , 'clear' ), 0 ); - - /* add filter for catching canonical redirects */ - if ( WP_CACHE ) - add_filter('redirect_canonical', 'wp_ffpc_redirect_callback', 10, 2); - - /* clean up schedule if needed */ - if ( !isset( $this->options['precache_schedule'] ) || $this->options['precache_schedule'] == 'null' ) { - $this->log ( sprintf ( __( 'clearing scheduled hook %s', $this->plugin_constant ), self::precache_id ) ); - } - - /* add precache coldrun action */ - add_action( self::precache_id , array( &$this, 'precache_coldrun' ) ); - - $settings_link = ' » ' . __( 'WP-FFPC Settings', $this->plugin_constant ) . ''; - /* check for errors */ - if ( ! WP_CACHE ) - $this->errors['no_wp_cache'] = __("WP_CACHE is disabled, plugin will not work that way. Please add define `( 'WP_CACHE', true );` in wp-config.php", $this->plugin_constant ) . $settings_link; - - if ( ! $this->global_saved ) - $this->errors['no_global_saved'] = __("Plugin settings are not yet saved for the site, please save settings!", $this->plugin_constant) . $settings_link; - - if ( ! file_exists ( $this->acache ) ) - $this->errors['no_acache_saved'] = __("Advanced cache file is yet to be generated, please save settings!", $this->plugin_constant). $settings_link; - - if ( file_exists ( $this->acache ) && ! is_writable ( $this->acache ) ) - $this->errors['no_acache_write'] = __("Advanced cache file is not writeable!
Please change the permissions on the file: ", $this->plugin_constant) . $this->acache; - - foreach ( $this->valid_cache_type as $backend => $status ) { - if ( $this->options['cache_type'] == $backend && ! $status ) { - $this->errors['no_backend'] = sprintf ( __('%s cache backend activated but no PHP %s extension was found.
Please either use different backend or activate the module!', $this->plugin_constant), $backend, $backend ); - } - } - - /* get the current runtime configuration for memcache in PHP because Memcache in binary mode is really problematic */ - if ( extension_loaded ( 'memcache' ) ) { - $memcache_settings = ini_get_all( 'memcache' ); - if ( !empty ( $memcache_settings ) && $this->options['cache_type'] == 'memcache' ) - { - $memcache_protocol = strtolower($memcache_settings['memcache.protocol']['local_value']); - if ( $memcache_protocol == 'binary' ) { - $this->errors['binary_memcache'] = __('WARNING: Memcache extension is configured to use binary mode. This is very buggy and the plugin will most probably not work correctly.
Please consider to change either to ASCII mode or to Memcached extension.', $this->plugin_constant ); - } - } - } - - /* display errors globally */ - if ( $this->network ) - add_action( 'network_admin_notices', array( &$this, 'display_errors') ); - else - add_action( 'admin_notices', array( &$this, 'display_errors') ); - } - - /** - * activation hook function, to be extended - */ - public function plugin_activate() { - /* we leave this empty to avoid not detecting WP network correctly */ - } - - /** - * deactivation hook function, to be extended - */ - public function plugin_deactivate () { - /* remove current site config from global config */ - $this->update_global_config( true ); - } - - /** - * uninstall hook function, to be extended - */ - public function plugin_uninstall( $delete_options = true ) { - /* delete advanced-cache.php file */ - unlink ( $this->acache ); - - /* delete site settings */ - if ( $delete_options ) { - $this->plugin_options_delete (); + foreach ( $this->shell_possibilities as $possible ) { + if ( function_exists ($possible) && ! ( ini_get('safe_mode') || in_array( $possible, $disabled_functions ) ) ) { + /* set shell function */ + $this->shell_function = $possible; + break; } } - /** - * extending admin init - * - */ - public function plugin_hook_admin_init () { - /* save parameter updates, if there are any */ - if ( isset( $_POST[ $this->button_flush ] ) ) { + /* set global config key; here, because it's needed for migration */ + if ( $this->network ) + $this->global_config_key = 'network'; + else + $this->global_config_key = $_SERVER['HTTP_HOST']; - /* remove precache log entry */ - $this->_delete_option( self::precache_log ); - /* remove precache timestamp entry */ - $this->_delete_option( self::precache_timestamp ); + /* cache type possible values array */ + $this->select_cache_type = array ( + 'apc' => __( 'APC' , $this->plugin_constant ), + 'apcu' => __( 'APCu' , $this->plugin_constant ), + 'xcache' => __( 'XCache' , $this->plugin_constant ), + 'memcache' => __( 'PHP Memcache' , $this->plugin_constant ), + 'memcached' => __( 'PHP Memcached' , $this->plugin_constant ), + ); + /* check for required functions / classes for the cache types */ + $this->valid_cache_type = array ( + 'apc' => function_exists( 'apc_cache_info' ) ? true : false, + 'apcu' => function_exists( 'apcu_cache_info' ) ? true : false, + 'xcache' => function_exists( 'xcache_info' ) ? true : false, + 'memcache' => class_exists ( 'Memcache') ? true : false, + 'memcached' => class_exists ( 'Memcached') ? true : false, + ); - /* remove precache logfile */ - if ( @file_exists ( $this->precache_logfile ) ) { - unlink ( $this->precache_logfile ); - } + /* invalidation method possible values array */ + $this->select_invalidation_method = array ( + 0 => __( 'flush cache' , $this->plugin_constant ), + 1 => __( 'only modified post' , $this->plugin_constant ), + 2 => __( 'modified post and all taxonomies' , $this->plugin_constant ), + ); - /* remove precache PHP worker */ - if ( @file_exists ( $this->precache_phpfile ) ) { - unlink ( $this->precache_phpfile ); - } + /* map of possible key masks */ + $this->list_uri_vars = array ( + '$scheme' => __('The HTTP scheme (i.e. http, https).', $this->plugin_constant ), + '$host' => __('Host in the header of request or name of the server processing the request if the Host header is not available.', $this->plugin_constant ), + '$request_uri' => __('The *original* request URI as received from the client including the args', $this->plugin_constant ), + '$remote_user' => __('Name of user, authenticated by the Auth Basic Module', $this->plugin_constant ), + '$cookie_PHPSESSID' => __('PHP Session Cookie ID, if set ( empty if not )', $this->plugin_constant ), + //'$cookie_COOKnginy IE' => __('Value of COOKIE', $this->plugin_constant ), + //'$http_HEADER' => __('Value of HTTP request header HEADER ( lowercase, dashes converted to underscore )', $this->plugin_constant ), + //'$query_string' => __('Full request URI after rewrites', $this->plugin_constant ), + //'' => __('', $this->plugin_constant ), + ); - /* flush backend */ - $this->backend->clear( false, true ); - $this->status = 3; - header( "Location: ". $this->settings_link . self::slug_flush ); + /* get current wp_cron schedules */ + $wp_schedules = wp_get_schedules(); + /* add 'null' to switch off timed precache */ + $schedules['null'] = __( 'do not use timed precache' ); + foreach ( $wp_schedules as $interval=>$details ) { + $schedules[ $interval ] = $details['display']; + } + $this->select_schedules = $schedules; + + } + + /** + * additional init, steps that needs the plugin options + * + */ + public function plugin_post_init () { + + /* initiate backend */ + $this->backend = new WP_FFPC_Backend ( $this->options, $this->network ); + + /* get all available post types */ + $post_types = get_post_types( ); + + /* cache invalidation hooks */ + foreach ( $post_types as $post_type ) { + add_action( 'new_to_publish_' .$post_type , array( &$this->backend , 'clear' ), 0 ); + add_action( 'draft_to_publish' .$post_type , array( &$this->backend , 'clear' ), 0 ); + add_action( 'pending_to_publish' .$post_type , array( &$this->backend , 'clear' ), 0 ); + add_action( 'private_to_publish' .$post_type , array( &$this->backend , 'clear' ), 0 ); + add_action( 'publish_' . $post_type , array( &$this->backend , 'clear' ), 0 ); + } + + /* comments invalidation hooks */ + if ( $this->options['comments_invalidate'] ) { + add_action( 'comment_post', array( &$this->backend , 'clear' ), 0 ); + add_action( 'edit_comment', array( &$this->backend , 'clear' ), 0 ); + add_action( 'trashed_comment', array( &$this->backend , 'clear' ), 0 ); + add_action( 'pingback_post', array( &$this->backend , 'clear' ), 0 ); + add_action( 'trackback_post', array( &$this->backend , 'clear' ), 0 ); + add_action( 'wp_insert_comment', array( &$this->backend , 'clear' ), 0 ); + add_action( '', array( &$this->backend , 'clear' ), 0 ); + } + + /* invalidation on some other ocasions as well */ + add_action( 'switch_theme', array( &$this->backend , 'clear' ), 0 ); + add_action( 'deleted_post', array( &$this->backend , 'clear' ), 0 ); + add_action( 'edit_post', array( &$this->backend , 'clear' ), 0 ); + + /* add filter for catching canonical redirects */ + if ( WP_CACHE ) + add_filter('redirect_canonical', 'wp_ffpc_redirect_callback', 10, 2); + + /* clean up schedule if needed */ + if ( !isset( $this->options['precache_schedule'] ) || $this->options['precache_schedule'] == 'null' ) { + $this->log ( sprintf ( __( 'clearing scheduled hook %s', $this->plugin_constant ), self::precache_id ) ); + } + + /* add precache coldrun action */ + add_action( self::precache_id , array( &$this, 'precache_coldrun' ) ); + + $settings_link = ' » ' . __( 'WP-FFPC Settings', $this->plugin_constant ) . ''; + /* check for errors */ + if ( ! WP_CACHE ) + $this->errors['no_wp_cache'] = __("WP_CACHE is disabled, plugin will not work that way. Please add define `( 'WP_CACHE', true );` in wp-config.php", $this->plugin_constant ) . $settings_link; + + if ( ! $this->global_saved ) + $this->errors['no_global_saved'] = __("Plugin settings are not yet saved for the site, please save settings!", $this->plugin_constant) . $settings_link; + + if ( ! file_exists ( $this->acache ) ) + $this->errors['no_acache_saved'] = __("Advanced cache file is yet to be generated, please save settings!", $this->plugin_constant). $settings_link; + + if ( file_exists ( $this->acache ) && ! is_writable ( $this->acache ) ) + $this->errors['no_acache_write'] = __("Advanced cache file is not writeable!
Please change the permissions on the file: ", $this->plugin_constant) . $this->acache; + + foreach ( $this->valid_cache_type as $backend => $status ) { + if ( $this->options['cache_type'] == $backend && ! $status ) { + $this->errors['no_backend'] = sprintf ( __('%s cache backend activated but no PHP %s extension was found.
Please either use different backend or activate the module!', $this->plugin_constant), $backend, $backend ); } + } - /* save parameter updates, if there are any */ - if ( isset( $_POST[ $this->button_precache ] ) ) { - - /* is no shell function is possible, fail */ - if ( $this->shell_function == false ) { - $this->status = 5; - header( "Location: ". $this->settings_link . self::slug_precache_disabled ); - } - /* otherwise start full precache */ - else { - $this->precache_message = $this->precache_coldrun(); - $this->status = 4; - header( "Location: ". $this->settings_link . self::slug_precache ); + /* get the current runtime configuration for memcache in PHP because Memcache in binary mode is really problematic */ + if ( extension_loaded ( 'memcache' ) ) { + $memcache_settings = ini_get_all( 'memcache' ); + if ( !empty ( $memcache_settings ) && $this->options['cache_type'] == 'memcache' ) + { + $memcache_protocol = strtolower($memcache_settings['memcache.protocol']['local_value']); + if ( $memcache_protocol == 'binary' ) { + $this->errors['binary_memcache'] = __('WARNING: Memcache extension is configured to use binary mode. This is very buggy and the plugin will most probably not work correctly.
Please consider to change either to ASCII mode or to Memcached extension.', $this->plugin_constant ); } } } - /** - * admin help panel - */ - public function plugin_admin_help($contextual_help, $screen_id ) { + foreach ( $this->errors as $e => $msg ) { + $this->utils->alert ( $msg, 'error', $this->network ); + } + } - /* add our page only if the screenid is correct */ - if ( strpos( $screen_id, $this->plugin_settings_page ) ) { - $contextual_help = __('

Please visit the official support forum of the plugin for help.

', $this->plugin_constant ); + /** + * activation hook function, to be extended + */ + public function plugin_activate() { + /* we leave this empty to avoid not detecting WP network correctly */ + } - /* [TODO] give detailed information on errors & troubleshooting - get_current_screen()->add_help_tab( array( - 'id' => $this->plugin_constant . '-issues', - 'title' => __( 'Troubleshooting' ), - 'content' => __( '

List of errors, possible reasons and solutions

-
E#
- ' ) - ) ); - */ + /** + * deactivation hook function, to be extended + */ + public function plugin_deactivate () { + /* remove current site config from global config */ + $this->update_global_config( true ); + } + /** + * uninstall hook function, to be extended + */ + public function plugin_uninstall( $delete_options = true ) { + /* delete advanced-cache.php file */ + unlink ( $this->acache ); + + /* delete site settings */ + if ( $delete_options ) { + $this->plugin_options_delete (); + } + } + + /** + * extending admin init + * + */ + public function plugin_extend_admin_init () { + /* save parameter updates, if there are any */ + if ( isset( $_POST[ $this->button_flush ] ) ) { + + /* remove precache log entry */ + $this->_delete_option( self::precache_log ); + /* remove precache timestamp entry */ + $this->_delete_option( self::precache_timestamp ); + + /* remove precache logfile */ + if ( @file_exists ( $this->precache_logfile ) ) { + unlink ( $this->precache_logfile ); } - return $contextual_help; - } - - /** - * admin panel, the admin page displayed for plugin settings - */ - public function plugin_admin_panel() { - /** - * security, if somehow we're running without WordPress security functions - */ - if( ! function_exists( 'current_user_can' ) || ! current_user_can( 'manage_options' ) ){ - die( ); - } - ?> - -
- - - - plugin_donation_form(); - - /** - * if options were saved, display saved message - */ - if (isset($_GET[ self::key_save ]) && $_GET[ self::key_save ]=='true' || $this->status == 1) { ?> -

plugin_constant ) ?>

- status == 2) { ?> -

plugin_constant ) ?>

- status == 3) { ?> -

plugin_constant ); ?>

- status == 4) { ?> -

plugin_constant ) ?>

- - -

plugin_name ; _e( ' settings', $this->plugin_constant ) ; ?>

- -
-

plugin_constant); echo $this->options['cache_type']; ?>

- options['cache_type'], 'memcache') ) { - ?>

Backend status:
', $this->plugin_constant ); - - /* we need to go through all servers */ - $servers = $this->backend->status(); - foreach ( $servers as $server_string => $status ) { - echo $server_string ." => "; - - if ( $status == 0 ) - _e ( 'down
', $this->plugin_constant ); - elseif ( ( $this->options['cache_type'] == 'memcache' && $status > 0 ) || $status == 1 ) - _e ( 'up & running
', $this->plugin_constant ); - else - _e ( 'unknown, please try re-saving settings!
', $this->plugin_constant ); - } - - ?>

-
-
- - - -
- plugin_constant ); ?> -
-
- -
-
- - plugin_constant); ?> -
- -
- -
-
- - plugin_constant); ?> -
- -
- -
-
- - plugin_constant); ?> -
- -
- -
-
- -
  • flush cache - clears everything in storage, including values set by other applications
  • only modified post - clear only the modified posts entry, everything else remains in cache
  • modified post and all taxonomies - removes all taxonomy term cache ( categories, tags, home, etc ) and the modified post as well
  • ', $this->plugin_constant); ?>
    -
    - -
    - -
    -
    - options['comments_invalidate'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - - WARNING: changing this will result the previous cache to becomes invalid!
    If you are caching with nginx, you should update your nginx configuration and reload nginx after changing this value.', $this->plugin_constant); ?>
    -
    - -
    - -
    -
    - - WARNING: changing this will result the previous cache to becomes invalid!', $this->plugin_constant); ?> -
    - -
    - -
    -
    - - use the guide below to change it.
    WARNING: changing this will result the previous cache to becomes invalid!
    If you are caching with nginx, you should update your nginx configuration and reload nginx after changing this value.', $this->plugin_constant); ?>
    -
    list_uri_vars as $uri => $desc ) { - echo '
    '. $uri .'
    '. $desc .'
    '; - } - ?>
    -
    - -
    -
    - -
    - plugin_constant ); ?> -
    -
    - -
    -
    - options['log'],true); ?> /> - WP_DEBUG is enabled, notices and info level is displayed as well, otherwie only ERRORS are logged.', $this->plugin_constant); ?> -
    - -
    - -
    -
    - options['response_header'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - options['generate_time'],true); ?> /> - tag.', $this->plugin_constant); ?> -
    - -
    -
    - -
    - plugin_constant ); ?> -
    -
    - -
    -
    - options['cache_loggedin'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - options['nocache_home'],true); ?> /> - plugin_constant); ?> - -
    - -
    - -
    -
    - options['nocache_feed'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - options['nocache_archive'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - options['nocache_single'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - options['nocache_page'],true); ?> /> - plugin_constant); ?> -
    - -
    - -
    -
    - - If you are caching with nginx, you should update your nginx configuration and reload nginx after changing this value.', $this->plugin_constant); ?> -
    -
    -
    - -
    - plugin_constant); ?> -
    -
    - -
    -
    - - - - in case of TCP based connections, list the servers as host1:port1,host2:port2,... . Do not add trailing , and always separate host and port with : .
    - in2.0.0b1 case using unix sockets with the Memcache driver: unix:// ', $this->plugin_constant); ?>
    -
    -
    - -
    -
    - options['persistent'],true); ?> /> - plugin_constant); ?> -
    - - options['cache_type'], 'memcached') && extension_loaded ( 'memcached' ) && version_compare( phpversion( 'memcached' ) , '2.0.0', '>=' ) ) { ?> - options['authuser'] ) || !empty( $this->options['authpass'] ) ) ) { ?> -

    plugin_constant ) ?>

    - -
    - -
    -
    - - - plugin_constant); ?> -
    - -
    - -
    -
    - - - plugin_constant); ?> -
    - -
    -
    - -
    - plugin_constant); ?> -
    nginx_example(); ?>
    -
    - -
    - plugin_constant); ?> - -
    - -
    -
    - - plugin_constant); ?> -
    - - _get_option( self::precache_timestamp ); - $log = $this->_get_option( self::precache_log ); - - if ( @file_exists ( $this->precache_logfile ) ) { - $logtime = filemtime ( $this->precache_logfile ); - - /* update precache log in DB if needed */ - if ( $logtime > $gentime ) { - $log = file ( $this->precache_logfile ); - $this->_update_option( self::precache_log , $log ); - $this->_update_option( self::precache_timestamp , $logtime ); - } - - } - - if ( empty ( $log ) ) { - _e('No precache log was found!', $this->plugin_constant); - } - else { ?> -

    -
    - - - - - - - - - - - - -
    - -
    - -

    - -

    - -
    - -
    - - - -
    - plugin_constant ); ?> -
    -
    - status == 5 || $this->shell_function == false ) { ?> - Since precaching may take a very long time, it's done through a background CLI process in order not to run out of max execution time of PHP. Please enable one of the following functions if you whish to use precaching: " , $this->plugin_constant ) ?>shell_possibilities ); ?> - - - -
    -
    - The plugin tries to visit links of taxonomy terms without the taxonomy name as well. This may generate 404 hits, please be prepared for these in your logfiles if you plan to pre-cache.', $this->plugin_constant); ?> -
    -
    -
    -
    - plugin_constant ); ?> -
    -
    - -
    -
    - plugin_constant ); ?> -
    -
    -
    -
    - plugin_constant ); ?> -
    -
    - -
    -
    - plugin_constant ); ?> -
    -
    -
    -
    -
    - options['precache_schedule'] != 'null' ) { - /* clear all other schedules before adding a new in order to replace */ - wp_clear_scheduled_hook ( self::precache_id ); - $this->log ( __( 'Scheduling WP-CRON event', $this->plugin_constant ) ); - $this->scheduled = wp_schedule_event( time(), $this->options['precache_schedule'] , self::precache_id ); - } - elseif ( ( !isset($this->options['precache_schedule']) || $this->options['precache_schedule'] == 'null' ) && !empty( $schedule ) ) { - $this->log ( __('Clearing WP-CRON scheduled hook ' , $this->plugin_constant ) ); - wp_clear_scheduled_hook ( self::precache_id ); + /* remove precache PHP worker */ + if ( @file_exists ( $this->precache_phpfile ) ) { + unlink ( $this->precache_phpfile ); } - /* flush the cache when news options are saved, not needed on activation */ - if ( !$activating ) - $this->backend->clear(); - - /* create the to-be-included configuration for advanced-cache.php */ - $this->update_global_config(); - - /* create advanced cache file, needed only once or on activation, because there could be lefover advanced-cache.php from different plugins */ - if ( !$activating ) - $this->deploy_advanced_cache(); - + /* flush backend */ + $this->backend->clear( false, true ); + $this->status = 3; + header( "Location: ". $this->settings_link . self::slug_flush ); } - /** - * read hook; needs to be implemented - */ - public function plugin_hook_options_read( &$options ) { - /* read the global options, network compatibility */ - $this->global_config = get_site_option( $this->global_option ); - - /* check if current site present in global config */ - if ( !empty ( $this->global_config[ $this->global_config_key ] ) ) - $this->global_saved = true; - - $this->global_config[ $this->global_config_key ] = $options; - } - - /** - * options delete hook; needs to be implemented - */ - public function plugin_hook_options_delete( ) { - delete_site_option ( $this->global_option ); - } - - /** - * need to do migrations from previous versions of the plugin - * - */ - public function plugin_hook_options_migrate( &$options ) { - $migrated = false; - - if ( version_compare ( $options['version'] , $this->plugin_version , '<' ) ) { - /* cleanup possible leftover files from previous versions */ - $check = array ( 'advanced-cache.php', 'nginx-sample.conf', 'wp-ffpc.admin.css', 'wp-ffpc-common.php' ); - foreach ( $check as $fname ) { - $fname = $this->plugin_dir . $fname; - if ( file_exists ( $fname ) ) - unlink ( $fname ); - } - - /* look for previous config leftovers */ - $try = get_site_option( $this->plugin_constant ); - /* network option key changed, remove & migrate the leftovers if there's any */ - if ( !empty ( $try ) && $this->network ) { - /* clean it up, we don't use it anymore */ - delete_site_option ( $this->plugin_constant ); - - if ( empty ( $options ) && array_key_exists ( $this->global_config_key, $try ) ) { - $options = $try [ $this->global_config_key ]; - $migrated = true; - } - elseif ( empty ( $options ) && array_key_exists ( 'host', $try ) ) { - $options = $try; - $migrated = true; - } - } - - /* updating from version <= 0.4.x */ - if ( !empty ( $options['host'] ) ) { - $options['hosts'] = $options['host'] . ':' . $options['port']; - $migrated = true; - } - /* migrating from version 0.6.x */ - elseif ( is_array ( $options ) && array_key_exists ( $this->global_config_key , $options ) ) { - $options = $options[ $this->global_config_key ]; - $migrated = true; - } - /* migrating from something, drop previous config */ - else { - $options = array(); - } - - if ( $migrated ) { - /* renamed options */ - if ( isset ( $options['syslog'] ) ) - $options['log'] = $options['syslog']; - if ( isset ( $options['debug'] ) ) - $options['response_header'] = $options['debug']; - } + /* save parameter updates, if there are any */ + if ( isset( $_POST[ $this->button_precache ] ) ) { + /* is no shell function is possible, fail */ + if ( $this->shell_function == false ) { + $this->status = 5; + header( "Location: ". $this->settings_link . self::slug_precache_disabled ); } - } - - /** - * advanced-cache.php creator function - * - */ - private function deploy_advanced_cache( ) { - - /* in case advanced-cache.php was already there, remove it */ - if ( @file_exists( $this->acache )) - unlink ($this->acache); - - /* is deletion was unsuccessful, die, we have no rights to do that, fail */ - if ( @file_exists( $this->acache )) - return false; - - /* if no active site left no need for advanced cache :( */ - if ( empty ( $this->global_config ) ) - return false; - - /* add the required includes and generate the needed code */ - $string[] = "global_config, true ) . ';' ; - //$string[] = "include_once ('" . $this->acache_backend . "');"; - $string[] = "include_once ('" . $this->acache_worker . "');"; - $string[] = "?>"; - - /* write the file and start caching from this point */ - return file_put_contents( $this->acache, join( "\n" , $string ) ); - } - - /** - * function to generate working example from the nginx sample file - * - * @return string nginx config file - * - */ - private function nginx_example () { - /* read the sample file */ - $nginx = file_get_contents ( $this->nginx_sample ); - - /* replace the data prefix with the configured one */ - $to_replace = array ( 'DATAPREFIX' , 'SERVERROOT', 'SERVERLOG' ); - $replace_with = array ( $this->options['prefix_data'] . $this->options['key'] , ABSPATH, $_SERVER['SERVER_NAME'] ); - $nginx = str_replace ( $to_replace , $replace_with , $nginx ); - - /* set upstream servers from configured servers, best to get from the actual backend */ - $servers = $this->backend->get_servers(); - $nginx_servers = ''; - if ( is_array ( $servers )) { - foreach ( array_keys( $servers ) as $server ) { - $nginx_servers .= " server ". $server .";\n"; - } - } + /* otherwise start full precache */ else { - $nginx_servers .= " server ". $servers .";\n"; + $this->precache_message = $this->precache_coldrun(); + $this->status = 4; + header( "Location: ". $this->settings_link . self::slug_precache ); } - $nginx = str_replace ( 'MEMCACHED_SERVERS' , $nginx_servers , $nginx ); - - $loggedincookies = join('|', $this->backend->cookies ); - /* this part is not used when the cache is turned on for logged in users */ - $loggedin = ' - if ($http_cookie ~* "'. $loggedincookies .'" ) { - set $memcached_request 0; - }'; - - /* add logged in cache, if valid */ - if ( ! $this->options['cache_loggedin']) - $nginx = str_replace ( 'LOGGEDIN_EXCEPTION' , $loggedin , $nginx ); - else - $nginx = str_replace ( 'LOGGEDIN_EXCEPTION' , '' , $nginx ); - - /* nginx can skip caching for visitors with certain cookies specified in the options */ - if( $this->options['nocache_cookies'] ) { - $cookies = str_replace( ",","|", $this->options['nocache_cookies'] ); - $cookies = str_replace( " ","", $cookies ); - $cookie_exception = '# avoid cache for cookies specified - if ($http_cookie ~* ' . $cookies . ' ) { - set $memcached_request 0; - }'; - $nginx = str_replace ( 'COOKIES_EXCEPTION' , $cookie_exception , $nginx ); - } else { - $nginx = str_replace ( 'COOKIES_EXCEPTION' , '' , $nginx ); - } - - /* add custom response header if specified in the options */ - if( $this->options['response_header'] ){ - $response_header = 'add_header X-Cache-Engine "WP-FFPC with ' . $this->options['cache_type'] .' via nginx";'; - $nginx = str_replace ( 'RESPONSE_HEADER' , $response_header , $nginx ); - } else{ - $nginx = str_replace ( 'RESPONSE_HEADER' , '' , $nginx ); - } - - return $nginx; } + } - /** - * function to update global configuration - * - * @param boolean $remove_site Bool to remove or add current config to global - * - */ - private function update_global_config ( $remove_site = false ) { + /** + * admin help panel + */ + public function plugin_admin_help($contextual_help, $screen_id ) { - /* remove or add current config to global config */ - if ( $remove_site ) { - unset ( $this->global_config[ $this->global_config_key ] ); - } - else { - $this->global_config[ $this->global_config_key ] = $this->options; - } + /* add our page only if the screenid is correct */ + if ( strpos( $screen_id, $this->plugin_settings_page ) ) { + $contextual_help = __('

    Please visit the official support forum of the plugin for help.

    ', $this->plugin_constant ); - /* deploy advanced-cache.php */ - $this->deploy_advanced_cache (); - - /* save options to database */ - update_site_option( $this->global_option , $this->global_config ); - } - - - /** - * generate cache entry for every available permalink, might be very-very slow, - * therefore it starts a background process - * - */ - private function precache ( &$links ) { - - /* double check if we do have any links to pre-cache */ - if ( !empty ( $links ) && !$this->precache_running() ) { - - $out = ' $dummy ) { - $starttime = explode ( " ", microtime() ); - $starttime = $starttime[1] + $starttime[0]; - - $page = file_get_contents( $permalink ); - $size = round ( ( strlen ( $page ) / 1024 ), 2 ); - - $endtime = explode ( " ", microtime() ); - $endtime = round( ( $endtime[1] + $endtime[0] ) - $starttime, 2 ); - - echo $permalink . "\t" . $endtime . "\t" . $size . "\n"; - unset ( $page, $size, $starttime, $endtime ); - sleep( 1 ); - } - unlink ( "'. $this->precache_phpfile .'" ); - ?>'; - - file_put_contents ( $this->precache_phpfile, $out ); - /* call the precache worker file in the background */ - $shellfunction = $this->shell_function; - $shellfunction( 'php '. $this->precache_phpfile .' >'. $this->precache_logfile .' 2>&1 &' ); - } - - } - - /** - * check is precache is still ongoing - * - */ - private function precache_running () { - $return = false; - - /* if the precache file exists, it did not finish running as it should delete itself on finish */ - if ( file_exists ( $this->precache_phpfile )) { - $return = true; - } - /* - [TODO] cross-platform process check; this is *nix only - else { - $shellfunction = $this->shell_function; - $running = $shellfunction( "ps aux | grep \"". $this->precache_phpfile ."\" | grep -v grep | awk '{print $2}'" ); - if ( is_int( $running ) && $running != 0 ) { - $return = true; - } - } + /* [TODO] give detailed information on errors & troubleshooting + get_current_screen()->add_help_tab( array( + 'id' => $this->plugin_constant . '-issues', + 'title' => __( 'Troubleshooting' ), + 'content' => __( '

    List of errors, possible reasons and solutions

    +
    E#
    + ' ) + ) ); */ - return $return; } + return $contextual_help; + } + + /** + * admin panel, the admin page displayed for plugin settings + */ + public function plugin_admin_panel() { /** - * run full-site precache + * security, if somehow we're running without WordPress security functions */ - public function precache_coldrun () { + if( ! function_exists( 'current_user_can' ) || ! current_user_can( 'manage_options' ) ){ + die( ); + } + ?> - /* container for links to precache, well be accessed by reference */ - $links = array(); +
    - /* when plugin is network wide active, we need to pre-cache for all link of all blogs */ - if ( $this->network ) { - /* list all blogs */ - global $wpdb; - $blog_list = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM wp_blogs ORDER BY blog_id", '' ) ); + - foreach ($blog_list as $blog) { - if ( $blog->archived != 1 && $blog->spam != 1 && $blog->deleted != 1) { - /* get permalinks for this blog */ - $this->precache_list_permalinks ( $links, $blog->blog_id ); + plugin_donation_form(); + + /** + * if options were saved, display saved message + */ + if (isset($_GET[ self::key_save ]) && $_GET[ self::key_save ]=='true' || $this->status == 1) { ?> +

    plugin_constant ) ?>

    + status == 2) { ?> +

    plugin_constant ) ?>

    + status == 3) { ?> +

    plugin_constant ); ?>

    + status == 4) { ?> +

    plugin_constant ) ?>

    + + +

    plugin_name ; _e( ' settings', $this->plugin_constant ) ; ?>

    + +
    +

    plugin_constant); echo $this->options['cache_type']; ?>

    + options['cache_type'], 'memcache') ) { + ?>

    Backend status:
    ', $this->plugin_constant ); + + /* we need to go through all servers */ + $servers = $this->backend->status(); + foreach ( $servers as $server_string => $status ) { + echo $server_string ." => "; + + if ( $status == 0 ) + _e ( 'down
    ', $this->plugin_constant ); + elseif ( ( $this->options['cache_type'] == 'memcache' && $status > 0 ) || $status == 1 ) + _e ( 'up & running
    ', $this->plugin_constant ); + else + _e ( 'unknown, please try re-saving settings!
    ', $this->plugin_constant ); + } + + ?>

    +
    +
    + + + +
    + plugin_constant ); ?> +
    +
    + +
    +
    + + plugin_constant); ?> +
    + +
    + +
    +
    + + plugin_constant); ?> +
    + +
    + +
    +
    + + plugin_constant); ?> +
    + +
    + +
    +
    + +
  • flush cache - clears everything in storage, including values set by other applications
  • only modified post - clear only the modified posts entry, everything else remains in cache
  • modified post and all taxonomies - removes all taxonomy term cache ( categories, tags, home, etc ) and the modified post as well
  • ', $this->plugin_constant); ?>
    +
    + +
    + +
    +
    + options['comments_invalidate'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + + WARNING: changing this will result the previous cache to becomes invalid!
    If you are caching with nginx, you should update your nginx configuration and reload nginx after changing this value.', $this->plugin_constant); ?>
    +
    + +
    + +
    +
    + + WARNING: changing this will result the previous cache to becomes invalid!', $this->plugin_constant); ?> +
    + +
    + +
    +
    + + use the guide below to change it.
    WARNING: changing this will result the previous cache to becomes invalid!
    If you are caching with nginx, you should update your nginx configuration and reload nginx after changing this value.', $this->plugin_constant); ?>
    +
    list_uri_vars as $uri => $desc ) { + echo '
    '. $uri .'
    '. $desc .'
    '; } - } - } - else { - /* no network, better */ - $this->precache_list_permalinks ( $links, false ); - } + ?>
    +
    - /* double check if we do have any links to pre-cache */ - if ( !empty ( $links ) ) { - $this->precache ( $links ); - } +
    +
    + +
    + plugin_constant ); ?> +
    +
    + +
    +
    + options['log'],true); ?> /> + WP_DEBUG is enabled, notices and info level is displayed as well, otherwie only ERRORS are logged.', $this->plugin_constant); ?> +
    + +
    + +
    +
    + options['response_header'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + options['generate_time'],true); ?> /> + tag.', $this->plugin_constant); ?> +
    + +
    +
    + +
    + plugin_constant ); ?> +
    +
    + +
    +
    + options['cache_loggedin'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + options['nocache_home'],true); ?> /> + plugin_constant); ?> + +
    + +
    + +
    +
    + options['nocache_feed'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + options['nocache_archive'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + options['nocache_single'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + options['nocache_page'],true); ?> /> + plugin_constant); ?> +
    + +
    + +
    +
    + + If you are caching with nginx, you should update your nginx configuration and reload nginx after changing this value.', $this->plugin_constant); ?> +
    +
    +
    + +
    + plugin_constant); ?> +
    +
    + +
    +
    + + + - in case of TCP based connections, list the servers as host1:port1,host2:port2,... . Do not add trailing , and always separate host and port with : .
    - in2.0.0b1 case using unix sockets with the Memcache driver: unix:// ', $this->plugin_constant); ?>
    +
    +
    + +
    +
    + options['persistent'],true); ?> /> + plugin_constant); ?> +
    + + options['cache_type'], 'memcached') && extension_loaded ( 'memcached' ) && version_compare( phpversion( 'memcached' ) , '2.0.0', '>=' ) ) { ?> + options['authuser'] ) || !empty( $this->options['authpass'] ) ) ) { ?> +

    plugin_constant ) ?>

    + +
    + +
    +
    + + + plugin_constant); ?> +
    + +
    + +
    +
    + + + plugin_constant); ?> +
    + +
    +
    + +
    + plugin_constant); ?> +
    nginx_example(); ?>
    +
    + +
    + plugin_constant); ?> + +
    + +
    +
    + + plugin_constant); ?> +
    + + utils->_get_option( self::precache_timestamp, $this->network ); + $log = $this->utils->_get_option( self::precache_log, $this->network ); + + if ( @file_exists ( $this->precache_logfile ) ) { + $logtime = filemtime ( $this->precache_logfile ); + + /* update precache log in DB if needed */ + if ( $logtime > $gentime ) { + $log = file ( $this->precache_logfile ); + $this->utils->_update_option( self::precache_log , $log, $this->network ); + $this->utils->_update_option( self::precache_timestamp , $logtime, $this->network ); + } + + } + + if ( empty ( $log ) ) { + _e('No precache log was found!', $this->plugin_constant); + } + else { ?> +

    +
    + + + + + + + + + + + + +
    + +
    + +

    + +

    + +
    + +
    + + + +
    + plugin_constant ); ?> +
    +
    + status == 5 || $this->shell_function == false ) { ?> + Since precaching may take a very long time, it's done through a background CLI process in order not to run out of max execution time of PHP. Please enable one of the following functions if you whish to use precaching: " , $this->plugin_constant ) ?>shell_possibilities ); ?> + + + +
    +
    + The plugin tries to visit links of taxonomy terms without the taxonomy name as well. This may generate 404 hits, please be prepared for these in your logfiles if you plan to pre-cache.', $this->plugin_constant); ?> +
    +
    +
    +
    + plugin_constant ); ?> +
    +
    + +
    +
    + plugin_constant ); ?> +
    +
    +
    +
    + plugin_constant ); ?> +
    +
    + +
    +
    + plugin_constant ); ?> +
    +
    +
    +
    +
    + options['precache_schedule'] != 'null' ) { + /* clear all other schedules before adding a new in order to replace */ + wp_clear_scheduled_hook ( self::precache_id ); + $this->log ( __( 'Scheduling WP-CRON event', $this->plugin_constant ) ); + $this->scheduled = wp_schedule_event( time(), $this->options['precache_schedule'] , self::precache_id ); + } + elseif ( ( !isset($this->options['precache_schedule']) || $this->options['precache_schedule'] == 'null' ) && !empty( $schedule ) ) { + $this->log ( __('Clearing WP-CRON scheduled hook ' , $this->plugin_constant ) ); + wp_clear_scheduled_hook ( self::precache_id ); } - /** - * gets all post-like entry permalinks for a site, returns values in passed-by-reference array - * - */ - private function precache_list_permalinks ( &$links, $site = false ) { - /* $post will be populated when running throught the posts */ - global $post; - include_once ( ABSPATH . "wp-load.php" ); + /* flush the cache when news options are saved, not needed on activation */ + if ( !$activating ) + $this->backend->clear(); - /* if a site id was provided, save current blog and change to the other site */ - if ( $site !== false ) { - $current_blog = get_current_blog_id(); - switch_to_blog( $site ); + /* create the to-be-included configuration for advanced-cache.php */ + $this->update_global_config(); - $url = $this->_site_url( $site ); - //$url = get_blog_option ( $site, 'siteurl' ); - if ( substr( $url, -1) !== '/' ) - $url = $url . '/'; + /* create advanced cache file, needed only once or on activation, because there could be lefover advanced-cache.php from different plugins */ + if ( !$activating ) + $this->deploy_advanced_cache(); - $links[ $url ] = true; + } + + /** + * read hook; needs to be implemented + */ + public function plugin_extend_options_read( &$options ) { + /* read the global options, network compatibility */ + $this->global_config = get_site_option( $this->global_option ); + + /* check if current site present in global config */ + if ( !empty ( $this->global_config[ $this->global_config_key ] ) ) + $this->global_saved = true; + + $this->global_config[ $this->global_config_key ] = $options; + } + + /** + * options delete hook; needs to be implemented + */ + public function plugin_extend_options_delete( ) { + delete_site_option ( $this->global_option ); + } + + /** + * need to do migrations from previous versions of the plugin + * + */ + public function plugin_options_migrate( &$options ) { + $migrated = false; + + if ( version_compare ( $options['version'] , $this->plugin_version , '<' ) ) { + /* cleanup possible leftover files from previous versions */ + $check = array ( 'advanced-cache.php', 'nginx-sample.conf', 'wp-ffpc.admin.css', 'wp-ffpc-common.php' ); + foreach ( $check as $fname ) { + $fname = $this->plugin_dir . $fname; + if ( file_exists ( $fname ) ) + unlink ( $fname ); } - /* get all published posts */ - $args = array ( - 'post_type' => 'any', - 'posts_per_page' => -1, - 'post_status' => 'publish', - ); - $posts = new WP_Query( $args ); + /* look for previous config leftovers */ + $try = get_site_option( $this->plugin_constant ); + /* network option key changed, remove & migrate the leftovers if there's any */ + if ( !empty ( $try ) && $this->network ) { + /* clean it up, we don't use it anymore */ + delete_site_option ( $this->plugin_constant ); - /* get all the posts, one by one */ - while ( $posts->have_posts() ) { - $posts->the_post(); - - /* get the permalink for currently selected post */ - switch ($post->post_type) { - case 'revision': - case 'nav_menu_item': - break; - case 'page': - $permalink = get_page_link( $post->ID ); - break; - case 'post': - $permalink = get_permalink( $post->ID ); - break; - case 'attachment': - $permalink = get_attachment_link( $post->ID ); - break; - default: - $permalink = get_post_permalink( $post->ID ); - break; + if ( empty ( $options ) && array_key_exists ( $this->global_config_key, $try ) ) { + $options = $try [ $this->global_config_key ]; + $migrated = true; } - - /* in case the bloglinks are relative links add the base url, site specific */ - $baseurl = empty( $url ) ? $this->_site_url() : $url; - if ( !strstr( $permalink, $baseurl ) ) { - $permalink = $baseurl . $permalink; + elseif ( empty ( $options ) && array_key_exists ( 'host', $try ) ) { + $options = $try; + $migrated = true; } + } - /* collect permalinks */ - $links[ $permalink ] = true; - + /* updating from version <= 0.4.x */ + if ( !empty ( $options['host'] ) ) { + $options['hosts'] = $options['host'] . ':' . $options['port']; + $migrated = true; + } + /* migrating from version 0.6.x */ + elseif ( is_array ( $options ) && array_key_exists ( $this->global_config_key , $options ) ) { + $options = $options[ $this->global_config_key ]; + $migrated = true; + } + /* migrating from something, drop previous config */ + else { + $options = array(); } - $this->backend->taxonomy_links ( $links ); + if ( $migrated ) { + /* renamed options */ + if ( isset ( $options['syslog'] ) ) + $options['log'] = $options['syslog']; + if ( isset ( $options['debug'] ) ) + $options['response_header'] = $options['debug']; + } - /* just in case, reset $post */ - wp_reset_postdata(); + } + } - /* switch back to original site if we navigated away */ - if ( $site !== false ) { - switch_to_blog( $current_blog ); + /** + * advanced-cache.php creator function + * + */ + private function deploy_advanced_cache( ) { + + /* in case advanced-cache.php was already there, remove it */ + if ( @file_exists( $this->acache )) + unlink ($this->acache); + + /* is deletion was unsuccessful, die, we have no rights to do that, fail */ + if ( @file_exists( $this->acache )) + return false; + + /* if no active site left no need for advanced cache :( */ + if ( empty ( $this->global_config ) ) + return false; + + /* add the required includes and generate the needed code */ + $string[] = "global_config, true ) . ';' ; + //$string[] = "include_once ('" . $this->acache_backend . "');"; + $string[] = "include_once ('" . $this->acache_worker . "');"; + $string[] = "?>"; + + /* write the file and start caching from this point */ + return file_put_contents( $this->acache, join( "\n" , $string ) ); + } + + /** + * function to generate working example from the nginx sample file + * + * @return string nginx config file + * + */ + private function nginx_example () { + /* read the sample file */ + $nginx = file_get_contents ( $this->nginx_sample ); + + /* replace the data prefix with the configured one */ + $to_replace = array ( 'DATAPREFIX' , 'SERVERROOT', 'SERVERLOG' ); + $replace_with = array ( $this->options['prefix_data'] . $this->options['key'] , ABSPATH, $_SERVER['SERVER_NAME'] ); + $nginx = str_replace ( $to_replace , $replace_with , $nginx ); + + /* set upstream servers from configured servers, best to get from the actual backend */ + $servers = $this->backend->get_servers(); + $nginx_servers = ''; + if ( is_array ( $servers )) { + foreach ( array_keys( $servers ) as $server ) { + $nginx_servers .= " server ". $server .";\n"; } } + else { + $nginx_servers .= " server ". $servers .";\n"; + } + $nginx = str_replace ( 'MEMCACHED_SERVERS' , $nginx_servers , $nginx ); - /** - * print errors, called by global errors hook - * - */ - public function display_errors ( ) { - if ( empty ( $this->errors )) { - $r = false; - } - else { - $r = '
    '. __('WP-FFPC found errors, please correct them!', $this->plugin_constant ) .'
      '; - foreach ( $this->errors as $eid => $message ) { - $r .= '
    1. ' . $message . '
    2. '; + $loggedincookies = join('|', $this->backend->cookies ); + /* this part is not used when the cache is turned on for logged in users */ + $loggedin = ' + if ($http_cookie ~* "'. $loggedincookies .'" ) { + set $memcached_request 0; + }'; + + /* add logged in cache, if valid */ + if ( ! $this->options['cache_loggedin']) + $nginx = str_replace ( 'LOGGEDIN_EXCEPTION' , $loggedin , $nginx ); + else + $nginx = str_replace ( 'LOGGEDIN_EXCEPTION' , '' , $nginx ); + + /* nginx can skip caching for visitors with certain cookies specified in the options */ + if( $this->options['nocache_cookies'] ) { + $cookies = str_replace( ",","|", $this->options['nocache_cookies'] ); + $cookies = str_replace( " ","", $cookies ); + $cookie_exception = '# avoid cache for cookies specified + if ($http_cookie ~* ' . $cookies . ' ) { + set $memcached_request 0; + }'; + $nginx = str_replace ( 'COOKIES_EXCEPTION' , $cookie_exception , $nginx ); + } else { + $nginx = str_replace ( 'COOKIES_EXCEPTION' , '' , $nginx ); + } + + /* add custom response header if specified in the options */ + if( $this->options['response_header'] ){ + $response_header = 'add_header X-Cache-Engine "WP-FFPC with ' . $this->options['cache_type'] .' via nginx";'; + $nginx = str_replace ( 'RESPONSE_HEADER' , $response_header , $nginx ); + } else{ + $nginx = str_replace ( 'RESPONSE_HEADER' , '' , $nginx ); + } + + return $nginx; + } + + /** + * function to update global configuration + * + * @param boolean $remove_site Bool to remove or add current config to global + * + */ + private function update_global_config ( $remove_site = false ) { + + /* remove or add current config to global config */ + if ( $remove_site ) { + unset ( $this->global_config[ $this->global_config_key ] ); + } + else { + $this->global_config[ $this->global_config_key ] = $this->options; + } + + /* deploy advanced-cache.php */ + $this->deploy_advanced_cache (); + + /* save options to database */ + update_site_option( $this->global_option , $this->global_config ); + } + + + /** + * generate cache entry for every available permalink, might be very-very slow, + * therefore it starts a background process + * + */ + private function precache ( &$links ) { + + /* double check if we do have any links to pre-cache */ + if ( !empty ( $links ) && !$this->precache_running() ) { + + $out = ' $dummy ) { + $starttime = explode ( " ", microtime() ); + $starttime = $starttime[1] + $starttime[0]; + + $page = file_get_contents( $permalink ); + $size = round ( ( strlen ( $page ) / 1024 ), 2 ); + + $endtime = explode ( " ", microtime() ); + $endtime = round( ( $endtime[1] + $endtime[0] ) - $starttime, 2 ); + + echo $permalink . "\t" . $endtime . "\t" . $size . "\n"; + unset ( $page, $size, $starttime, $endtime ); + sleep( 1 ); } - $r.= '
    '; - } + unlink ( "'. $this->precache_phpfile .'" ); + ?>'; - echo $r; + file_put_contents ( $this->precache_phpfile, $out ); + /* call the precache worker file in the background */ + $shellfunction = $this->shell_function; + $shellfunction( 'php '. $this->precache_phpfile .' >'. $this->precache_logfile .' 2>&1 &' ); } } + + /** + * check is precache is still ongoing + * + */ + private function precache_running () { + $return = false; + + /* if the precache file exists, it did not finish running as it should delete itself on finish */ + if ( file_exists ( $this->precache_phpfile )) { + $return = true; + } + /* + [TODO] cross-platform process check; this is *nix only + else { + $shellfunction = $this->shell_function; + $running = $shellfunction( "ps aux | grep \"". $this->precache_phpfile ."\" | grep -v grep | awk '{print $2}'" ); + if ( is_int( $running ) && $running != 0 ) { + $return = true; + } + } + */ + + return $return; + } + + /** + * run full-site precache + */ + public function precache_coldrun () { + + /* container for links to precache, well be accessed by reference */ + $links = array(); + + /* when plugin is network wide active, we need to pre-cache for all link of all blogs */ + if ( $this->network ) { + /* list all blogs */ + global $wpdb; + $blog_list = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM wp_blogs ORDER BY blog_id", '' ) ); + + foreach ($blog_list as $blog) { + if ( $blog->archived != 1 && $blog->spam != 1 && $blog->deleted != 1) { + /* get permalinks for this blog */ + $this->precache_list_permalinks ( $links, $blog->blog_id ); + } + } + } + else { + /* no network, better */ + $this->precache_list_permalinks ( $links, false ); + } + + /* double check if we do have any links to pre-cache */ + if ( !empty ( $links ) ) { + $this->precache ( $links ); + } + } + + /** + * gets all post-like entry permalinks for a site, returns values in passed-by-reference array + * + */ + private function precache_list_permalinks ( &$links, $site = false ) { + /* $post will be populated when running throught the posts */ + global $post; + include_once ( ABSPATH . "wp-load.php" ); + + /* if a site id was provided, save current blog and change to the other site */ + if ( $site !== false ) { + $current_blog = get_current_blog_id(); + switch_to_blog( $site ); + + $url = $this->_site_url( $site ); + //$url = get_blog_option ( $site, 'siteurl' ); + if ( substr( $url, -1) !== '/' ) + $url = $url . '/'; + + $links[ $url ] = true; + } + + /* get all published posts */ + $args = array ( + 'post_type' => 'any', + 'posts_per_page' => -1, + 'post_status' => 'publish', + ); + $posts = new WP_Query( $args ); + + /* get all the posts, one by one */ + while ( $posts->have_posts() ) { + $posts->the_post(); + + /* get the permalink for currently selected post */ + switch ($post->post_type) { + case 'revision': + case 'nav_menu_item': + break; + case 'page': + $permalink = get_page_link( $post->ID ); + break; + case 'post': + $permalink = get_permalink( $post->ID ); + break; + case 'attachment': + $permalink = get_attachment_link( $post->ID ); + break; + default: + $permalink = get_post_permalink( $post->ID ); + break; + } + + /* in case the bloglinks are relative links add the base url, site specific */ + $baseurl = empty( $url ) ? $this->_site_url() : $url; + if ( !strstr( $permalink, $baseurl ) ) { + $permalink = $baseurl . $permalink; + } + + /* collect permalinks */ + $links[ $permalink ] = true; + + } + + $this->backend->taxonomy_links ( $links ); + + /* just in case, reset $post */ + wp_reset_postdata(); + + /* switch back to original site if we navigated away */ + if ( $site !== false ) { + switch_to_blog( $current_blog ); + } + } + + /** + * log wrapper to include options + * + */ + public function log ( $message, $log_level = LOG_WARNING ) { + if ( !isset ( $this->options['log'] ) || $this->options['log'] != 1 ) + return false; + else + $this->utils->log ( $this->plugin_constant, $message, $log_level ); + } + } -?> +endif; diff --git a/wp-ffpc.php b/wp-ffpc.php index ef1da94..975ccf8 100644 --- a/wp-ffpc.php +++ b/wp-ffpc.php @@ -53,6 +53,6 @@ $wp_ffpc_defaults = array ( 'comments_invalidate' => true, ); -$wp_ffpc = new WP_FFPC ( 'wp-ffpc', '1.6.0', 'WP-FFPC', $wp_ffpc_defaults, 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=XU3DG7LLA76WC' ); +$wp_ffpc = new WP_FFPC ( 'wp-ffpc', '1.6.0', 'WP-FFPC', $wp_ffpc_defaults, 'PeterMolnar_WordPressPlugins_wp-ffpc_HU' , 'WP-FFPC' , 'FA3NT7XDVHPWU' ); ?>