Test Failed
Push — issues/1944 ( 84cdd0...5feab3 )
by Ravinder
04:15
created

Give_Cache::delete_form_related_cache()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 4
nop 1
dl 0
loc 26
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/**
3
 * Class for managing cache
4
 * Note: only use for internal purpose.
5
 *
6
 * @package     Give
7
 * @subpackage  Classes/Give_Cache
8
 * @copyright   Copyright (c) 2017, WordImpress
9
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
10
 * @since       1.8.7
11
 */
12
13
// Exit if accessed directly.
14
if ( ! defined( 'ABSPATH' ) ) {
15
	exit;
16
}
17
18
class Give_Cache {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
19
	/**
20
	 * Instance.
21
	 *
22
	 * @since  1.8.7
23
	 * @access private
24
	 * @var Give_Cache
25
	 */
26
	static private $instance;
27
28
	/**
29
	 * Flag to check if caching enabled or not.
30
	 *
31
	 * @since  2.0
32
	 * @access private
33
	 * @var
34
	 */
35
	private $is_cache;
36
37
	/**
38
	 * Singleton pattern.
39
	 *
40
	 * @since  1.8.7
41
	 * @access private
42
	 * Give_Cache constructor.
43
	 */
44
	private function __construct() {
45
	}
46
47
48
	/**
49
	 * Get instance.
50
	 *
51
	 * @since  1.8.7
52
	 * @access public
53
	 * @return static
54
	 */
55
	public static function get_instance() {
56
		if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Give_Cache ) ) {
57
			self::$instance = new Give_Cache();
58
		}
59
60
		return self::$instance;
61
	}
62
63
	/**
64
	 * Setup hooks.
65
	 *
66
	 * @since  1.8.7
67
	 * @access public
68
	 */
69
	public function setup() {
70
		self::$instance->is_cache = give_is_setting_enabled( give_get_option( 'cache', 'enabled' ) );
71
72
		// weekly delete all expired cache.
73
		Give_Cron::add_weekly_event( array( $this, 'delete_all_expired' ) );
74
75
		add_action( 'save_post_give_forms', array( $this, 'delete_form_related_cache' ) );
76
		add_action( 'give_delete_give-donors_cache', array( $this, 'delete_donor_related_cache' ), 10, 3 );
77
	}
78
79
	/**
80
	 * Get cache key.
81
	 *
82
	 * @since  1.8.7
83
	 *
84
	 * @param  string $action     Cache key prefix.
85
	 * @param  array  $query_args (optional) Query array.
86
	 *
87
	 * @return string|WP_Error
88
	 */
89
90
	public static function get_key( $action, $query_args = null ) {
91
		// Bailout.
92
		if ( empty( $action ) ) {
93
			return new WP_Error( 'give_invalid_cache_key_action', __( 'Do not pass empty action to generate cache key.', 'give' ) );
94
		}
95
96
		// Handle specific cache key prefix.
97
		// @see https://core.trac.wordpress.org/ticket/4476
98
		if ( 'give-db-queries' === $action ) {
99
			$timestamp = get_option( 'give-last-cache-updated' );
100
			$timestamp = empty( $timestamp ) ? current_time( 'timestamp', 1 ) : $timestamp;
101
102
			return "give-db-queries-{$timestamp}";
103
		}
104
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
105
106
		// Set cache key.
107
		$cache_key = "give_cache_{$action}";
108
109
		// Bailout.
110
		if ( ! empty( $query_args ) ) {
111
			$cache_key = "{$cache_key}_" . substr( md5( serialize( $query_args ) ), 0, 15 );
112
		}
113
114
		/**
115
		 * Filter the cache key name.
116
		 *
117
		 * @since 2.0
118
		 */
119
		return apply_filters( 'give_get_cache_key', $cache_key, $action, $query_args );
120
	}
121
122
	/**
123
	 * Get cache.
124
	 *
125
	 * @since  1.8.7
126
	 *
127
	 * @param  string $cache_key
128
	 * @param  bool   $custom_key
129
	 * @param  mixed  $query_args
130
	 *
131
	 * @return mixed
132
	 */
