Test Failed
Push — issues/1944 ( 134d99...df1c08 )
by Ravinder
05:33
created

Give_Cache::update_cache_version()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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