Test Failed
Push — issues/1944 ( 5feab3...920e61 )
by Ravinder
04:13
created

Give_Cache::delete_donations_related_cache()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 3
dl 0
loc 8
rs 9.4285
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_deleted_give-donors_cache', array( $this, 'delete_donor_related_cache' ), 10, 3 );
77
		add_action( 'give_deleted_give-donations_cache', array( $this, 'delete_donations_related_cache' ), 10, 3 );
78
	}
79
80
	/**
81
	 * Get cache key.
82
	 *
83
	 * @since  1.8.7
84
	 *
85
	 * @param  string $action     Cache key prefix.
86
	 * @param  array  $query_args (optional) Query array.
87
	 *
88
	 * @return string|WP_Error
89
	 */
90
91
	public static function get_key( $action, $query_args = null ) {
92
		// Bailout.
93
		if ( empty( $action ) ) {
94
			return new WP_Error( 'give_invalid_cache_key_action', __( 'Do not pass empty action to generate cache key.', 'give' ) );
95
		}
96
97
		// Handle specific cache key prefix.
98
		// @see https://core.trac.wordpress.org/ticket/4476
99
		if ( 'give-db-queries' === $action ) {
100
			$timestamp = get_option( 'give-last-cache-updated' );
101
			$timestamp = empty( $timestamp ) ? current_time( 'timestamp', 1 ) : $timestamp;
102
103
			return "give-db-queries-{$timestamp}";
104
		}
105
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
106
107
		// Set cache key.
108
		$cache_key = "give_cache_{$action}";
109
110
		// Bailout.
111
		if ( ! empty( $query_args ) ) {
112
			$cache_key = "{$cache_key}_" . substr( md5( serialize( $query_args ) ), 0, 15 );
113
		}
114
115
		/**
116
		 * Filter the cache key name.
117
		 *
118
		 * @since 2.0
119
		 */
120
		return apply_filters( 'give_get_cache_key', $cache_key, $action, $query_args );
121
	}
122
123
	/**
124
	 * Get cache.
125
	 *
126
	 * @since  1.8.7
127
	 *
128
	 * @param  string $cache_key
129
	 * @param  bool   $custom_key
130
	 * @param  mixed  $query_args
131
	 *
132
	 * @return mixed
133
	 */
134
135
	public static function get( $cache_key, $custom_key = false, $query_args = array() ) {
136 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...
137
			if ( ! $custom_key ) {
138
				return new WP_Error( 'give_invalid_cache_key', __( 'Cache key format should be give_cache_*', 'give' ) );
139
			}
140
141
			$cache_key = self::get_key( $cache_key, $query_args );
142
		}
143
144
		$option = get_option( $cache_key );
145
146
		// Backward compatibility (<1.8.7).
147
		if ( ! is_array( $option ) || empty( $option ) || ! array_key_exists( 'expiration', $option ) ) {
148
			return $option;
149
		}
150
151
		// Get current time.
152
		$current_time = current_time( 'timestamp', 1 );
153
154
		if ( empty( $option['expiration'] ) || ( $current_time < $option['expiration'] ) ) {
155
			$option = $option['data'];
156
		} else {
157
			$option = false;
158
		}
159
160
		return $option;
161
	}
162
163
	/**
164
	 * Set cache.
165
	 *
166
	 * @since  1.8.7
167
	 *
168
	 * @param  string   $cache_key
169
	 * @param  mixed    $data
170
	 * @param  int|null $expiration Timestamp should be in GMT format.
171
	 * @param  bool     $custom_key
172
	 * @param  mixed    $query_args
173
	 *
174
	 * @return mixed
175
	 */
176
177
	public static function set( $cache_key, $data, $expiration = null, $custom_key = false, $query_args = array() ) {
178 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...
179
			if ( ! $custom_key ) {
180
				return new WP_Error( 'give_invalid_cache_key', __( 'Cache key format should be give_cache_*', 'give' ) );
181
			}
182
183
			$cache_key = self::get_key( $cache_key, $query_args );
184
		}
185
186
		$option_value = array(
187
			'data'       => $data,
188
			'expiration' => ! is_null( $expiration )
189
				? ( $expiration + current_time( 'timestamp', 1 ) )
190
				: null,
191
		);
192
193
		$result = update_option( $cache_key, $option_value, 'no' );
194
195
		return $result;
196
	}
197
198
	/**
199
	 * Delete cache.
200
	 *
201
	 * Note: only for internal use
202
	 *
203
	 * @since  1.8.7
204
	 *
205
	 * @param  string|array $cache_keys
206
	 *
207
	 * @return bool|WP_Error
208
	 */
