Completed
Push — add/sync-partial-sync-checksum... ( e921de...304861 )
by
unknown
76:02 queued 67:35
created

Table_Checksum   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 589
Duplicated Lines 1.02 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
dl 6
loc 589
rs 8.48
c 0
b 0
f 0
wmc 49
lcom 2
cbo 2

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
B get_default_tables() 0 63 1
A prepare_fields() 0 8 3
A validate_table_name() 0 13 3
A validate_fields() 0 9 3
A validate_fields_against_table() 0 18 4
A validate_input() 0 6 1
B prepare_filter_values_as_sql() 0 28 6
B build_filter_statement() 6 55 9
B build_checksum_query() 0 60 4
B get_range_edges() 0 64 6
A prepare_results_for_output() 0 19 3
A calculate_checksum() 0 33 5

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 Table_Checksum 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 Table_Checksum, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Table Checksums Class.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync\Replicastore;
9
10
use Automattic\Jetpack\Sync\Settings;
11
use Exception;
12
use WP_Error;
13
14
// TODO add rest endpoints to work with this, hopefully in the same folder.
15
/**
16
 * Class to handle Table Checksums.
17
 */
18
class Table_Checksum {
19
20
	/**
21
	 * Table to be checksummed.
22
	 *
23
	 * @var string
24
	 */
25
	public $table = '';
26
27
	/**
28
	 * Table Checksum Configuration.
29
	 *
30
	 * @var array
31
	 */
32
	public $table_configuration = array();
33
34
	/**
35
	 * Field to be used for range queries.
36
	 *
37
	 * @var string
38
	 */
39
	public $range_field = '';
40
41
	/**
42
	 * ID Field(s) to be used.
43
	 *
44
	 * @var array
45
	 */
46
	public $key_fields = array();
47
48
	/**
49
	 * Field(s) to be used in generating the checksum value.
50
	 *
51
	 * @var array
52
	 */
53
	public $checksum_fields = array();
54
55
	/**
56
	 * Default filter values for the table
57
	 *
58
	 * @var array
59
	 */
60
	public $filter_values = array();
61
62
	/**
63
	 * SQL Query to be used to filter results (allow/disallow).
64
	 *
65
	 * @var string
66
	 */
67
	public $additional_filter_sql = '';
68
69
	/**
70
	 * Default Checksum Table Configurations.
71
	 *
72
	 * @var array
73
	 */
74
	public $default_tables = array();
75
76
	/**
77
	 * Salt to be used when generating checksum.
78
	 *
79
	 * @var string
80
	 */
81
	public $salt = '';
82
83
	/**
84
	 * Tables which are allowed to be checksummed.
85
	 *
86
	 * @var string
87
	 */
88
	public $allowed_tables = array();
89
90
	/**
91
	 * If the table has a "parent" table that it's related to.
92
	 *
93
	 * @var mixed|null
94
	 */
95
	private $parent_table = null;
96
97
	/**
98
	 * Table_Checksum constructor.
99
	 *
100
	 * @param string $table The table to calculate checksums for.
101
	 * @param string $salt  Optional salt to add to the checksum.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $salt not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
102
	 *
103
	 * @throws Exception Throws exception from inner functions.
104
	 */
105
	public function __construct( $table, $salt = null ) {
106
		$this->salt = $salt;
107
108
		$this->default_tables = $this->get_default_tables();
109
110
		// TODO change filters to allow the array format.
111
		// TODO add get_fields or similar method to get things out of the table.
112
		// TODO extract this configuration in a better way, still make it work with `$wpdb` names.
113
		// TODO take over the replicastore functions and move them over to this class.
114
		// TODO make the API work.
115
116
		$this->allowed_tables = apply_filters( 'jetpack_sync_checksum_allowed_tables', $this->default_tables );
117
118
		$this->table               = $this->validate_table_name( $table );
119
		$this->table_configuration = $this->allowed_tables[ $table ];
120
121
		$this->prepare_fields( $this->table_configuration );
122
123
	}
124
125
	/**
126
	 * Get Default Table configurations.
127
	 *
128
	 * @return array
129
	 */
130
	private function get_default_tables() {
131
		global $wpdb;
132
133
		return array(
134
			'posts'              => array(
135
				'table'           => $wpdb->posts,
136
				'range_field'     => 'ID',
137
				'key_fields'      => array( 'ID' ),
138
				'checksum_fields' => array( 'post_modified_gmt' ),
139
				'filter_values'   => Settings::get_disallowed_post_types_structured(),
140
			),
141
			'postmeta'           => array(
142
				'table'           => $wpdb->postmeta,
143
				'range_field'     => 'post_id',
144
				'key_fields'      => array( 'post_id', 'meta_key' ),
145
				'checksum_fields' => array( 'meta_key', 'meta_value' ),
146
				'filter_values'   => Settings::get_allowed_post_meta_structured(),
147
				'parent_table'    => 'posts',
148
			),
149
			'comments'           => array(
150
				'table'           => $wpdb->comments,
151
				'range_field'     => 'comment_ID',
152
				'key_fields'      => array( 'comment_ID' ),
153
				'checksum_fields' => array( 'comment_content' ),
154
				'filter_sql'      => Settings::get_comments_filter_sql(),
155
			),
156
			'commentmeta'        => array(
157
				'table'           => $wpdb->commentmeta,
158
				'range_field'     => 'comment_id',
159
				'key_fields'      => array( 'comment_id', 'meta_key' ),
160
				'checksum_fields' => array( 'meta_key', 'meta_value' ),
161
				'filter_values'   => Settings::get_allowed_comment_meta_structured(),
162
				'parent_table'    => 'comments',
163
			),
164
			'terms'              => array(
165
				'table'           => $wpdb->terms,
166
				'range_field'     => 'term_id',
167
				'key_fields'      => array( 'term_id' ),
168
				'checksum_fields' => array( 'term_id', 'name', 'slug' ),
169
			),
170
			'termmeta'           => array(
171
				'table'           => $wpdb->termmeta,
172
				'range_field'     => 'term_id',
173
				'key_fields'      => array( 'term_id', 'meta_key' ),
174
				'checksum_fields' => array( 'meta_key', 'meta_value' ),
175
				'parent_table'    => 'terms',
176
			),
177
			'term_relationships' => array(
178
				'table'           => $wpdb->term_relationships,
179
				'range_field'     => 'object_id',
180
				'key_fields'      => array( 'object_id' ),
181
				'checksum_fields' => array( 'object_id', 'term_taxonomy_id' ),
182
			),
183
			'term_taxonomy'      => array(
184
				'table'           => $wpdb->term_taxonomy,
185
				'range_field'     => 'term_taxonomy_id',
186
				'key_fields'      => array( 'term_taxonomy_id' ),
187
				'checksum_fields' => array( 'term_taxonomy_id', 'term_id', 'taxonomy', 'description', 'parent' ),
188
			),
189
			'links'              => $wpdb->links, // TODO describe in the array format or add exceptions.
190
			'options'            => $wpdb->options, // TODO describe in the array format or add exceptions.
191
		);
192
	}
193
194
	/**
195
	 * Prepare field params based off provided configuration.
196
	 *
197
	 * @param array $table_configuration The table configuration array.
198
	 */
199
	private function prepare_fields( $table_configuration ) {
200
		$this->key_fields            = $table_configuration['key_fields'];
201
		$this->range_field           = $table_configuration['range_field'];
202
		$this->checksum_fields       = $table_configuration['checksum_fields'];
203
		$this->filter_values         = $table_configuration['filter_values'];
204
		$this->additional_filter_sql = ! empty( $table_configuration['filter_sql'] ) ? $table_configuration['filter_sql'] : '';
205
		$this->parent_table          = isset( $table_configuration['parent_table'] ) ? $table_configuration['parent_table'] : null;
206
	}
207
208
	/**
209
	 * Verify provided table name is valid for checksum processing.
210
	 *
211
	 * @param string $table Table name to validate.
212
	 *
213
	 * @return mixed|string
214
	 * @throws Exception Throw an exception on validation failure.
215
	 */
216
	private function validate_table_name( $table ) {
217
		if ( empty( $table ) ) {
218
			throw new Exception( 'Invalid table name: empty' );
219
		}
220
221
		if ( ! array_key_exists( $table, $this->allowed_tables ) ) {
222
			throw new Exception( "Invalid table name: $table not allowed" );
223
		}
224
225
		// TODO other checks if such are needed.
226
227
		return $this->allowed_tables[ $table ]['table'];
228
	}
229
230
	/**
231
	 * Verify provided fields are proper names.
232
	 *
233
	 * @param array $fields Array of field names to validate.
234
	 *
235
	 * @throws Exception Throw an exception on failure to validate.
236
	 */
237
	private function validate_fields( $fields ) {
238
		foreach ( $fields as $field ) {
239
			if ( ! preg_match( '/^[0-9,a-z,A-Z$_]+$/i', $field ) ) {
240
				throw new Exception( "Invalid field name: $field is not allowed" );
241
			}
242
243
			// TODO other verifications of the field names.
244
		}
245
	}
246
247
	/**
248
	 * Verify the fields exist in the table.
249
	 *
250
	 * @param array $fields Array of fields to validate.
251
	 *
252
	 * @return bool
253
	 * @throws Exception Throw an exception on failure to validate.
254
	 */
255
	private function validate_fields_against_table( $fields ) {
256
		global $wpdb;
257
258
		// TODO: Is this safe enough?
259
		$result = $wpdb->get_row( "SELECT * FROM {$this->table} LIMIT 1", ARRAY_A );
260
		if ( ! is_array( $result ) ) {
261
			throw new Exception( 'Unexpected $wpdb->query output: not array' );
262
		}
263
264
		// Check if the fields are actually contained in the table.
265
		foreach ( $fields as $field_to_check ) {
266
			if ( ! array_key_exists( $field_to_check, $result ) ) {
267
				throw new Exception( "Invalid field name: field '{$field_to_check}' doesn't exist in table {$this->table}" );
268
			}
269
		}
270
271
		return true;
272
	}
273
274
	/**
275
	 * Verify the configured fields.
276
	 *
277
	 * @throws Exception Throw an exception on failure to validate in the internal functions.
278
	 */
279
	private function validate_input() {
280
		$fields = array_merge( array( $this->range_field ), $this->key_fields, $this->checksum_fields );
281
282
		$this->validate_fields( $fields );
283
		$this->validate_fields_against_table( $fields );
284
	}
285
286
	/**
287
	 * Prepare filter values as SQL statements to be added to the other filters.
288
	 *
289
	 * @param array  $filter_values The filter values array.
290
	 * @param string $table_prefix  If the values are going to be used in a sub-query, add a prefix with the table alias.
291
	 *
292
	 * @return array|null
293
	 */
294
	private function prepare_filter_values_as_sql( $filter_values = array(), $table_prefix = '' ) {
295
		global $wpdb;
296
297
		if ( ! is_array( $filter_values ) ) {
298
			return null;
299
		}
300
301
		$result = array();
302
303
		foreach ( $filter_values as $field => $filter ) {
304
			$key = ( $table_prefix ? $table_prefix . '.' : '' ) . $field;
305
306
			switch ( $filter['operator'] ) {
307
				case 'IN':
308
				case 'NOT IN':
309
					$values_placeholders = implode( ',', array_fill( 0, count( $filter['values'] ), '%s' ) );
310
311
					$statement          = "{$key} {$filter['operator']} ( $values_placeholders )";
312
					$prepared_statement = $wpdb->prepare( $statement, $filter['values'] );
313
314
					$result[] = $prepared_statement;
315
					break;
316
				// TODO implement other operators if needed.
317
			}
318
		}
319
320
		return $result;
321
	}
322
323
	/**
324
	 * Build the filter query baased off range fields and values and the additional sql.
325
	 *
326
	 * @param int|null   $range_from    Start of the range.
327
	 * @param int|null   $range_to      End of the range.
328
	 * @param array|null $filter_values Additional filter values. Not used at the moment.
329
	 * @param string     $table_prefix  Table name to be prefixed to the columns. Used in sub-queries where columns can clash.
330
	 *
331
	 * @return string
332
	 */
333
	public function build_filter_statement( $range_from = null, $range_to = null, $filter_values = null, $table_prefix = '' ) {
334
		global $wpdb;
335
336
		// If there is a field prefix that we want to use with table aliases.
337
		$parent_prefix = ! empty( $table_prefix ) ? $table_prefix . '.' : '';
0 ignored issues
show
Unused Code introduced by
$parent_prefix is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
338
339
		/**
340
		 * Prepare the ranges.
341
		 */
342
343
		$filter_array = array();
344 View Code Duplication
		if ( null !== $range_from ) {
345
			$filter_array[] = $wpdb->prepare( "{$this->range_field} >= %d", array( intval( $range_from ) ) );
346
		}
347 View Code Duplication
		if ( null != $range_to ) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $range_to of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
348
			$filter_array[] = $wpdb->prepare( "{$this->range_field} <= %d", array( intval( $range_to ) ) );
349
		}
350
351
		/**
352
		 * End prepare the ranges.
353
		 */
354
355
		/**
356
		 * Prepare data filters.
357
		 */
358
359
		// Default filters.
360
		if ( $this->filter_values ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->filter_values of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
361
			$prepared_values_statements = $this->prepare_filter_values_as_sql( $this->filter_values, $table_prefix );
362
			if ( $prepared_values_statements ) {
363
				$filter_array = array_merge( $filter_array, $prepared_values_statements );
364
			}
365
		}
366
367
		// Additional filters.
368
		if ( ! empty( $filter_values ) ) {
369
			// Prepare filtering.
370
			$prepared_values_statements = $this->prepare_filter_values_as_sql( $filter_values, $table_prefix );
371
			if ( $prepared_values_statements ) {
372
				$filter_array = array_merge( $filter_array, $prepared_values_statements );
373
			}
374
		}
375
376
		// Add any additional filters via direct SQL statement.
377
		// Currently used only because we haven't converted all filtering to happen via `filter_values`.
378
		// This SQL is NOT prefixed and column clashes can occur when used in sub-queries.
379
		if ( $this->additional_filter_sql ) {
380
			$filter_array[] = $this->additional_filter_sql;
381
		}
382
383
		/**
384
		 * End prepare data filters.
385
		 */
386
		return implode( ' AND ', $filter_array );
387
	}
