Completed
Push — master ( fa2280...8edf6d )
by Mike
08:29
created

WC_Cache_Helper::delete_version_transients()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 13
rs 9.2
cc 4
eloc 7
nc 3
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 16 and the first side effect is on line 4.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * WC_Cache_Helper class.
9
 *
10
 * @class 		WC_Cache_Helper
11
 * @version		2.2.0
12
 * @package		WooCommerce/Classes
13
 * @category	Class
14
 * @author 		WooThemes
15
 */
16
class WC_Cache_Helper {
17
18
	/**
19
	 * Hook in methods.
20
	 */
21
	public static function init() {
22
		add_action( 'template_redirect', array( __CLASS__, 'geolocation_ajax_redirect' ) );
23
		add_action( 'before_woocommerce_init', array( __CLASS__, 'prevent_caching' ) );
24
		add_action( 'admin_notices', array( __CLASS__, 'notices' ) );
25
		add_action( 'delete_version_transients', array( __CLASS__, 'delete_version_transients' ) );
26
	}
27
28
	/**
29
	 * Get prefix for use with wp_cache_set. Allows all cache in a group to be invalidated at once.
30
	 * @param  string $group
31
	 * @return string
32
	 */
33
	public static function get_cache_prefix( $group ) {
34
		// Get cache key - uses cache key wc_orders_cache_prefix to invalidate when needed
35
		$prefix = wp_cache_get( 'wc_' . $group . '_cache_prefix', $group );
36
37
		if ( false === $prefix ) {
38
			$prefix = 1;
39
			wp_cache_set( 'wc_' . $group . '_cache_prefix', $prefix, $group );
40
		}
41
42
		return 'wc_cache_' . $prefix . '_';
43
	}
44
45
	/**
46
	 * Increment group cache prefix (invalidates cache).
47
	 * @param  string $group
48
	 */
49
	public static function incr_cache_prefix( $group ) {
50
		wp_cache_incr( 'wc_' . $group . '_cache_prefix', 1, $group );
51
	}
52
53
	/**
54
	 * Get a hash of the customer location.
55
	 * @return string
56
	 */
57
	public static function geolocation_ajax_get_location_hash() {
58
		$customer             = new WC_Customer();
59
		$location             = array();
60
		$location['country']  = $customer->get_country();
61
		$location['state']    = $customer->get_state();
62
		$location['postcode'] = $customer->get_postcode();
63
		$location['city']     = $customer->get_city();
64
		return substr( md5( implode( '', $location ) ), 0, 12 );
65
	}
66
67
	/**
68
	 * When using geolocation via ajax, to bust cache, redirect if the location hash does not equal the querystring.
69
	 *
70
	 * This prevents caching of the wrong data for this request.
71
	 */
72
	public static function geolocation_ajax_redirect() {
73
		if ( 'geolocation_ajax' === get_option( 'woocommerce_default_customer_address' ) && ! is_checkout() && ! is_cart() && ! is_account_page() && ! is_ajax() && empty( $_POST ) ) {
74
			$location_hash = self::geolocation_ajax_get_location_hash();
75
			$current_hash  = isset( $_GET['v'] ) ? wc_clean( $_GET['v'] ) : '';
76
			if ( empty( $current_hash ) || $current_hash !== $location_hash ) {
77
				global $wp;
78
79
				$redirect_url = trailingslashit( home_url( $wp->request ) );
80
81
				if ( ! empty( $_SERVER['QUERY_STRING'] ) ) {
82
					$redirect_url = add_query_arg( $_SERVER['QUERY_STRING'], '', $redirect_url );
83
				}
84
85
				if ( ! get_option( 'permalink_structure' ) ) {
86
					$redirect_url = add_query_arg( $wp->query_string, '', $redirect_url );
87
				}
88
89
				$redirect_url = add_query_arg( 'v', $location_hash, remove_query_arg( 'v', $redirect_url ) );
90
91
				wp_safe_redirect( esc_url_raw( $redirect_url ), 307 );
92
				exit;
93
			}
94
		}
95
	}
96
97
	/**
98
	 * Get transient version.
99
	 *
100
	 * When using transients with unpredictable names, e.g. those containing an md5.
101
	 * hash in the name, we need a way to invalidate them all at once.
102
	 *
103
	 * When using default WP transients we're able to do this with a DB query to.
104
	 * delete transients manually.
105
	 *
106
	 * With external cache however, this isn't possible. Instead, this function is used.
107
	 * to append a unique string (based on time()) to each transient. When transients.
108
	 * are invalidated, the transient version will increment and data will be regenerated.
109
	 *
110
	 * Raised in issue https://github.com/woothemes/woocommerce/issues/5777.
111
	 * Adapted from ideas in http://tollmanz.com/invalidation-schemes/.
112
	 *
113
	 * @param  string  $group   Name for the group of transients we need to invalidate
114
	 * @param  boolean $refresh true to force a new version
115
	 * @return string transient version based on time(), 10 digits
116
	 */
117
	public static function get_transient_version( $group, $refresh = false ) {
118
		$transient_name  = $group . '-transient-version';
119
		$transient_value = get_transient( $transient_name );
120
121
		if ( false === $transient_value || true === $refresh ) {
122
			self::delete_version_transients( $transient_value );
123
			set_transient( $transient_name, $transient_value = time() );
124
		}
125
		return $transient_value;
126
	}
127
128
	/**
129
	 * When the transient version increases, this is used to remove all past transients to avoid filling the DB.
130
	 *
131
	 * Note; this only works on transients appended with the transient version, and when object caching is not being used.
132
	 *
133
	 * @since  2.3.10
134
	 */
135
	public static function delete_version_transients( $version = '' ) {
136
		if ( ! wp_using_ext_object_cache() && ! empty( $version ) ) {
137
			global $wpdb;
138
139
			$limit    = apply_filters( 'woocommerce_delete_version_transients_limit', 1000 );
140
			$affected = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_id LIMIT %d;", "\_transient\_%" . $version, $limit ) );
141
142
			// If affected rows is equal to limit, there are more rows to delete. Delete in 10 secs.
143
			if ( $affected === $limit ) {
144
				wp_schedule_single_event( time() + 10, 'delete_version_transients', array( $version ) );
145
			}
146
		}
147
	}
148
149
	/**
150
	 * Get the page name/id for a WC page.
151
	 * @param  string $wc_page
152
	 * @return array
153
	 */
154
	private static function get_page_uris( $wc_page ) {
155
		$wc_page_uris = array();
156
157
		if ( ( $page_id = wc_get_page_id( $wc_page ) ) && $page_id > 0 && ( $page = get_post( $page_id ) ) ) {
158
			$wc_page_uris[] = 'p=' . $page_id;
159
			$wc_page_uris[] = '/' . $page->post_name . '/';
160
		}
161
162
		return $wc_page_uris;
163
	}
164
165
	/**
166
	 * Prevent caching on dynamic pages.
167
	 * @access public
168
	 */
169
	public static function prevent_caching() {
170
		if ( false === ( $wc_page_uris = get_transient( 'woocommerce_cache_excluded_uris' ) ) ) {
171
			$wc_page_uris   = array_filter( array_merge( self::get_page_uris( 'cart' ), self::get_page_uris( 'checkout' ), self::get_page_uris( 'myaccount' ) ) );
172
	    	set_transient( 'woocommerce_cache_excluded_uris', $wc_page_uris );
173
		}
174
175
		if ( isset( $_GET['download_file'] ) ) {
176
			self::nocache();
177
		} elseif ( is_array( $wc_page_uris ) ) {
178
			foreach( $wc_page_uris as $uri ) {
179
				if ( stristr( $_SERVER['REQUEST_URI'], $uri ) ) {
180
					self::nocache();
181
					break;
182
				}
183
			}
184
		}
185
186
		// Noindex for endpoints
187
		if ( is_wc_endpoint_url() || isset( $_GET['download_file'] ) ) {
188
			self::noindex();
189
		}
190
	}
191
192
	/**
193
	 * Set nocache constants and headers.
194
	 * @access private
195
	 */
196
	private static function nocache() {
197
		if ( ! defined( 'DONOTCACHEPAGE' ) ) {
198
			define( "DONOTCACHEPAGE", true );
199
		}
200
		if ( ! defined( 'DONOTCACHEOBJECT' ) ) {
201
			define( "DONOTCACHEOBJECT", true );
202
		}
203
		if ( ! defined( 'DONOTCACHEDB' ) ) {
204
			define( "DONOTCACHEDB", true );
205
		}
206
		nocache_headers();
207
	}
208
209
	/**
210
	 * Set noindex headers.
211
	 * @access private
212
	 */
213
	private static function noindex() {
214
		@header( 'X-Robots-Tag: noindex' );
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
215
	}
216
217
	/**
218
	 * notices function.
219
	 */
220
	public static function notices() {
221
		if ( ! function_exists( 'w3tc_pgcache_flush' ) || ! function_exists( 'w3_instance' ) ) {
222
			return;
223
		}
224
225
		$config   = w3_instance('W3_Config');
226
		$enabled  = $config->get_integer( 'dbcache.enabled' );
227
		$settings = array_map( 'trim', $config->get_array( 'dbcache.reject.sql' ) );
228
229
		if ( $enabled && ! in_array( '_wc_session_', $settings ) ) {
230
			?>
231
			<div class="error">
232
				<p><?php printf( __( 'In order for <strong>database caching</strong> to work with WooCommerce you must add <code>_wc_session_</code> to the "Ignored Query Strings" option in W3 Total Cache settings <a href="%s">here</a>.', 'woocommerce' ), admin_url( 'admin.php?page=w3tc_dbcache' ) ); ?></p>
233
			</div>
234
			<?php
235
		}
236
	}
237
}
238
239
WC_Cache_Helper::init();
240