209
210
	public static function delete( $cache_keys ) {
211
		$result       = true;
212
		$invalid_keys = array();
213
214
		if ( ! empty( $cache_keys ) ) {
215
			$cache_keys = is_array( $cache_keys ) ? $cache_keys : array( $cache_keys );
216
217
			foreach ( $cache_keys as $cache_key ) {
218
				if ( ! self::is_valid_cache_key( $cache_key ) ) {
219
					$invalid_keys[] = $cache_key;
220
					$result         = false;
221
				}
222
223
				delete_option( $cache_key );
224
			}
225
		}
226
227
		if ( ! $result ) {
228
			$result = new WP_Error(
229
				'give_invalid_cache_key',
230
				__( 'Cache key format should be give_cache_*', 'give' ),
231
				$invalid_keys
232
			);
233
		}
234
235
		return $result;
236
	}
237
238
	/**
239
	 * Delete all logging cache.
240
	 *
241
	 * Note: only for internal use
242
	 *
243
	 * @since  1.8.7
244
	 * @access public
245
	 * @global wpdb $wpdb
246
	 *
247
	 * @param bool  $force If set to true then all cached values will be delete instead of only expired
248
	 *
249
	 * @return bool
250
	 */
251
	public static function delete_all_expired( $force = false ) {
252
		global $wpdb;
253
		$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...
254
			$wpdb->prepare(
255
				"SELECT option_name, option_value
256
						FROM {$wpdb->options}
257
						Where option_name
258
						LIKE '%%%s%%'",
259
				'give_cache'
260
			),
261
			ARRAY_A
262
		);
263
264
		// Bailout.
265
		if ( empty( $options ) ) {
266
			return false;
267
		}
268
269
		$current_time = current_time( 'timestamp', 1 );
270
271
		// Delete log cache.
272
		foreach ( $options as $option ) {
273
			$option['option_value'] = maybe_unserialize( $option['option_value'] );
274
275
			if (
276
				(
277
					! self::is_valid_cache_key( $option['option_name'] )
278
					|| ! is_array( $option['option_value'] ) // Backward compatibility (<1.8.7).
279
					|| ! array_key_exists( 'expiration', $option['option_value'] ) // Backward compatibility (<1.8.7).
280
					|| empty( $option['option_value']['expiration'] )
281
					|| ( $current_time < $option['option_value']['expiration'] )
282
				)
283
				&& ! $force
284
			) {
285
				continue;
286
			}
287
288
			self::delete( $option['option_name'] );
289
		}
290
	}
291
292
293
	/**
294
	 * Get list of options like.
295
	 *
296
	 * Note: only for internal use
297
	 *
298
	 * @since  1.8.7
299
	 * @access public
300
	 *
301
	 * @param string $option_name
302
	 * @param bool   $fields
303
	 *
304
	 * @return array
305
	 */
306
	public static function get_options_like( $option_name, $fields = false ) {
307
		global $wpdb;
308
309
		if ( empty( $option_name ) ) {
310
			return array();
311
		}
312
313
		$field_names = $fields ? 'option_name, option_value' : 'option_name';
314
315
		if ( $fields ) {
316
			$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...
317
				$wpdb->prepare(
318
					"SELECT {$field_names }
319
						FROM {$wpdb->options}
320
						Where option_name
321
						LIKE '%%%s%%'",
322
					"give_cache_{$option_name}"
323
				),
324
				ARRAY_A
325
			);
326
		} else {
327
			$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...
328
				$wpdb->prepare(
329
					"SELECT *
330
						FROM {$wpdb->options}
331
						Where option_name
332
						LIKE '%%%s%%'",
333
					"give_cache_{$option_name}"
334
				),
335
				1
336
			);
337
		}
338
339
		if ( ! empty( $options ) && $fields ) {
340
			foreach ( $options as $index => $option ) {
341
				$option['option_value'] = maybe_unserialize( $option['option_value'] );
342
				$options[ $index ]      = $option;
343
			}
344
		}
345
346
		return $options;
347
	}
348
349
	/**
350
	 * Check cache key validity.
351
	 *
352
	 * @since  1.8.7
353
	 * @access public
354
	 *
355
	 * @param $cache_key
356
	 *
357
	 * @return bool
358
	 */
359
	public static function is_valid_cache_key( $cache_key ) {
360
		$is_valid = ( false !== strpos( $cache_key, 'give_cache_' ) );
361
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
362
363
		/**
364
		 * Filter the flag which tell about cache key valid or not
365
		 *
366
		 * @since 2.0
367
		 */
368
		return apply_filters( 'give_is_valid_cache_key', $is_valid, $cache_key );
369
	}
370
371
372
	/**
373
	 * Get cache from group
374
	 *
375
	 * @since  2.0
376
	 * @access public
377
	 *
378
	 * @param int    $id
379
	 * @param string $group
380
	 *
381
	 * @return mixed
382
	 */
383
	public static function get_group( $id, $group = '' ) {
384
		$cached_data = false;
385
386
		// Bailout.
387
		if ( ! self::$instance->is_cache || empty( $id ) ) {
388
			return $cached_data;
389
		}
390
391
		$cached_data = wp_cache_get( $id, $group );
392
393
		return $cached_data;
394
	}
