2013-04-09 09:38:40 +00:00
< ? php
/**
* WC_Cache_Helper class .
*
2020-08-05 16:36:24 +00:00
* @ package WooCommerce\Classes
2013-04-09 09:38:40 +00:00
*/
2017-11-08 14:54:13 +00:00
2018-02-26 19:18:38 +00:00
defined ( 'ABSPATH' ) || exit ;
2017-11-08 14:54:13 +00:00
/**
* WC_Cache_Helper .
*/
2013-04-09 09:38:40 +00:00
class WC_Cache_Helper {
2019-01-18 23:12:46 +00:00
/**
* Transients to delete on shutdown .
*
* @ var array Array of transient keys .
*/
private static $delete_transients = array ();
2013-04-09 09:38:40 +00:00
/**
2015-11-03 13:31:20 +00:00
* Hook in methods .
2013-04-09 09:38:40 +00:00
*/
2014-05-28 13:52:50 +00:00
public static function init () {
2019-02-19 16:35:26 +00:00
add_filter ( 'nocache_headers' , array ( __CLASS__ , 'additional_nocache_headers' ), 10 );
2019-01-18 23:12:46 +00:00
add_action ( 'shutdown' , array ( __CLASS__ , 'delete_transients_on_shutdown' ), 10 );
2015-06-22 13:55:15 +00:00
add_action ( 'template_redirect' , array ( __CLASS__ , 'geolocation_ajax_redirect' ) );
2021-02-19 22:50:59 +00:00
add_action ( 'wc_ajax_update_order_review' , array ( __CLASS__ , 'update_geolocation_hash' ), 5 );
2014-05-28 13:52:50 +00:00
add_action ( 'admin_notices' , array ( __CLASS__ , 'notices' ) );
2019-02-06 12:15:29 +00:00
add_action ( 'delete_version_transients' , array ( __CLASS__ , 'delete_version_transients' ), 10 );
2017-11-08 14:54:13 +00:00
add_action ( 'wp' , array ( __CLASS__ , 'prevent_caching' ) );
2018-02-28 18:35:58 +00:00
add_action ( 'clean_term_cache' , array ( __CLASS__ , 'clean_term_cache' ), 10 , 2 );
2018-03-13 19:00:08 +00:00
add_action ( 'edit_terms' , array ( __CLASS__ , 'clean_term_cache' ), 10 , 2 );
2013-04-09 09:38:40 +00:00
}
2019-02-19 16:35:26 +00:00
/**
2019-03-04 14:57:49 +00:00
* Set additional nocache headers .
2019-02-19 16:35:26 +00:00
*
* @ param array $headers Header names and field values .
* @ since 3.6 . 0
*/
public static function additional_nocache_headers ( $headers ) {
2020-06-24 17:26:25 +00:00
$agent = isset ( $_SERVER [ 'HTTP_USER_AGENT' ] ) ? wp_unslash ( $_SERVER [ 'HTTP_USER_AGENT' ] ) : '' ; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2020-06-17 18:02:09 +00:00
/**
2020-06-24 17:26:25 +00:00
* Allow plugins to enable nocache headers . Enabled for Google weblight .
2020-06-17 18:02:09 +00:00
*
2020-06-24 17:26:25 +00:00
* @ see https :// support . google . com / webmasters / answer / 1061943 ? hl = en
* @ param bool $enable_nocache_headers Flag indicating whether to add nocache headers . Default : false .
2020-06-17 18:02:09 +00:00
*/
2020-06-24 17:26:25 +00:00
if ( false !== strpos ( $agent , 'googleweblight' ) || apply_filters ( 'woocommerce_enable_nocache_headers' , false ) ) {
2020-06-18 13:56:31 +00:00
// no-transform: Opt-out of Google weblight. https://support.google.com/webmasters/answer/6211428?hl=en.
2020-06-17 18:02:09 +00:00
$headers [ 'Cache-Control' ] = 'no-transform, no-cache, no-store, must-revalidate' ;
}
2019-02-19 16:35:26 +00:00
return $headers ;
}
2019-01-18 23:12:46 +00:00
/**
* Add a transient to delete on shutdown .
*
* @ since 3.6 . 0
* @ param string | array $keys Transient key or keys .
*/
public static function queue_delete_transient ( $keys ) {
self :: $delete_transients = array_unique ( array_merge ( is_array ( $keys ) ? $keys : array ( $keys ), self :: $delete_transients ) );
}
/**
* Transients that don ' t need to be cleaned right away can be deleted on shutdown to avoid repetition .
*
* @ since 3.6 . 0
*/
public static function delete_transients_on_shutdown () {
if ( self :: $delete_transients ) {
foreach ( self :: $delete_transients as $key ) {
delete_transient ( $key );
}
self :: $delete_transients = array ();
}
}
/**
* Used to clear layered nav counts based on passed attribute names .
*
* @ since 3.6 . 0
* @ param array $attribute_keys Attribute keys .
*/
public static function invalidate_attribute_count ( $attribute_keys ) {
if ( $attribute_keys ) {
foreach ( $attribute_keys as $attribute_key ) {
self :: queue_delete_transient ( 'wc_layered_nav_counts_' . $attribute_key );
}
}
}
2015-11-13 23:06:38 +00:00
/**
* Get prefix for use with wp_cache_set . Allows all cache in a group to be invalidated at once .
2017-11-08 14:54:13 +00:00
*
* @ param string $group Group of cache to get .
2015-11-13 23:06:38 +00:00
* @ return string
*/
public static function get_cache_prefix ( $group ) {
2017-11-08 14:54:13 +00:00
// Get cache key - uses cache key wc_orders_cache_prefix to invalidate when needed.
2015-11-13 23:06:38 +00:00
$prefix = wp_cache_get ( 'wc_' . $group . '_cache_prefix' , $group );
if ( false === $prefix ) {
2019-11-03 13:14:53 +00:00
$prefix = microtime ();
2015-11-13 23:06:38 +00:00
wp_cache_set ( 'wc_' . $group . '_cache_prefix' , $prefix , $group );
}
return 'wc_cache_' . $prefix . '_' ;
}
/**
* Increment group cache prefix ( invalidates cache ) .
2017-11-08 14:54:13 +00:00
*
* @ param string $group Group of cache to clear .
2015-11-13 23:06:38 +00:00
*/
public static function incr_cache_prefix ( $group ) {
2019-11-28 13:03:57 +00:00
wc_deprecated_function ( 'WC_Cache_Helper::incr_cache_prefix' , '3.9.0' , 'WC_Cache_Helper::invalidate_cache_group' );
self :: invalidate_cache_group ( $group );
}
/**
* Invalidate cache group .
*
* @ param string $group Group of cache to clear .
* @ since 3.9 . 0
*/
public static function invalidate_cache_group ( $group ) {
2019-11-03 13:14:53 +00:00
wp_cache_set ( 'wc_' . $group . '_cache_prefix' , microtime (), $group );
2015-11-13 23:06:38 +00:00
}
2015-06-22 13:55:15 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get a hash of the customer location .
2017-11-08 14:54:13 +00:00
*
2015-06-22 13:55:15 +00:00
* @ return string
*/
public static function geolocation_ajax_get_location_hash () {
2016-11-14 18:18:08 +00:00
$customer = new WC_Customer ( 0 , true );
2015-06-22 13:55:15 +00:00
$location = array ();
2016-03-17 19:39:29 +00:00
$location [ 'country' ] = $customer -> get_billing_country ();
$location [ 'state' ] = $customer -> get_billing_state ();
$location [ 'postcode' ] = $customer -> get_billing_postcode ();
$location [ 'city' ] = $customer -> get_billing_city ();
2019-01-16 14:47:59 +00:00
return apply_filters ( 'woocommerce_geolocation_ajax_get_location_hash' , substr ( md5 ( implode ( '' , $location ) ), 0 , 12 ), $location , $customer );
2015-06-22 13:55:15 +00:00
}
2017-11-08 14:27:30 +00:00
/**
* Prevent caching on certain pages
*/
2017-11-08 14:54:13 +00:00
public static function prevent_caching () {
2017-11-08 14:27:30 +00:00
if ( ! is_blog_installed () ) {
return ;
}
$page_ids = array_filter ( array ( wc_get_page_id ( 'cart' ), wc_get_page_id ( 'checkout' ), wc_get_page_id ( 'myaccount' ) ) );
2017-11-08 15:07:00 +00:00
2017-11-08 14:27:30 +00:00
if ( is_page ( $page_ids ) ) {
2017-11-08 15:07:00 +00:00
self :: set_nocache_constants ();
nocache_headers ();
2017-11-08 14:27:30 +00:00
}
}
2015-06-22 13:55:15 +00:00
/**
* When using geolocation via ajax , to bust cache , redirect if the location hash does not equal the querystring .
*
* This prevents caching of the wrong data for this request .
*/
public static function geolocation_ajax_redirect () {
2017-11-08 14:54:13 +00:00
if ( 'geolocation_ajax' === get_option ( 'woocommerce_default_customer_address' ) && ! is_checkout () && ! is_cart () && ! is_account_page () && ! is_ajax () && empty ( $_POST ) ) { // WPCS: CSRF ok, input var ok.
2015-06-22 13:55:15 +00:00
$location_hash = self :: geolocation_ajax_get_location_hash ();
2018-02-26 19:18:38 +00:00
$current_hash = isset ( $_GET [ 'v' ] ) ? wc_clean ( wp_unslash ( $_GET [ 'v' ] ) ) : '' ; // WPCS: sanitization ok, input var ok, CSRF ok.
2015-06-22 13:55:15 +00:00
if ( empty ( $current_hash ) || $current_hash !== $location_hash ) {
global $wp ;
2015-06-22 14:30:42 +00:00
$redirect_url = trailingslashit ( home_url ( $wp -> request ) );
2017-11-08 14:54:13 +00:00
if ( ! empty ( $_SERVER [ 'QUERY_STRING' ] ) ) { // WPCS: Input var ok.
$redirect_url = add_query_arg ( wp_unslash ( $_SERVER [ 'QUERY_STRING' ] ), '' , $redirect_url ); // WPCS: sanitization ok, Input var ok.
2015-09-07 15:58:05 +00:00
}
2015-06-22 14:30:42 +00:00
if ( ! get_option ( 'permalink_structure' ) ) {
$redirect_url = add_query_arg ( $wp -> query_string , '' , $redirect_url );
}
$redirect_url = add_query_arg ( 'v' , $location_hash , remove_query_arg ( 'v' , $redirect_url ) );
wp_safe_redirect ( esc_url_raw ( $redirect_url ), 307 );
2015-06-22 13:55:15 +00:00
exit ;
}
}
}
2021-02-19 22:50:59 +00:00
/**
* Updates the `woocommerce_geo_hash` cookie , which is used to help ensure we display
* the correct pricing etc to customers , according to their billing country .
*
* Note that :
*
* A ) This only sets the cookie if the default customer address is set to " Geolocate (with
* Page Caching Support ) " .
*
* B ) It is hooked into the `wc_ajax_update_order_review` action , which has the benefit of
* ensuring we update the cookie any time the billing country is changed .
*/
public static function update_geolocation_hash () {
if ( 'geolocation_ajax' === get_option ( 'woocommerce_default_customer_address' ) ) {
wc_setcookie ( 'woocommerce_geo_hash' , static :: geolocation_ajax_get_location_hash (), time () + HOUR_IN_SECONDS );
}
}
2014-07-03 11:59:54 +00:00
/**
2015-11-03 13:31:20 +00:00
* Get transient version .
2014-07-03 11:59:54 +00:00
*
2018-01-18 18:50:27 +00:00
* When using transients with unpredictable names , e . g . those containing an md5
2014-09-20 18:44:22 +00:00
* hash in the name , we need a way to invalidate them all at once .
2014-07-03 11:59:54 +00:00
*
2018-01-18 18:50:27 +00:00
* When using default WP transients we ' re able to do this with a DB query to
2014-07-03 11:59:54 +00:00
* delete transients manually .
*
2018-01-18 18:50:27 +00:00
* With external cache however , this isn ' t possible . Instead , this function is used
* to append a unique string ( based on time ()) to each transient . When transients
2014-07-03 11:59:54 +00:00
* are invalidated , the transient version will increment and data will be regenerated .
*
2016-09-28 10:17:40 +00:00
* Raised in issue https :// github . com / woocommerce / woocommerce / issues / 5777.
2015-11-03 13:31:20 +00:00
* Adapted from ideas in http :// tollmanz . com / invalidation - schemes /.
2014-09-20 18:44:22 +00:00
*
2017-11-08 14:54:13 +00:00
* @ param string $group Name for the group of transients we need to invalidate .
* @ param boolean $refresh true to force a new version .
* @ return string transient version based on time (), 10 digits .
2014-07-03 11:59:54 +00:00
*/
public static function get_transient_version ( $group , $refresh = false ) {
$transient_name = $group . '-transient-version' ;
2014-07-03 12:02:19 +00:00
$transient_value = get_transient ( $transient_name );
2014-07-03 11:59:54 +00:00
2018-09-05 14:46:38 +00:00
if ( false === $transient_value || true === $refresh ) {
$transient_value = ( string ) time ();
2018-02-26 19:18:38 +00:00
set_transient ( $transient_name , $transient_value );
2014-07-03 11:59:54 +00:00
}
2018-09-05 14:46:38 +00:00
return $transient_value ;
2018-06-14 13:40:08 +00:00
}
2013-04-09 09:38:40 +00:00
/**
2017-11-08 15:07:00 +00:00
* Set constants to prevent caching by some plugins .
*
* @ param mixed $return Value to return . Previously hooked into a filter .
* @ return mixed
2013-04-09 09:38:40 +00:00
*/
2017-11-08 15:07:00 +00:00
public static function set_nocache_constants ( $return = true ) {
2017-09-05 19:52:39 +00:00
wc_maybe_define_constant ( 'DONOTCACHEPAGE' , true );
wc_maybe_define_constant ( 'DONOTCACHEOBJECT' , true );
wc_maybe_define_constant ( 'DONOTCACHEDB' , true );
2017-11-08 15:07:00 +00:00
return $return ;
2013-04-09 09:38:40 +00:00
}
/**
2017-11-08 14:54:13 +00:00
* Notices function .
2013-04-09 09:38:40 +00:00
*/
2014-05-28 13:52:50 +00:00
public static function notices () {
if ( ! function_exists ( 'w3tc_pgcache_flush' ) || ! function_exists ( 'w3_instance' ) ) {
2013-04-09 09:38:40 +00:00
return ;
2014-05-28 13:52:50 +00:00
}
2013-04-09 09:38:40 +00:00
2016-09-02 01:51:31 +00:00
$config = w3_instance ( 'W3_Config' );
2013-04-09 09:38:40 +00:00
$enabled = $config -> get_integer ( 'dbcache.enabled' );
2014-10-21 20:24:55 +00:00
$settings = array_map ( 'trim' , $config -> get_array ( 'dbcache.reject.sql' ) );
2013-04-09 09:38:40 +00:00
2017-11-08 14:54:13 +00:00
if ( $enabled && ! in_array ( '_wc_session_' , $settings , true ) ) {
2013-04-09 09:38:40 +00:00
?>
< div class = " error " >
2018-02-26 19:18:38 +00:00
< p >
< ? php
/* translators: 1: key 2: URL */
echo wp_kses_post ( sprintf ( __ ( 'In order for <strong>database caching</strong> to work with WooCommerce you must add %1$s to the "Ignored Query Strings" option in <a href="%2$s">W3 Total Cache settings</a>.' , 'woocommerce' ), '<code>_wc_session_</code>' , esc_url ( admin_url ( 'admin.php?page=w3tc_dbcache' ) ) ) );
?>
</ p >
2013-04-09 09:38:40 +00:00
</ div >
< ? php
}
}
2018-02-26 19:15:42 +00:00
/**
* Clean term caches added by WooCommerce .
*
* @ since 3.3 . 4
2018-03-13 19:00:08 +00:00
* @ param array | int $ids Array of ids or single ID to clear cache for .
* @ param string $taxonomy Taxonomy name .
2018-02-28 18:35:58 +00:00
*/
public static function clean_term_cache ( $ids , $taxonomy ) {
if ( 'product_cat' === $taxonomy ) {
2018-03-13 19:00:08 +00:00
$ids = is_array ( $ids ) ? $ids : array ( $ids );
2018-05-30 12:29:44 +00:00
$clear_ids = array ( 0 );
2018-03-25 18:28:14 +00:00
2018-03-13 19:00:08 +00:00
foreach ( $ids as $id ) {
$clear_ids [] = $id ;
$clear_ids = array_merge ( $clear_ids , get_ancestors ( $id , 'product_cat' , 'taxonomy' ) );
}
$clear_ids = array_unique ( $clear_ids );
foreach ( $clear_ids as $id ) {
wp_cache_delete ( 'product-category-hierarchy-' . $id , 'product_cat' );
}
2018-02-28 18:35:58 +00:00
}
}
2019-01-21 13:08:01 +00:00
/**
* When the transient version increases , this is used to remove all past transients to avoid filling the DB .
*
* Note ; this only works on transients appended with the transient version , and when object caching is not being used .
*
* @ deprecated 3.6 . 0 Adjusted transient usage to include versions within the transient values , making this cleanup obsolete .
* @ since 2.3 . 10
* @ param string $version Version of the transient to remove .
*/
public static function delete_version_transients ( $version = '' ) {
if ( ! wp_using_ext_object_cache () && ! empty ( $version ) ) {
global $wpdb ;
$limit = apply_filters ( 'woocommerce_delete_version_transients_limit' , 1000 );
if ( ! $limit ) {
return ;
}
$affected = $wpdb -> query ( $wpdb -> prepare ( " DELETE FROM { $wpdb -> options } WHERE option_name LIKE %s LIMIT %d; " , '\_transient\_%' . $version , $limit ) ); // WPCS: cache ok, db call ok.
// If affected rows is equal to limit, there are more rows to delete. Delete in 30 secs.
if ( $affected === $limit ) {
wp_schedule_single_event ( time () + 30 , 'delete_version_transients' , array ( $version ) );
}
}
}
2013-04-09 09:38:40 +00:00
}
2014-05-28 13:52:50 +00:00
WC_Cache_Helper :: init ();