Plugin Directory

Changeset 1260794


Ignore:
Timestamp:
10/07/2015 05:28:37 AM (10 years ago)
Author:
vnsavage
Message:

Bump version; Port multiple bugfixes from Github - https://github.com/Automattic/batcache

Location:
batcache/tags/1.2
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • batcache/tags/1.2/advanced-cache.php

    r634460 r1260794  
    11<?php
     2if ( is_readable( dirname( __FILE__ ) . '/batcache-stats.php' ) )
     3    require_once dirname( __FILE__ ) . '/batcache-stats.php';
     4
     5if ( !function_exists( 'batcache_stats' ) ) {
     6    function batcache_stats( $name, $value, $num = 1, $today = FALSE, $hour = FALSE ) { }
     7}
    28
    39// nananananananananananananananana BATCACHE!!!
     
    814    if ( is_object($batcache) )
    915        $batcache->cancel = true;
     16}
     17
     18// Variants can be set by functions which use early-set globals like $_SERVER to run simple tests.
     19// Functions defined in WordPress, plugins, and themes are not available and MUST NOT be used.
     20// Example: vary_cache_on_function('return preg_match("/feedburner/i", $_SERVER["HTTP_USER_AGENT"]);');
     21//          This will cause batcache to cache a variant for requests from Feedburner.
     22// Tips for writing $function:
     23//  X_X  DO NOT use any functions from your theme or plugins. Those files have not been included. Fatal error.
     24//  X_X  DO NOT use any WordPress functions except is_admin() and is_multisite(). Fatal error.
     25//  X_X  DO NOT include or require files from anywhere without consulting expensive professionals first. Fatal error.
     26//  X_X  DO NOT use $wpdb, $blog_id, $current_user, etc. These have not been initialized.
     27//  ^_^  DO understand how create_function works. This is how your code is used: create_function('', $function);
     28//  ^_^  DO remember to return something. The return value determines the cache variant.
     29function vary_cache_on_function($function) {
     30    global $batcache;
     31
     32    if ( preg_match('/include|require|echo|print|dump|export|open|sock|unlink|`|eval/i', $function) )
     33        die('Illegal word in variant determiner.');
     34
     35    if ( !preg_match('/\$_/', $function) )
     36        die('Variant determiner should refer to at least one $_ variable.');
     37
     38    $batcache->add_variant($function);
    1039}
    1140
     
    1342    // This is the base configuration. You can edit these variables or move them into your wp-config.php file.
    1443    var $max_age =  300; // Expire batcache items aged this many seconds (zero to disable batcache)
    15    
     44
    1645    var $remote  =    0; // Zero disables sending buffers to remote datacenters (req/sec is never sent)
    17    
     46
    1847    var $times   =    2; // Only batcache a page after it is accessed this many times... (two or more)
    1948    var $seconds =  120; // ...in this many seconds (zero to ignore this and use batcache immediately)
    20    
     49
    2150    var $group   = 'batcache'; // Name of memcached group. You can simulate a cache flush by changing this.
    22    
     51
    2352    var $unique  = array(); // If you conditionally serve different content, put the variable values here.
    24    
    25     var $headers = array(); // Add headers here. These will be sent with every response from the cache.
     53
     54    var $vary    = array(); // Array of functions for create_function. The return value is added to $unique above.
     55
     56    var $headers = array(); // Add headers here as name=>value or name=>array(values). These will be sent with every response from the cache.
    2657
    2758    var $cache_redirects = false; // Set true to enable redirect caching.
     
    3768    var $cancel = false; // Change this to cancel the output buffer. Use batcache_cancel();
    3869
    39     var $genlock; // Used internally
    40     var $do; // Used internally
     70    var $noskip_cookies = array( 'wordpress_test_cookie' ); // Names of cookies - if they exist and the cache would normally be bypassed, don't bypass it
     71
     72    var $genlock = false;
     73    var $do = false;
    4174
    4275    function batcache( $settings ) {
     
    5790    }
    5891
    59     function status_header( $status_header ) {
     92    function status_header( $status_header, $status_code ) {
    6093        $this->status_header = $status_header;
     94        $this->status_code = $status_code;
    6195
    6296        return $status_header;
     
    70104
    71105        return $status;
     106    }
     107
     108    function do_headers( $headers1, $headers2 = array() ) {
     109        // Merge the arrays of headers into one
     110        $headers = array();
     111        $keys = array_unique( array_merge( array_keys( $headers1 ), array_keys( $headers2 ) ) );
     112        foreach ( $keys as $k ) {
     113            $headers[$k] = array();
     114            if ( isset( $headers1[$k] ) && isset( $headers2[$k] ) )
     115                $headers[$k] = array_merge( (array) $headers2[$k], (array) $headers1[$k] );
     116            elseif ( isset( $headers2[$k] ) )
     117                $headers[$k] = (array) $headers2[$k];
     118            else
     119                $headers[$k] = (array) $headers1[$k];
     120            $headers[$k] = array_unique( $headers[$k] );
     121        }
     122        // These headers take precedence over any previously sent with the same names
     123        foreach ( $headers as $k => $values ) {
     124            $clobber = true;
     125            foreach ( $values as $v ) {
     126                header( "$k: $v", $clobber );
     127                $clobber = false;
     128            }
     129        }
    72130    }
    73131
     
    110168            return;
    111169
     170        // Do not cache 5xx responses
     171        if ( isset( $this->status_code ) && intval($this->status_code / 100) == 5 )
     172            return $output;
     173
     174        $this->do_variants($this->vary);
     175        $this->generate_keys();
     176
    112177        // Construct and save the batcache
    113         $cache = array(
     178        $this->cache = array(
    114179            'output' => $output,
    115180            'time' => time(),
    116181            'timer' => $this->timer_stop(false, 3),
     182            'headers' => array(),
    117183            'status_header' => $this->status_header,
    118184            'redirect_status' => $this->redirect_status,
     
    121187        );
    122188
    123         if ( function_exists( 'headers_list' ) ) {
    124             foreach ( headers_list() as $header ) {
    125                 list($k, $v) = array_map('trim', explode(':', $header, 2));
    126                 $cache['headers'][$k] = $v;
    127             }
    128         } elseif ( function_exists( 'apache_response_headers' ) ) {
    129             $cache['headers'] = apache_response_headers();
    130         }
    131 
    132         if ( $cache['headers'] && !empty( $this->uncached_headers ) ) {
    133             foreach ( $cache['headers'] as $header => $value ) {
    134                 if ( in_array( strtolower( $header ), $this->uncached_headers ) )
    135                     unset( $cache['headers'][$header] );
    136             }
    137         }
    138 
    139         wp_cache_set($this->key, $cache, $this->group, $this->max_age + $this->seconds + 30);
     189        foreach ( headers_list() as $header ) {
     190            list($k, $v) = array_map('trim', explode(':', $header, 2));
     191            $this->cache['headers'][$k][] = $v;
     192        }
     193
     194        if ( !empty( $this->cache['headers'] ) && !empty( $this->uncached_headers ) ) {
     195            foreach ( $this->uncached_headers as $header )
     196                unset( $this->cache['headers'][$header] );
     197        }
     198
     199        foreach ( $this->cache['headers'] as $header => $values ) {
     200            // Do not cache if cookies were set
     201            if ( strtolower( $header ) === 'set-cookie' )
     202                return $output;
     203
     204            foreach ( (array) $values as $value )
     205                if ( preg_match('/^Cache-Control:.*max-?age=(\d+)/i', "$header: $value", $matches) )
     206                    $this->max_age = intval($matches[1]);
     207        }
     208
     209        $this->cache['max_age'] = $this->max_age;
     210
     211        wp_cache_set($this->key, $this->cache, $this->group, $this->max_age + $this->seconds + 30);
    140212
    141213        // Unlock regeneration
     
    143215
    144216        if ( $this->cache_control ) {
    145             header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $cache['time'] ) . ' GMT', true );
    146             header("Cache-Control: max-age=$this->max_age, must-revalidate", false);
    147         }
    148 
    149         if ( !empty($this->headers) ) foreach ( $this->headers as $k => $v ) {
    150             if ( is_array( $v ) )
    151                 header("{$v[0]}: {$v[1]}", false);
    152             else
    153                 header("$k: $v", true);
    154         }
    155 
    156         // Add some debug info just before </head>
     217            // Don't clobber Last-Modified header if already set, e.g. by WP::send_headers()
     218            if ( !isset($this->cache['headers']['Last-Modified']) )
     219                header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $this->cache['time'] ) . ' GMT', true );
     220            if ( !isset($this->cache['headers']['Cache-Control']) )
     221                header("Cache-Control: max-age=$this->max_age, must-revalidate", false);
     222        }
     223
     224        $this->do_headers( $this->headers );
     225
     226        // Add some debug info just before <head
    157227        if ( $this->debug ) {
    158             $tag = "<!--\n\tgenerated in " . $cache['timer'] . " seconds\n\t" . strlen(serialize($cache)) . " bytes batcached for " . $this->max_age . " seconds\n-->\n";
    159             if ( false !== $tag_position = strpos($output, '</head>') ) {
    160                 $tag = "<!--\n\tgenerated in " . $cache['timer'] . " seconds\n\t" . strlen(serialize($cache)) . " bytes batcached for " . $this->max_age . " seconds\n-->\n";
    161                 $output = substr($output, 0, $tag_position) . $tag . substr($output, $tag_position);
     228            $this->add_debug_just_cached();
     229        }
     230
     231        // Pass output to next ob handler
     232        batcache_stats( 'batcache', 'total_page_views' );
     233        return $this->cache['output'];
     234    }
     235
     236    function add_variant($function) {
     237        $key = md5($function);
     238        $this->vary[$key] = $function;
     239    }
     240
     241    function do_variants($dimensions = false) {
     242        // This function is called without arguments early in the page load, then with arguments during the OB handler.
     243        if ( $dimensions === false )
     244            $dimensions = wp_cache_get("{$this->url_key}_vary", $this->group);
     245        else
     246            wp_cache_set("{$this->url_key}_vary", $dimensions, $this->group, $this->max_age + 10);
     247
     248        if ( is_array($dimensions) ) {
     249            ksort($dimensions);
     250            foreach ( $dimensions as $key => $function ) {
     251                $fun = create_function('', $function);
     252                $value = $fun();
     253                $this->keys[$key] = $value;
    162254            }
    163255        }
    164 
    165         // Pass output to next ob handler
    166         return $output;
    167     }
    168 }
     256    }
     257
     258    function generate_keys() {
     259        // ksort($this->keys); // uncomment this when traffic is slow
     260        $this->key = md5(serialize($this->keys));
     261        $this->req_key = $this->key . '_req';
     262    }
     263
     264    function add_debug_just_cached() {
     265        $generation = $this->cache['timer'];
     266        $bytes = strlen( serialize( $this->cache ) );
     267        $html = <<<HTML
     268<!--
     269    generated in $generation seconds
     270    $bytes bytes batcached for {$this->max_age} seconds
     271-->
     272
     273HTML;
     274        $this->add_debug_html_to_output( $html );
     275    }
     276
     277    function add_debug_from_cache() {
     278        $seconds_ago = time() - $this->cache['time'];
     279        $generation = $this->cache['timer'];
     280        $serving = $this->timer_stop( false, 3 );
     281        $expires = $this->cache['max_age'] - time() + $this->cache['time'];
     282        $html = <<<HTML
     283<!--
     284    generated $seconds_ago seconds ago
     285    generated in $generation seconds
     286    served from batcache in $serving seconds
     287    expires in $expires seconds
     288-->
     289
     290HTML;
     291        $this->add_debug_html_to_output( $html );
     292    }
     293
     294    function add_debug_html_to_output( $debug_html ) {
     295        // Casing on the Content-Type header is inconsistent
     296        foreach ( array( 'Content-Type', 'Content-type' ) as $key ) {
     297            if ( isset( $this->cache['headers'][ $key ][0] ) && 0 !== strpos( $this->cache['headers'][ $key ][0], 'text/html' ) )
     298                return;
     299        }
     300
     301        $head_position = strpos( $this->cache['output'], '<head' );
     302        if ( false === $head_position ) {
     303            return;
     304        }
     305        $this->cache['output'] .= "\n$debug_html";
     306    }
     307}
     308
    169309global $batcache;
    170310// Pass in the global variable which may be an array of settings to override defaults.
     
    180320            'wp-app.php',
    181321            'xmlrpc.php',
    182             'ms-files.php',
    183322        ) ) )
    184323    return;
     
    193332
    194333// Never batcache when cookies indicate a cache-exempt visitor.
    195 if ( is_array( $_COOKIE) && ! empty( $_COOKIE ) )
    196     foreach ( array_keys( $_COOKIE ) as $batcache->cookie )
    197         if ( $batcache->cookie != 'wordpress_test_cookie' && ( substr( $batcache->cookie, 0, 2 ) == 'wp' || substr( $batcache->cookie, 0, 9 ) == 'wordpress' || substr( $batcache->cookie, 0, 14 ) == 'comment_author' ) )
     334if ( is_array( $_COOKIE) && ! empty( $_COOKIE ) ) {
     335    foreach ( array_keys( $_COOKIE ) as $batcache->cookie ) {
     336        if ( ! in_array( $batcache->cookie, $batcache->noskip_cookies ) && ( substr( $batcache->cookie, 0, 2 ) == 'wp' || substr( $batcache->cookie, 0, 9 ) == 'wordpress' || substr( $batcache->cookie, 0, 14 ) == 'comment_author' ) ) {
     337            batcache_stats( 'batcache', 'cookie_skip' );
    198338            return;
     339        }
     340    }
     341}
    199342
    200343if ( ! include_once( WP_CONTENT_DIR . '/object-cache.php' ) )
     
    218361
    219362/* Example: batcache everything on this host regardless of traffic level
    220 if ( $_SERVER['HTTP_HOST'] == 'always-batcache-me.com' ) {
    221     $batcache->max_age = 600; // Cache for 10 minutes
    222     $batcache->seconds = $batcache->times = 0; // No need to wait till n number of people have accessed the page, cache instantly
    223 }
     363if ( $_SERVER['HTTP_HOST'] == 'always-batcache-me.com' )
     364    return;
    224365*/
    225366
     
    243384if ( isset( $_SERVER['QUERY_STRING'] ) )
    244385    parse_str($_SERVER['QUERY_STRING'], $batcache->query);
     386
    245387$batcache->keys = array(
    246388    'host' => $_SERVER['HTTP_HOST'],
     
    254396    $batcache->keys['ssl'] = true;
    255397
     398// Recreate the permalink from the URL
     399$batcache->permalink = 'http://' . $batcache->keys['host'] . $batcache->keys['path'] . ( isset($batcache->keys['query']['p']) ? "?p=" . $batcache->keys['query']['p'] : '' );
     400$batcache->url_key = md5($batcache->permalink);
    256401$batcache->configure_groups();
    257 
    258 // Generate the batcache key
    259 $batcache->key = md5(serialize($batcache->keys));
    260 
    261 // Generate the traffic threshold measurement key
    262 $batcache->req_key = $batcache->key . '_req';
     402$batcache->url_version = (int) wp_cache_get("{$batcache->url_key}_version", $batcache->group);
     403$batcache->do_variants();
     404$batcache->generate_keys();
    263405
    264406// Get the batcache
    265407$batcache->cache = wp_cache_get($batcache->key, $batcache->group);
    266408
    267 // Are we only caching frequently-requested pages?
    268 if ( $batcache->seconds < 1 || $batcache->times < 2 ) {
     409if ( isset($batcache->cache['version']) && $batcache->cache['version'] < $batcache->url_version ) {
     410    // Always refresh the cache if a newer version is available.
     411    $batcache->do = true;
     412} else if ( $batcache->seconds < 1 || $batcache->times < 2 ) {
     413    // Are we only caching frequently-requested pages?
    269414    $batcache->do = true;
    270415} else {
     
    281426}
    282427
    283 // Recreate the permalink from the URL
    284 $batcache->permalink = 'http://' . $batcache->keys['host'] . $batcache->keys['path'] . ( isset($batcache->keys['query']['p']) ? "?p=" . $batcache->keys['query']['p'] : '' );
    285 $batcache->url_key = md5($batcache->permalink);
    286 $batcache->url_version = (int) wp_cache_get("{$batcache->url_key}_version", $batcache->group);
    287 
    288428// If the document has been updated and we are the first to notice, regenerate it.
    289 if ( $batcache->do !== false && isset($batcache->cache['version']) && $batcache->cache['version'] < $batcache->url_version )
    290     $batcache->genlock = wp_cache_add("{$batcache->url_key}_genlock", 1, $batcache->group);
     429if ( $batcache->do )
     430    $batcache->genlock = wp_cache_add("{$batcache->url_key}_genlock", 1, $batcache->group, 10);
     431
     432// Temporary: remove after 2010-11-12. I added max_age to the cache. This upgrades older caches on the fly.
     433if ( !isset($batcache->cache['max_age']) )
     434    $batcache->cache['max_age'] = $batcache->max_age;
     435
    291436
    292437// Did we find a batcached page that hasn't expired?
    293 if ( isset($batcache->cache['time']) && ! $batcache->genlock && time() < $batcache->cache['time'] + $batcache->max_age ) {
     438if ( isset($batcache->cache['time']) && ! $batcache->genlock && time() < $batcache->cache['time'] + $batcache->cache['max_age'] ) {
    294439    // Issue redirect if cached and enabled
    295440    if ( $batcache->cache['redirect_status'] && $batcache->cache['redirect_location'] && $batcache->cache_redirects ) {
     
    298443        // From vars.php
    299444        $is_IIS = (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false);
     445
     446        $batcache->do_headers( $batcache->headers );
    300447        if ( $is_IIS ) {
    301448            header("Refresh: 0;url=$location");
     
    325472    }
    326473
    327     // Issue "304 Not Modified" only if the dates match exactly.
    328     if ( $batcache->cache_control && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) {
    329         $since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
    330         if ( isset($batcache->cache['headers']['Last-Modified']) )
    331             $batcache->cache['time'] = strtotime( $batcache->cache['headers']['Last-Modified'] );
    332         if ( $batcache->cache['time'] == $since ) {
    333             header('Last-Modified: ' . $_SERVER['HTTP_IF_MODIFIED_SINCE'], true, 304);
    334             exit;
    335         }
    336     }
    337 
    338     // Use the batcache save time for Last-Modified so we can issue "304 Not Modified"
    339     if ( $batcache->cache_control ) {
     474    // Respect ETags served with feeds.
     475    $three04 = false;
     476    if ( isset( $SERVER['HTTP_IF_NONE_MATCH'] ) && isset( $batcache->cache['headers']['ETag'][0] ) && $_SERVER['HTTP_IF_NONE_MATCH'] == $batcache->cache['headers']['ETag'][0] )
     477        $three04 = true;
     478
     479    // Respect If-Modified-Since.
     480    elseif ( $batcache->cache_control && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ) {
     481        $client_time = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
     482        if ( isset($batcache->cache['headers']['Last-Modified'][0]) )
     483            $cache_time = strtotime($batcache->cache['headers']['Last-Modified'][0]);
     484        else
     485            $cache_time = $batcache->cache['time'];
     486
     487        if ( $client_time >= $cache_time )
     488            $three04 = true;
     489    }
     490
     491    // Use the batcache save time for Last-Modified so we can issue "304 Not Modified" but don't clobber a cached Last-Modified header.
     492    if ( $batcache->cache_control && !isset($batcache->cache['headers']['Last-Modified'][0]) ) {
    340493        header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $batcache->cache['time'] ) . ' GMT', true );
    341         header('Cache-Control: max-age=' . ($batcache->max_age - time() + $batcache->cache['time']) . ', must-revalidate', true);
     494        header('Cache-Control: max-age=' . ($batcache->cache['max_age'] - time() + $batcache->cache['time']) . ', must-revalidate', true);
    342495    }
    343496
    344497    // Add some debug info just before </head>
    345498    if ( $batcache->debug ) {
    346         if ( false !== $tag_position = strpos($batcache->cache['output'], '</head>') ) {
    347             $tag = "<!--\n\tgenerated " . (time() - $batcache->cache['time']) . " seconds ago\n\tgenerated in " . $batcache->cache['timer'] . " seconds\n\tserved from batcache in " . $batcache->timer_stop(false, 3) . " seconds\n\texpires in " . ($batcache->max_age - time() + $batcache->cache['time']) . " seconds\n-->\n";
    348             $batcache->cache['output'] = substr($batcache->cache['output'], 0, $tag_position) . $tag . substr($batcache->cache['output'], $tag_position);
    349         }
    350     }
    351 
    352     if ( !empty($batcache->cache['headers']) ) foreach ( $batcache->cache['headers'] as $k => $v )
    353         header("$k: $v", true);
    354 
    355     if ( !empty($batcache->headers) ) foreach ( $batcache->headers as $k => $v ) {
    356         if ( is_array( $v ) )
    357             header("{$v[0]}: {$v[1]}", false);
    358         else
    359             header("$k: $v", true);
     499        $batcache->add_debug_from_cache();
     500    }
     501
     502    $batcache->do_headers( $batcache->headers, $batcache->cache['headers'] );
     503
     504    if ( $three04 ) {
     505        header("HTTP/1.1 304 Not Modified", true, 304);
     506        die;
    360507    }
    361508
     
    371518    return;
    372519
    373 $wp_filter['status_header'][10]['batcache'] = array( 'function' => array(&$batcache, 'status_header'), 'accepted_args' => 1 );
     520$wp_filter['status_header'][10]['batcache'] = array( 'function' => array(&$batcache, 'status_header'), 'accepted_args' => 2 );
    374521$wp_filter['wp_redirect_status'][10]['batcache'] = array( 'function' => array(&$batcache, 'redirect_status'), 'accepted_args' => 2 );
    375522
  • batcache/tags/1.2/batcache.php

    r636875 r1260794  
    1010
    1111// Do not load if our advanced-cache.php isn't loaded
    12 if ( ! is_object($batcache) || ! method_exists( $wp_object_cache, 'incr' ) )
     12if ( ! isset( $batcache ) || ! is_object($batcache) || ! method_exists( $wp_object_cache, 'incr' ) )
    1313    return;
    1414
     
    2727
    2828    $post = get_post($post_id);
    29     if ( $post->post_type == 'revision' || get_post_status($post_id) != 'publish' )
     29    if ( empty( $post ) || $post->post_type == 'revision' || get_post_status($post_id) != 'publish' )
    3030        return;
    3131
  • batcache/tags/1.2/readme.txt

    r636874 r1260794  
    33Tags: cache, memcache, memcached, speed, performance, load, server
    44Requires at least: 3.2
    5 Tested up to: 3.5
     5Tested up to: 4.3.1
    66Stable tag: 1.2
    77
Note: See TracChangeset for help on using the changeset viewer.