Test Failed
Push — issues/1944 ( b2d497...3305a3 )
by Ravinder
04:14
created

Give_Cache   C

Complexity

Total Complexity 60

Size/Duplication

Total Lines 457
Duplicated Lines 3.06 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
dl 14
loc 457
rs 6.0975
c 0
b 0
f 0
wmc 60
lcom 2
cbo 2

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A get_instance() 0 7 3
A setup() 0 6 1
B get_key() 0 31 5
C get() 7 27 8
A set() 7 20 4
B delete() 0 27 6
D delete_all_expired() 0 40 9
C get_options_like() 0 42 7
A is_valid_cache_key() 0 11 1
A get_group() 0 16 4
A set_group() 0 18 4
C delete_group() 0 34 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Give_Cache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Give_Cache, and based on these observations, apply Extract Interface, too.

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
76
	/**
77
	 * Get cache key.
78
	 *
79
	 * @since  1.8.7
80
	 *
81
	 * @param  string $action     Cache key prefix.
82
	 * @param  array  $query_args (optional) Query array.
83
	 *
84
	 * @return string|WP_Error
85
	 */
86
87
	public static function get_key( $action, $query_args = null ) {
88
		// Bailout.
89
		if ( empty( $action ) ) {
90
			return new WP_Error( 'give_invalid_cache_key_action', __( 'Do not pass empty action to generate cache key.', 'give' ) );
91
		}
92
93
		// Handle specific cache key prefix.
94
		// @see https://core.trac.wordpress.org/ticket/4476
95
		if ( 'give-db-queries' === $action ) {
96
			$timestamp = get_option( 'give-last-cache-updated' );
97
			$timestamp = empty( $timestamp ) ? current_time( 'timestamp', 1 ) : $timestamp;
98
99
			return "give-db-queries-{$timestamp}";
100
		}
101
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
102
103
		// Set cache key.
104
		$cache_key = "give_cache_{$action}";
105
106
		// Bailout.
107
		if ( ! empty( $query_args ) ) {
108
			$cache_key = "{$cache_key}_" . substr( md5( serialize( $query_args ) ), 0, 15 );
109
		}
110
111
		/**
112
		 * Filter the cache key name.
113
		 *
114
		 * @since 2.0
115
		 */
116
		return apply_filters( 'give_get_cache_key', $cache_key, $action, $query_args );
117
	}
118
119
	/**
120
	 * Get cache.
121
	 *
122
	 * @since  1.8.7
123
	 *
124
	 * @param  string $cache_key
125
	 * @param  bool   $custom_key
126
	 * @param  mixed  $query_args
127
	 *
128
	 * @return mixed
129
	 */
130
131
	public static function get( $cache_key, $custom_key = false, $query_args = array() ) {
132 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...
133
			if ( ! $custom_key ) {
134
				return new WP_Error( 'give_invalid_cache_key', __( 'Cache key format should be give_cache_*', 'give' ) );
135
			}
136
137
			$cache_key = self::get_key( $cache_key, $query_args );
138
		}
139
140
		$option = get_option( $cache_key );
141
142
		// Backward compatibility (<1.8.7).
143
		if ( ! is_array( $option ) || empty( $option ) || ! array_key_exists( 'expiration', $option ) ) {
144
			return $option;
145
		}
146
147
		// Get current time.
148
		$current_time = current_time( 'timestamp', 1 );
149
150
		if ( empty( $option['expiration'] ) || ( $current_time < $option['expiration'] ) ) {
151
			$option = $option['data'];
152
		} else {
153
			$option = false;
154
		}
155
156
		return $option;
157
	}
158
159
	/**
160
	 * Set cache.
161
	 *
162
	 * @since  1.8.7
163
	 *
164
	 * @param  string   $cache_key
165
	 * @param  mixed    $data
166
	 * @param  int|null $expiration Timestamp should be in GMT format.
167
	 * @param  bool     $custom_key
168
	 * @param  mixed    $query_args
169
	 *
170
	 * @return mixed
171
	 */