395
396
	/**
397
	 * Cache small chunks inside group
398
	 *
399
	 * @since  2.0
400
	 * @access public
401
	 *
402
	 * @param int    $id
403
	 * @param mixed  $data
404
	 * @param string $group
405
	 * @param int    $expire
406
	 *
407
	 * @return bool
408
	 */
409
	public static function set_group( $id, $data, $group = '', $expire = 0 ) {
410
		$status = false;
411
412
		// Bailout.
413
		if ( ! self::$instance->is_cache || empty( $id ) ) {
414
			return $status;
415
		}
416
417
		$status = wp_cache_set( $id, $data, $group, $expire );
418
419
		update_option( 'give-last-cache-updated', current_time( 'timestamp', 1 ) );
420
421
		return $status;
422
	}
423
424
	/**
425
	 * Delete group cache
426
	 *
427
	 * @since  2.0
428
	 * @access public
429
	 *
430
	 * @param int|array $ids
431
	 * @param string    $group
432
	 * @param int       $expire
433
	 *
434
	 * @return bool
435
	 */
436
	public static function delete_group( $ids, $group = '', $expire = 0 ) {
437
		$status = false;
438
439
		// Bailout.
440
		if ( ! self::$instance->is_cache || empty( $ids ) ) {
441
			return $status;
442
		}
443
444
		// Delete single or multiple cache items from cache.
445
		if ( ! is_array( $ids ) ) {
446
			$status = wp_cache_delete( $ids, $group, $expire );
447
448
			/**
449
			 * Fire action when cache deleted for specific id.
450
			 *
451
			 * @since 2.0
452
			 *
453
			 * @param string $ids
454
			 * @param string $group
455
			 * @param int    $expire
456
			 */
457
			do_action( "give_deleted_{$group}_cache", $ids, $group, $expire );
458
459
		} else {
460
			foreach ( $ids as $id ) {
461
				$status = wp_cache_delete( $id, $group, $expire );
462
463
				/**
464
				 * Fire action when cache deleted for specific id .
465
				 *
466
				 * @since 2.0
467
				 *
468
				 * @param string $ids
469
				 * @param string $group
470
				 * @param int    $expire
471
				 */
472
				do_action( "give_deleted_{$group}_cache", $id, $group, $expire );
473
			}
474
		}
475
476
		// Update timestamp in DB when cache update.
477
		update_option( 'give-last-cache-updated', current_time( 'timestamp', 1 ) );
478
479
		return $status;
480
	}
481
482
483
	/**
484
	 * Delete form related cache
485
	 * Note: only use for internal purpose.
486
	 *
487
	 * @since  2.0
488
	 * @access public
489
	 *
490
	 * @param int $form_id
491
	 */
492
	public function delete_form_related_cache( $form_id ) {
493
		// If this is just a revision, don't send the email.
494
		if ( wp_is_post_revision( $form_id ) ) {
495
			return;
496
		}
497
498
		$donation_query = new Give_Payments_Query(
499
			array(
500
				'number'     => - 1,
501
				'give_forms' => $form_id
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
502
			)
503
		);
504
505
		$donations = $donation_query->get_payments();
506
507
		// Bailout.
508
		if ( empty( $donations ) ) {
509
			return;
510
		}
511
512
		/* @var Give_Payment $donation */
513
		foreach ( $donations as $donation ) {
514
			wp_cache_delete( $donation->ID, 'give-donations' );
515
			wp_cache_delete( $donation->donor_id, 'give-donors' );
516
		}
517
	}
518
519
	/**
520
	 * Delete donor related cache
521
	 * Note: only use for internal purpose.
522
	 *
523
	 * @since  2.0
524
	 * @access public
525
	 *
526
	 * @param string $id
527
	 * @param string $group
528
	 * @param int    $expire
529
	 */
530
	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...
531
		$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...
532
		$donation_ids = array_map( 'trim', (array) explode( ',', trim( $donor->payment_ids ) ) );
533
534
		// bailout.
535
		if ( empty( $donation_ids ) ) {
536
			return;
537
		}
538
539
		foreach ( $donation_ids as $donation ) {
540
			wp_cache_delete( $donation, 'give-donations' );
541
		}
542
	}
543
544
	/**
545
	 * Delete donations related cache
546
	 * Note: only use for internal purpose.
547
	 *
548
	 * @since  2.0
549
	 * @access public
550
	 *
551
	 * @param string $id
552
	 * @param string $group
553
	 * @param int    $expire
554
	 */
555
	public function delete_donations_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...
556
		/* @var Give_Payment $donation */
557
		$donation = new Give_Payment( $id );
558
559
		if ( $donation && $donation->donor_id ) {
560
			wp_cache_delete( $donation->donor_id, 'give-donors' );
561
		}
562
	}
563
}
564
565
// Initialize
566
Give_Cache::get_instance()->setup();
567