1
|
|
|
<?php |
|
|
|
|
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' ); |
|
|
|
|
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
|
|
|
|
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.