172
173
	public static function set( $cache_key, $data, $expiration = null, $custom_key = false, $query_args = array() ) {
174 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...
175
			if ( ! $custom_key ) {
176
				return new WP_Error( 'give_invalid_cache_key', __( 'Cache key format should be give_cache_*', 'give' ) );
177
			}
178
179
			$cache_key = self::get_key( $cache_key, $query_args );
180
		}
181
182
		$option_value = array(
183
			'data'       => $data,
184
			'expiration' => ! is_null( $expiration )
185
				? ( $expiration + current_time( 'timestamp', 1 ) )
186
				: null,
187
		);
188
189
		$result = update_option( $cache_key, $option_value, 'no' );
190
191
		return $result;
192
	}
193
194
	/**
195
	 * Delete cache.
196
	 *
197
	 * Note: only for internal use
198
	 *
199
	 * @since  1.8.7
200
	 *
201
	 * @param  string|array $cache_keys
202
	 *
203
	 * @return bool|WP_Error
204
	 */
205
206
	public static function delete( $cache_keys ) {
207
		$result       = true;
208
		$invalid_keys = array();
209
210
		if ( ! empty( $cache_keys ) ) {
211
			$cache_keys = is_array( $cache_keys ) ? $cache_keys : array( $cache_keys );
212
213
			foreach ( $cache_keys as $cache_key ) {
214
				if ( ! self::is_valid_cache_key( $cache_key ) ) {
215
					$invalid_keys[] = $cache_key;
216
					$result         = false;
217
				}
218
219
				delete_option( $cache_key );
220
			}
221
		}
222
223
		if ( ! $result ) {
224
			$result = new WP_Error(
225
				'give_invalid_cache_key',
226
				__( 'Cache key format should be give_cache_*', 'give' ),
227
				$invalid_keys
228
			);
229
		}
230
231
		return $result;
232
	}
233
234
	/**
235
	 * Delete all logging cache.
236
	 *
237
	 * Note: only for internal use
238
	 *
239
	 * @since  1.8.7
240
	 * @access public
241
	 * @global wpdb $wpdb
242
	 *
243
	 * @param bool  $force If set to true then all cached values will be delete instead of only expired
244
	 *
245
	 * @return bool
246
	 */
247
	public static function delete_all_expired( $force = false ) {
248
		global $wpdb;
249
		$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...
250
			$wpdb->prepare(
251
				"SELECT option_name, option_value
252
						FROM {$wpdb->options}
253
						Where option_name
254
						LIKE '%%%s%%'",
255
				'give_cache'
256
			),
257
			ARRAY_A
258
		);
259
260
		// Bailout.
261
		if ( empty( $options ) ) {
262
			return false;
263
		}
264
265
		$current_time = current_time( 'timestamp', 1 );
266
267
		// Delete log cache.
268
		foreach ( $options as $option ) {
269
			$option['option_value'] = maybe_unserialize( $option['option_value'] );
270
271
			if (
272
				(
273
					! self::is_valid_cache_key( $option['option_name'] )
274
					|| ! is_array( $option['option_value'] ) // Backward compatibility (<1.8.7).
275
					|| ! array_key_exists( 'expiration', $option['option_value'] ) // Backward compatibility (<1.8.7).
276
					|| empty( $option['option_value']['expiration'] )
277
					|| ( $current_time < $option['option_value']['expiration'] )
278
				)
279
				&& ! $force
280
			) {
281
				continue;
282
			}
283
284
			self::delete( $option['option_name'] );
285
		}
286
	}
287
288
289
	/**
290
	 * Get list of options like.
291
	 *
292
	 * Note: only for internal use
293
	 *
294
	 * @since  1.8.7
295
	 * @access public
296
	 *
297
	 * @param string $option_name
298
	 * @param bool   $fields
299
	 *
300
	 * @return array
301
	 */