388
389
	/**
390
	 * Returns the checksum query. All validation of fields and configurations are expected to occur prior to usage.
391
	 *
392
	 * @param int|null   $range_from      The start of the range.
393
	 * @param int|null   $range_to        The end of the range.
394
	 * @param array|null $filter_values   Additional filter values. Not used at the moment.
395
	 * @param bool       $granular_result If the function should return a granular result.
396
	 *
397
	 * @return string
398
	 *
399
	 * @throws Exception Throws and exception if validation fails in the internal function calls.
400
	 */
401
	private function build_checksum_query( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false ) {
402
		global $wpdb;
403
404
		// Escape the salt.
405
		$salt = $wpdb->prepare( '%s', $this->salt ); // TODO escape or prepare statement.
406
407
		// Prepare the compound key.
408
		$key_fields = implode( ',', $this->key_fields );
409
410
		// Prepare the checksum fields.
411
		$checksum_fields_string = implode( ',', array_merge( $this->checksum_fields, array( $salt ) ) );
412
413
		$additional_fields = '';
414
		if ( $granular_result ) {
415
			// TODO uniq the fields as sometimes(most) range_index is the key and there's no need to select the same field twice.
416
			$additional_fields = "
417
				{$this->range_field} as range_index,
418
			    {$key_fields},
419
			";
420
		}
421
422
		$filter_stamenet = $this->build_filter_statement( $range_from, $range_to, $filter_values );
423
424
		$join_statement = '';
425
		if ( $this->parent_table ) {
426
			$parent_table_obj    = new Table_Checksum( $this->parent_table );
427
			$parent_filter_query = $parent_table_obj->build_filter_statement( $range_from, $range_to, null, 'parent_table' );
428
429
			$join_statement = "
430
				INNER JOIN {$parent_table_obj->table} as parent_table ON ({$this->table}.{$this->range_field} = parent_table.{$parent_table_obj->range_field} AND {$parent_filter_query})
431
			";
432
		}
433
434
		$query = "
435
			SELECT
436
				{$additional_fields}
437
				SUM(
438
					CRC32(
439
						CONCAT_WS( '#', {$salt}, {$checksum_fields_string} )
440
					)
441
				)  AS checksum
442
			 FROM
443
			    {$this->table}
444
				{$join_statement}
445
			 WHERE
446
				{$filter_stamenet}
447
		";
448
449
		/**
450
		 * We need the GROUP BY only for compound keys.
451
		 */
452
		if ( $granular_result ) {
453
			$query .= "
454
				GROUP BY {$key_fields}
455
			";
456
		}
457
458
		return $query;
459
460
	}