133
134
	public static function get( $cache_key, $custom_key = false, $query_args = array() ) {
135 View Code Duplication
		if ( ! self::is_valid_cache_key( $cache_key ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
136
			if ( ! $custom_key ) {
137
				return new WP_Error( 'give_invalid_cache_key', __( 'Cache key format should be give_cache_*', 'give' ) );
138
			}
139
140
			$cache_key = self::get_key( $cache_key, $query_args );
141
		}
142
143
		$option = get_option( $cache_key );
144
145
		// Backward compatibility (<1.8.7).
146
		if ( ! is_array( $option ) || empty( $option ) || ! array_key_exists( 'expiration', $option ) ) {
147
			return $option;
148
		}
149
150
		// Get current time.
151
		$current_time = current_time( 'timestamp', 1 );
152
153
		if ( empty( $option['expiration'] ) || ( $current_time < $option['expiration'] ) ) {
154
			$option = $option['data'];
155
		} else {
156
			$option = false;
157
		}
158
159
		return $option;
160
	}
161
162
	/**
163
	 * Set cache.
164
	 *
165
	 * @since  1.8.7
166
	 *
167
	 * @param  string   $cache_key
168
	 * @param  mixed    $data
169
	 * @param  int|null $expiration Timestamp should be in GMT format.
170
	 * @param  bool     $custom_key
171
	 * @param  mixed    $query_args
172
	 *
173
	 * @return mixed
174
	 */
175
176
	public static function set( $cache_key, $data, $expiration = null, $custom_key = false, $query_args = array() ) {
177 View Code Duplication
		if ( ! self::is_valid_cache_key( $cache_key ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
			if ( ! $custom_key ) {
179
				return new WP_Error( 'give_invalid_cache_key', __( 'Cache key format should be give_cache_*', 'give' ) );
180
			}
181
182
			$cache_key = self::get_key( $cache_key, $query_args );
183
		}
184
185
		$option_value = array(
186
			'data'       => $data,
187
			'expiration' => ! is_null( $expiration )
188
				? ( $expiration + current_time( 'timestamp', 1 ) )
189
				: null,
190
		);
191
192
		$result = update_option( $cache_key, $option_value, 'no' );
193
194
		return $result;
195
	}
196
197
	/**
198
	 * Delete cache.
199
	 *
200
	 * Note: only for internal use
201
	 *
202
	 * @since  1.8.7
203
	 *
204
	 * @param  string|array $cache_keys
205
	 *
206
	 * @return bool|WP_Error
207
	 */
208
209
	public static function delete( $cache_keys ) {
210
		$result       = true;
211
		$invalid_keys = array();
212
213
		if ( ! empty( $cache_keys ) ) {
214
			$cache_keys = is_array( $cache_keys ) ? $cache_keys : array( $cache_keys );
215
216
			foreach ( $cache_keys as $cache_key ) {
217
				if ( ! self::is_valid_cache_key( $cache_key ) ) {
218
					$invalid_keys[] = $cache_key;
219
					$result         = false;
220
				}
221
222
				delete_option( $cache_key );
223
			}
224
		}
225
226
		if ( ! $result ) {
227
			$result = new WP_Error(
228
				'give_invalid_cache_key',
229
				__( 'Cache key format should be give_cache_*', 'give' ),
230
				$invalid_keys
231
			);
232
		}
233
234
		return $result;
235
	}
236
237
	/**
238
	 * Delete all logging cache.
239
	 *
240
	 * Note: only for internal use
241
	 *
242
	 * @since  1.8.7
243
	 * @access public
244
	 * @global wpdb $wpdb
245
	 *
246
	 * @param bool  $force If set to true then all cached values will be delete instead of only expired
247
	 *
248
	 * @return bool
249
	 */
250
	public static function delete_all_expired( $force = false ) {
251
		global $wpdb;
252
		$options = $wpdb->get_results(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
253
			$wpdb->prepare(
254
				"SELECT option_name, option_value
255
						FROM {$wpdb->options}
256
						Where option_name
257
						LIKE '%%%s%%'",
258
				'give_cache'
259
			),
260
			ARRAY_A
261
		);
262
263
		// Bailout.
264
		if ( empty( $options ) ) {
265
			return false;
266
		}
267
268
		$current_time = current_time( 'timestamp', 1 );
269
270
		// Delete log cache.
271
		foreach ( $options as $option ) {
272
			$option['option_value'] = maybe_unserialize( $option['option_value'] );
273
274
			if (
275
				(
276
					! self::is_valid_cache_key( $option['option_name'] )
277
					|| ! is_array( $option['option_value'] ) // Backward compatibility (<1.8.7).
278
					|| ! array_key_exists( 'expiration', $option['option_value'] ) // Backward compatibility (<1.8.7).
279
					|| empty( $option['option_value']['expiration'] )
280
					|| ( $current_time < $option['option_value']['expiration'] )
281
				)
282
				&& ! $force
283
			) {
284
				continue;
285
			}
286
287
			self::delete( $option['option_name'] );
288
		}
289
	}
290
291
292
	/**
293
	 * Get list of options like.
294
	 *
295
	 * Note: only for internal use
296
	 *
297
	 * @since  1.8.7
298
	 * @access public
299
	 *
300
	 * @param string $option_name
301
	 * @param bool   $fields
302
	 *
303
	 * @return array
304
	 */
305
	public static function get_options_like( $option_name, $fields = false ) {
306
		global $wpdb;
307
308
		if ( empty( $option_name ) ) {
309
			return array();
310
		}
311
312
		$field_names = $fields ? 'option_name, option_value' : 'option_name';
313
314
		if ( $fields ) {
315
			$options = $wpdb->get_results(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
316
				$wpdb->prepare(
317
					"SELECT {$field_names }
318
						FROM {$wpdb->options}
319
						Where option_name
320
						LIKE '%%%s%%'",
321
					"give_cache_{$option_name}"
322
				),
323
				ARRAY_A
324
			);
325
		} else {
326
			$options = $wpdb->get_col(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
327
				$wpdb->prepare(
328
					"SELECT *
329
						FROM {$wpdb->options}
330
						Where option_name
331
						LIKE '%%%s%%'",
332
					"give_cache_{$option_name}"
333
				),
334
				1
335
			);
336
		}
337
338
		if ( ! empty( $options ) && $fields ) {
339
			foreach ( $options as $index => $option ) {
340
				$option['option_value'] = maybe_unserialize( $option['option_value'] );
341
				$options[ $index ]      = $option;
342
			}
343
		}
344
345
		return $options;
346
	}
347
348
	/**
349
	 * Check cache key validity.
350
	 *
351
	 * @since  1.8.7
352
	 * @access public
353
	 *
354
	 * @param $cache_key
355
	 *
356
	 * @return bool
357
	 */
358
	public static function is_valid_cache_key( $cache_key ) {
359
		$is_valid = ( false !== strpos( $cache_key, 'give_cache_' ) );
360
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
361
362
		/**
363
		 * Filter the flag which tell about cache key valid or not
364
		 *
365
		 * @since 2.0
366
		 */
367
		return apply_filters( 'give_is_valid_cache_key', $is_valid, $cache_key );
368
	}
369
370
371
	/**
372
	 * Get cache from group
373
	 *
374
	 * @since  2.0
375
	 * @access public
376
	 *
377
	 * @param int    $id
378
	 * @param string $group
379
	 *
380
	 * @return mixed
381
	 */
382
	public static function get_group( $id, $group = '' ) {
383
		$cached_data = false;
384
385
		// Bailout.
386
		if ( ! self::$instance->is_cache || empty( $id ) ) {
387
			return $cached_data;
388
		}
389
390
		$cached_data = wp_cache_get( $id, $group );
391
392
		return $cached_data;
393
	}
394
395
	/**
396
	 * Cache small chunks inside group
397
	 *
398
	 * @since  2.0
399
	 * @access public
400
	 *
401
	 * @param int    $id
402
	 * @param mixed  $data
403
	 * @param string $group
404
	 * @param int    $expire
405
	 *
406
	 * @return bool
407
	 */
408
	public static function set_group( $id, $data, $group = '', $expire = 0 ) {
409
		$status = false;
410
411
		// Bailout.
412
		if ( ! self::$instance->is_cache || empty( $id ) ) {
413
			return $status;
414
		}
415
416
		$status = wp_cache_set( $id, $data, $group, $expire );
417
418
		update_option( 'give-last-cache-updated', current_time( 'timestamp', 1 ) );
419
420
		return $status;
421
	}
422
423
	/**
424
	 * Delete group cache
425
	 *
426
	 * @since  2.0
427
	 * @access public
428
	 *
429
	 * @param int|array $ids
430
	 * @param string    $group
431
	 * @param int       $expire
432
	 *
433
	 * @return bool
434
	 */
435
	public static function delete_group( $ids, $group = '', $expire = 0 ) {
436
		$status = false;
437
438
		// Bailout.
439
		if ( ! self::$instance->is_cache || empty( $ids ) ) {
440
			return $status;
441
		}
442
443
		// Delete single or multiple cache items from cache.
444
		if ( ! is_array( $ids ) ) {
445
			$status = wp_cache_delete( $ids, $group, $expire );
446
447
			/**
448
			 * Fire action when cache deleted for specific id.
449
			 *
450
			 * @since 2.0
451
			 *
452
			 * @param string $ids
453
			 * @param string $group
454
			 * @param int    $expire
455
			 */
456
			do_action( "give_delete_{$group}_cache", $ids, $group, $expire );
457
458
		} else {
459
			foreach ( $ids as $id ) {
460
				$status = wp_cache_delete( $id, $group, $expire );
461
462
				/**
463
				 * Fire action when cache deleted for specific id .
464
				 *
465
				 * @since 2.0
466
				 *
467
				 * @param string $ids
468
				 * @param string $group
469
				 * @param int    $expire
470
				 */
471
				do_action( "give_delete_{$group}_cache", $id, $group, $expire );
472
			}
473
		}
474
475
		// Update timestamp in DB when cache update.
476
		update_option( 'give-last-cache-updated', current_time( 'timestamp', 1 ) );
477
478
		return $status;
479
	}
480
481
482
	/**
483
	 * Delete form related cache
484
	 * Note: only use for internal purpose.
485
	 *
486
	 * @since  2.0
487
	 * @access public
488
	 *
489
	 * @param int $form_id
490
	 */
491
	public function delete_form_related_cache( $form_id ) {
492
		// If this is just a revision, don't send the email.
493
		if ( wp_is_post_revision( $form_id ) ) {
494
			return;
495
		}
496
497
		$donation_query = new Give_Payments_Query(
498
			array(
499
				'number'     => - 1,
500
				'give_forms' => $form_id
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
501
			)
502
		);
503
504
		$donations = $donation_query->get_payments();
505
506
		// Bailout.
507
		if ( empty( $donations ) ) {
508
			return;
509
		}
510
511
		$donations = wp_list_pluck( $donations, 'ID' );
512
513
		foreach ( $donations as $donation ) {
514
			wp_cache_delete( $donation, 'give-donations' );
515
		}
516
	}
517
518
	/**
519
	 * Delete donor related cache
520
	 * Note: only use for internal purpose.
521
	 *
522
	 * @since  2.0
523
	 * @access public
524
	 *
525
	 * @param string $id
526
	 * @param string $group
527
	 * @param int    $expire
528
	 */
529
	public function delete_donor_related_cache( $id, $group, $expire ) {
0 ignored issues
show
Unused Code introduced by
The parameter $group is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expire is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
530
		$donor        = new Give_Donor( $id );
0 ignored issues
show
Documentation introduced by
$id is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
531
		$donation_ids = array_map( 'trim', (array) explode( ',', trim( $donor->payment_ids ) ) );
532
533
		// bailout.
534
		if ( empty( $donation_ids ) ) {
535
			return;
536
		}
537
538
		foreach ( $donation_ids as $donation ) {
539
			wp_cache_delete( $donation, 'give-donations' );
540
		}
541
	}
542
}
543
544
// Initialize
545
Give_Cache::get_instance()->setup();
546