302
	public static function get_options_like( $option_name, $fields = false ) {
303
		global $wpdb;
304
305
		if ( empty( $option_name ) ) {
306
			return array();
307
		}
308
309
		$field_names = $fields ? 'option_name, option_value' : 'option_name';
310
311
		if ( $fields ) {
312
			$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...
313
				$wpdb->prepare(
314
					"SELECT {$field_names }
315
						FROM {$wpdb->options}
316
						Where option_name
317
						LIKE '%%%s%%'",
318
					"give_cache_{$option_name}"
319
				),
320
				ARRAY_A
321
			);
322
		} else {
323
			$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...
324
				$wpdb->prepare(
325
					"SELECT *
326
						FROM {$wpdb->options}
327
						Where option_name
328
						LIKE '%%%s%%'",
329
					"give_cache_{$option_name}"
330
				),
331
				1
332
			);
333
		}
334
335
		if ( ! empty( $options ) && $fields ) {
336
			foreach ( $options as $index => $option ) {
337
				$option['option_value'] = maybe_unserialize( $option['option_value'] );
338
				$options[ $index ]      = $option;
339
			}
340
		}
341
342
		return $options;
343
	}
344
345
	/**
346
	 * Check cache key validity.
347
	 *
348
	 * @since  1.8.7
349
	 * @access public
350
	 *
351
	 * @param $cache_key
352
	 *
353
	 * @return bool
354
	 */
355
	public static function is_valid_cache_key( $cache_key ) {
356
		$is_valid = ( false !== strpos( $cache_key, 'give_cache_' ) );
357
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
358
359
		/**
360
		 * Filter the flag which tell about cache key valid or not
361
		 *
362
		 * @since 2.0
363
		 */
364
		return apply_filters( 'give_is_valid_cache_key', $is_valid, $cache_key );
365
	}
366
367
368
	/**
369
	 * Get cache from group
370
	 *
371
	 * @since  2.0
372
	 * @access public
373
	 *
374
	 * @param int    $id
375
	 * @param string $group
376
	 *
377
	 * @return mixed
378
	 */
379
	public static function get_group( $id, $group = '' ) {
380
		$cached_data = false;
381
382
		// Bailout.
383
		if (
384
			! self::$instance->is_cache ||
385
			empty( $id ) ||
386
			empty( $cache_type )
387
		) {
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 (
414
			! self::$instance->is_cache ||
415
			empty( $id ) ||
416
			empty( $cache_type )
417
		) {
418
			return $status;
419
		}
420
421
		$status = wp_cache_set( $id, $data, $group, $expire );
422
423
		update_option( 'give-last-cache-updated', current_time( 'timestamp', 1 ) );
424
425
		return $status;
426
	}
427
428
	/**
429
	 * Delete group cache
430
	 *
431
	 * @since  2.0
432
	 * @access public
433
	 *
434
	 * @param int    $id
435
	 * @param string $group
436
	 * @param int    $expire
437
	 *
438
	 * @return bool
439
	 */
440
	public static function delete_group( $id, $group = '', $expire = 0 ) {
441
		$status = false;
442
443
		// Bailout.
444
		if (
445
			! self::$instance->is_cache ||
446
			empty( $id ) ||
447
			empty( $cache_type )
448
		) {
449
			return $status;
450
		}
451
452
		$status = wp_cache_delete( $id, $group, $expire );
453
454
		// Perform action when specific cache deleted.
455
		// @todo: move this code to async task.
456
		switch( $group ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
457
			case 'give-donors':
458
				$donor = new Give_Donor( $id );
0 ignored issues
show
Documentation introduced by
$id is of type integer, 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...
459
				$payment_ids = array_map('trim', (array) explode( ',', trim( $donor->payment_ids ) ) );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
460
461
				if( ! empty( $payment_ids ) ) {
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
462
					foreach ( $payment_ids as $payment_id ) {
463
						wp_cache_delete( $payment_id, 'give-donations' );
464
					}
465
				}
466
		}
467
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
468
469
		// Update timestamp in DB when cache update.
470
		update_option( 'give-last-cache-updated', current_time( 'timestamp', 1 ) );
471
472
		return $status;
473
	}
474
}
475
476
// Initialize
477
Give_Cache::get_instance()->setup();
478
479
// @todo Check if we can implement GIVE_CACHE for persistent and non-persistent cache.
480