461
462
	/**
463
	 * Obtain the min-max values (edges) of the range.
464
	 *
465
	 * @param int|null $range_from The start of the range.
466
	 * @param int|null $range_to   The end of the range.
467
	 * @param int|null $limit      How many values to return.
468
	 *
469
	 * @return array|object|void
470
	 * @throws Exception Throws an exception if validation fails on the internal function calls.
471
	 */
472
	public function get_range_edges( $range_from = null, $range_to = null, $limit = null ) {
473
		global $wpdb;
474
475
		$this->validate_fields( array( $this->range_field ) );
476
477
		// `trim()` to make sure we don't add the statement if it's empty.
478
		$filters = trim( $this->build_filter_statement( $range_from, $range_to ) );
479
480
		$filter_statement = '';
481
		if ( ! empty( $filters ) ) {
482
			$filter_statement = "
483
				WHERE
484
					{$filters}
485
			";
486
		}
487
488
		// Only make the distinct count when we know there can be multiple entries for the range column.
489
		$distinct_count = count( $this->key_fields ) > 1 ? 'DISTINCT' : '';
490
491
		$query = "
492
			SELECT
493
			       MIN({$this->range_field}) as min_range,
494
			       MAX({$this->range_field}) as max_range,
495
			       COUNT( {$distinct_count} {$this->range_field}) as item_count
496
			FROM
497
		";
498
499
		/**
500
		 * If `$limit` is not specified, we can directly use the table.
501
		 */
502
		if ( ! $limit ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
503
			$query .= "
504
				{$this->table}
505
	            {$filter_statement}
506
			";
507
		} else {
508
			/**
509
			 * If there is `$limit` specified, we can't directly use `MIN/MAX()` as they don't work with `LIMIT`.
510
			 * That's why we will alter the query for this case.
511
			 */
512
			$limit = intval( $limit );
513
514
			$query .= "
515
				(
516
					SELECT
517
						{$distinct_count} {$this->range_field}
518
					FROM
519
						{$this->table}
520
						{$filter_statement}
521
					ORDER BY
522
						{$this->range_field} ASC
523
					LIMIT {$limit}
524
				) as ids_query
525
			";
526
		}
527
528
		$result = $wpdb->get_row( $query, ARRAY_A );
529
530
		if ( ! $result || ! is_array( $result ) ) {
531
			throw new Exception( 'Unable to get range edges' );
532
		}
533
534
		return $result;
535
	}
