Test Failed
Push — issues/1944 ( 57386e...a22d42 )
by Ravinder
05:19
created

Give_Cache::delete_payment_related_cache()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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