536
537
	/**
538
	 * Update the results to have key/checksum format.
539
	 *
540
	 * @param array $results Prepare the results for output of granular results.
541
	 */
542
	public function prepare_results_for_output( &$results ) {
543
		// get the compound key.
544
		// only return range and compound key for granular results.
545
546
		foreach ( $results as &$result ) {
547
			// Working on reference to save memory here.
548
549
			$key = array();
550
			foreach ( $this->key_fields as $field ) {
551
				$key[] = $result[ $field ];
552
			}
553
554
			$result = array(
555
				'key'      => implode( '-', $key ),
556
				'checksum' => $result['checksum'],
557
			);
558
559
		}
560
	}
561
562
	/**
563
	 * Calculate the checksum based on provided range and filters.
564
	 *
565
	 * @param int|null   $range_from          The start of the range.
566
	 * @param int|null   $range_to            The end of the range.
567
	 * @param array|null $filter_values       Additional filter values. Not used at the moment.
568
	 * @param bool       $granular_result     If the returned result should be granular or only the checksum.
569
	 * @param bool       $simple_return_value If we want to use a simple return value for non-granular results (return only the checksum, without wrappers).
570
	 *
571
	 * @return array|mixed|object|WP_Error|null
572
	 */
573
	public function calculate_checksum( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false, $simple_return_value = true ) {
574
		try {
575
			$this->validate_input();
576
		} catch ( Exception $ex ) {
577
			return new WP_Error( 'invalid_input', $ex->getMessage() );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_input'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
578
		}
579
580
		$query = $this->build_checksum_query( $range_from, $range_to, $filter_values, $granular_result );
581
582
		global $wpdb;
583
584
		if ( ! $granular_result ) {
585
			$result = $wpdb->get_row( $query, ARRAY_A );
586
587
			if ( ! is_array( $result ) ) {
588
				return new WP_Error( 'invalid_query', "Result wasn't an array" );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_query'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
589
			}
590
591
			if ( $simple_return_value ) {
592
				return $result['checksum'];
593
			}
594
595
			return array(
596
				'range'    => $range_from . '-' . $range_to,
597
				'checksum' => $result['checksum'],
598
			);
599
		} else {
600
			$result = $wpdb->get_results( $query, ARRAY_A );
601
			$this->prepare_results_for_output( $result );
602
603
			return $result;
604
		}
605
	}
606
}
607