Completed
Push — fix/sync-checksum-taxonomy-bla... ( 17cf00 )
by
unknown
126:53 queued 118:11
created

Table_Checksum::prepare_results_for_output()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
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;
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
	 * What field to use for the parent table join, if it has a "parent" table.
99
	 *
100
	 * @var mixed|null
101
	 */
102
	private $parent_join_field = null;
103
104
	/**
105
	 * What field to use for the table join, if it has a "parent" table.
106
	 *
107
	 * @var mixed|null
108
	 */
109
	private $table_join_field = null;
110
111
	/**
112
	 * Table_Checksum constructor.
113
	 *
114
	 * @param string $table The table to calculate checksums for.
115
	 * @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...
116
	 *
117
	 * @throws Exception Throws exception from inner functions.
118
	 */
119
	public function __construct( $table, $salt = null ) {
120
121
		if ( ! Sync\Settings::is_checksum_enabled() ) {
122
			throw new Exception( 'Checksums are currently disabled.' );
123
		}
124
125
		$this->salt = $salt;
126
127
		$this->default_tables = $this->get_default_tables();
128
129
		// TODO change filters to allow the array format.
130
		// TODO add get_fields or similar method to get things out of the table.
131
		// TODO extract this configuration in a better way, still make it work with `$wpdb` names.
132
		// TODO take over the replicastore functions and move them over to this class.
133
		// TODO make the API work.
134
135
		$this->allowed_tables = apply_filters( 'jetpack_sync_checksum_allowed_tables', $this->default_tables );
136
137
		$this->table               = $this->validate_table_name( $table );
138
		$this->table_configuration = $this->allowed_tables[ $table ];
139
140
		$this->prepare_fields( $this->table_configuration );
141
142
	}
143
144
	/**
145
	 * Get Default Table configurations.
146
	 *
147
	 * @return array
148
	 */
149
	private function get_default_tables() {
150
		global $wpdb;
151
152
		return array(
153
			'posts'              => array(
154
				'table'           => $wpdb->posts,
155
				'range_field'     => 'ID',
156
				'key_fields'      => array( 'ID' ),
157
				'checksum_fields' => array( 'post_modified_gmt' ),
158
				'filter_values'   => Sync\Settings::get_disallowed_post_types_structured(),
159
			),
160
			'postmeta'           => array(
161
				'table'             => $wpdb->postmeta,
162
				'range_field'       => 'post_id',
163
				'key_fields'        => array( 'post_id', 'meta_key' ),
164
				'checksum_fields'   => array( 'meta_key', 'meta_value' ),
165
				'filter_values'     => Sync\Settings::get_allowed_post_meta_structured(),
166
				'parent_table'      => 'posts',
167
				'parent_join_field' => 'ID',
168
				'table_join_field'  => 'post_id',
169
			),
170
			'comments'           => array(
171
				'table'           => $wpdb->comments,
172
				'range_field'     => 'comment_ID',
173
				'key_fields'      => array( 'comment_ID' ),
174
				'checksum_fields' => array( 'comment_content' ),
175
				'filter_values'   => array(
176
					'comment_type'     => array(
177
						'operator' => 'IN',
178
						'values'   => apply_filters(
179
							'jetpack_sync_whitelisted_comment_types',
180
							array( '', 'comment', 'trackback', 'pingback', 'review' )
181
						),
182
					),
183
					'comment_approved' => array(
184
						'operator' => '<>',
185
						'values'   => array( 'spam' ),
186
					),
187
				),
188
			),
189
			'commentmeta'        => array(
190
				'table'             => $wpdb->commentmeta,
191
				'range_field'       => 'comment_id',
192
				'key_fields'        => array( 'comment_id', 'meta_key' ),
193
				'checksum_fields'   => array( 'meta_key', 'meta_value' ),
194
				'filter_values'     => Sync\Settings::get_allowed_comment_meta_structured(),
195
				'parent_table'      => 'comments',
196
				'parent_join_field' => 'comment_ID',
197
				'table_join_field'  => 'comment_id',
198
			),
199
			'terms'              => array(
200
				'table'           => $wpdb->terms,
201
				'range_field'     => 'term_id',
202
				'key_fields'      => array( 'term_id' ),
203
				'checksum_fields' => array( 'term_id', 'name', 'slug' ),
204
				'parent_table'    => 'term_taxonomy',
205
			),
206
			'termmeta'           => array(
207
				'table'           => $wpdb->termmeta,
208
				'range_field'     => 'term_id',
209
				'key_fields'      => array( 'term_id', 'meta_key' ),
210
				'checksum_fields' => array( 'meta_key', 'meta_value' ),
211
				'parent_table'    => 'term_taxonomy',
212
			),
213
			'term_relationships' => array(
214
				'table'             => $wpdb->term_relationships,
215
				'range_field'       => 'object_id',
216
				'key_fields'        => array( 'object_id' ),
217
				'checksum_fields'   => array( 'object_id', 'term_taxonomy_id' ),
218
				'parent_table'      => 'term_taxonomy',
219
				'parent_join_field' => 'term_taxonomy_id',
220
				'table_join_field'  => 'term_taxonomy_id',
221
			),
222
			'term_taxonomy'      => array(
223
				'table'           => $wpdb->term_taxonomy,
224
				'range_field'     => 'term_taxonomy_id',
225
				'key_fields'      => array( 'term_taxonomy_id' ),
226
				'checksum_fields' => array( 'term_taxonomy_id', 'term_id', 'taxonomy', 'description', 'parent' ),
227
				'filter_values'   => Sync\Settings::get_blacklisted_taxonomies_structured(),
228
			),
229
			'links'              => $wpdb->links, // TODO describe in the array format or add exceptions.
230
			'options'            => $wpdb->options, // TODO describe in the array format or add exceptions.
231
		);
232
	}
233
234
	/**
235
	 * Prepare field params based off provided configuration.
236
	 *
237
	 * @param array $table_configuration The table configuration array.
238
	 */
239
	private function prepare_fields( $table_configuration ) {
240
		$this->key_fields            = $table_configuration['key_fields'];
241
		$this->range_field           = $table_configuration['range_field'];
242
		$this->checksum_fields       = $table_configuration['checksum_fields'];
243
		$this->filter_values         = isset( $table_configuration['filter_values'] ) ? $table_configuration['filter_values'] : null;
244
		$this->additional_filter_sql = ! empty( $table_configuration['filter_sql'] ) ? $table_configuration['filter_sql'] : '';
245
		$this->parent_table          = isset( $table_configuration['parent_table'] ) ? $table_configuration['parent_table'] : null;
246
		$this->parent_join_field     = isset( $table_configuration['parent_join_field'] ) ? $table_configuration['parent_join_field'] : $table_configuration['range_field'];
247
		$this->table_join_field      = isset( $table_configuration['table_join_field'] ) ? $table_configuration['table_join_field'] : $table_configuration['range_field'];
248
	}
249
250
	/**
251
	 * Verify provided table name is valid for checksum processing.
252
	 *
253
	 * @param string $table Table name to validate.
254
	 *
255
	 * @return mixed|string
256
	 * @throws Exception Throw an exception on validation failure.
257
	 */
258
	private function validate_table_name( $table ) {
259
		if ( empty( $table ) ) {
260
			throw new Exception( 'Invalid table name: empty' );
261
		}
262
263
		if ( ! array_key_exists( $table, $this->allowed_tables ) ) {
264
			throw new Exception( "Invalid table name: $table not allowed" );
265
		}
266
267
		// TODO other checks if such are needed.
268
269
		return $this->allowed_tables[ $table ]['table'];
270
	}
271
272
	/**
273
	 * Verify provided fields are proper names.
274
	 *
275
	 * @param array $fields Array of field names to validate.
276
	 *
277
	 * @throws Exception Throw an exception on failure to validate.
278
	 */
279
	private function validate_fields( $fields ) {
280
		foreach ( $fields as $field ) {
281
			if ( ! preg_match( '/^[0-9,a-z,A-Z$_]+$/i', $field ) ) {
282
				throw new Exception( "Invalid field name: $field is not allowed" );
283
			}
284
285
			// TODO other verifications of the field names.
286
		}
287
	}
288
289
	/**
290
	 * Verify the fields exist in the table.
291
	 *
292
	 * @param array $fields Array of fields to validate.
293
	 *
294
	 * @return bool
295
	 * @throws Exception Throw an exception on failure to validate.
296
	 */
297
	private function validate_fields_against_table( $fields ) {
298
		global $wpdb;
299
300
		$valid_fields = array();
301
302
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
303
		$result = $wpdb->get_results( "SHOW COLUMNS FROM {$this->table}", ARRAY_A );
304
305
		foreach ( $result as $result_row ) {
306
			$valid_fields[] = $result_row['Field'];
307
		}
308
309
		// Check if the fields are actually contained in the table.
310
		foreach ( $fields as $field_to_check ) {
311
			if ( ! in_array( $field_to_check, $valid_fields, true ) ) {
312
				throw new Exception( "Invalid field name: field '{$field_to_check}' doesn't exist in table {$this->table}" );
313
			}
314
		}
315
316
		return true;
317
	}
318
319
	/**
320
	 * Verify the configured fields.
321
	 *
322
	 * @throws Exception Throw an exception on failure to validate in the internal functions.
323
	 */
324
	private function validate_input() {
325
		$fields = array_merge( array( $this->range_field ), $this->key_fields, $this->checksum_fields );
326
327
		$this->validate_fields( $fields );
328
		$this->validate_fields_against_table( $fields );
329
	}
330
331
	/**
332
	 * Prepare filter values as SQL statements to be added to the other filters.
333
	 *
334
	 * @param array  $filter_values The filter values array.
335
	 * @param string $table_prefix  If the values are going to be used in a sub-query, add a prefix with the table alias.
336
	 *
337
	 * @return array|null
338
	 */
339
	private function prepare_filter_values_as_sql( $filter_values = array(), $table_prefix = '' ) {
340
		global $wpdb;
341
342
		if ( ! is_array( $filter_values ) ) {
343
			return null;
344
		}
345
346
		$result = array();
347
348
		foreach ( $filter_values as $field => $filter ) {
349
			$key = ( ! empty( $table_prefix ) ? $table_prefix : $this->table ) . '.' . $field;
350
351
			switch ( $filter['operator'] ) {
352
				case 'IN':
353
				case 'NOT IN':
354
				case '<>':
355
					$values_placeholders = implode( ',', array_fill( 0, count( $filter['values'] ), '%s' ) );
356
					$statement           = "{$key} {$filter['operator']} ( $values_placeholders )";
357
358
					// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
359
					$prepared_statement = $wpdb->prepare( $statement, $filter['values'] );
360
361
					$result[] = $prepared_statement;
362
					break;
363
				// TODO implement other operators if needed.
364
			}
365
		}
366
367
		return $result;
368
	}
369
370
	/**
371
	 * Build the filter query baased off range fields and values and the additional sql.
372
	 *
373
	 * @param int|null   $range_from    Start of the range.
374
	 * @param int|null   $range_to      End of the range.
375
	 * @param array|null $filter_values Additional filter values. Not used at the moment.
376
	 * @param string     $table_prefix  Table name to be prefixed to the columns. Used in sub-queries where columns can clash.
377
	 * @param bool       $exclude_range Flag to exclude addition of range filter for parent tables.
378
	 *
379
	 * @return string
380
	 */
381
	public function build_filter_statement( $range_from = null, $range_to = null, $filter_values = null, $table_prefix = '', $exclude_range = false ) {
382
		global $wpdb;
383
384
		// If there is a field prefix that we want to use with table aliases.
385
		$parent_prefix = ( ! empty( $table_prefix ) ? $table_prefix : $this->table ) . '.';
386
387
		/**
388
		 * Prepare the ranges.
389
		 */
390
391
		$filter_array = array( '1 = 1' );
392
		if ( ! $exclude_range ) {
393 View Code Duplication
			if ( null !== $range_from ) {
394
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
395
				$filter_array[] = $wpdb->prepare( "{$parent_prefix}{$this->range_field} >= %d", array( intval( $range_from ) ) );
396
			}
397 View Code Duplication
			if ( null !== $range_to ) {
398
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
399
				$filter_array[] = $wpdb->prepare( "{$parent_prefix}{$this->range_field} <= %d", array( intval( $range_to ) ) );
400
			}
401
		}
402
403
		/**
404
		 * End prepare the ranges.
405
		 */
406
407
		/**
408
		 * Prepare data filters.
409
		 */
410
411
		// Default filters.
412
		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...
413
			$prepared_values_statements = $this->prepare_filter_values_as_sql( $this->filter_values, $table_prefix );
414
			if ( $prepared_values_statements ) {
415
				$filter_array = array_merge( $filter_array, $prepared_values_statements );
416
			}
417
		}
418
419
		// Additional filters.
420
		if ( ! empty( $filter_values ) ) {
421
			// Prepare filtering.
422
			$prepared_values_statements = $this->prepare_filter_values_as_sql( $filter_values, $table_prefix );
423
			if ( $prepared_values_statements ) {
424
				$filter_array = array_merge( $filter_array, $prepared_values_statements );
425
			}
426
		}
427
428
		// Add any additional filters via direct SQL statement.
429
		// Currently used only because we haven't converted all filtering to happen via `filter_values`.
430
		// This SQL is NOT prefixed and column clashes can occur when used in sub-queries.
431
		if ( $this->additional_filter_sql ) {
432
			$filter_array[] = $this->additional_filter_sql;
433
		}
434
435
		/**
436
		 * End prepare data filters.
437
		 */
438
		return implode( ' AND ', $filter_array );
439
	}
440
441
	/**
442
	 * Returns the checksum query. All validation of fields and configurations are expected to occur prior to usage.
443
	 *
444
	 * @param int|null   $range_from      The start of the range.
445
	 * @param int|null   $range_to        The end of the range.
446
	 * @param array|null $filter_values   Additional filter values. Not used at the moment.
447
	 * @param bool       $granular_result If the function should return a granular result.
448
	 *
449
	 * @return string
450
	 *
451
	 * @throws Exception Throws and exception if validation fails in the internal function calls.
452
	 */
453
	private function build_checksum_query( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false ) {
454
		global $wpdb;
455
456
		// Escape the salt.
457
		$salt = $wpdb->prepare( '%s', $this->salt ); // TODO escape or prepare statement.
458
459
		// Prepare the compound key.
460
		$key_fields = array();
461
462
		// Prefix the fields with the table name, to avoid clashes in queries with sub-queries (e.g. meta tables).
463
		foreach ( $this->key_fields as $field ) {
464
			$key_fields[] = $this->table . '.' . $field;
465
		}
466
467
		$key_fields = implode( ',', $key_fields );
468
469
		// Prepare the checksum fields.
470
		$checksum_fields = array();
471
		// Prefix the fields with the table name, to avoid clashes in queries with sub-queries (e.g. meta tables).
472
		foreach ( $this->checksum_fields as $field ) {
473
			$checksum_fields[] = $this->table . '.' . $field;
474
		}
475
		$checksum_fields_string = implode( ',', array_merge( $checksum_fields, array( $salt ) ) );
476
477
		$additional_fields = '';
478
		if ( $granular_result ) {
479
			// TODO uniq the fields as sometimes(most) range_index is the key and there's no need to select the same field twice.
480
			$additional_fields = "
481
				{$this->table}.{$this->range_field} as range_index,
482
			    {$key_fields},
483
			";
484
		}
485
486
		$filter_stamenet = $this->build_filter_statement( $range_from, $range_to, $filter_values );
487
488
		$join_statement = '';
489
		if ( $this->parent_table ) {
490
			$parent_table_obj    = new Table_Checksum( $this->parent_table );
491
			$parent_filter_query = $parent_table_obj->build_filter_statement( $range_from, $range_to, null, 'parent_table', true );
492
493
			$join_statement = "
494
				INNER JOIN {$parent_table_obj->table} as parent_table ON ({$this->table}.{$this->table_join_field} = parent_table.{$this->parent_join_field} AND {$parent_filter_query})
495
			";
496
		}
497
498
		$query = "
499
			SELECT
500
				{$additional_fields}
501
				SUM(
502
					CRC32(
503
						CONCAT_WS( '#', {$salt}, {$checksum_fields_string} )
504
					)
505
				)  AS checksum
506
			 FROM
507
			    {$this->table}
508
				{$join_statement}
509
			 WHERE
510
				{$filter_stamenet}
511
		";
512
513
		/**
514
		 * We need the GROUP BY only for compound keys.
515
		 */
516
		if ( $granular_result ) {
517
			$query .= "
518
				GROUP BY {$key_fields}
519
				LIMIT 9999999
520
			";
521
		}
522
523
		return $query;
524
	}
525
526
	/**
527
	 * Obtain the min-max values (edges) of the range.
528
	 *
529
	 * @param int|null $range_from The start of the range.
530
	 * @param int|null $range_to   The end of the range.
531
	 * @param int|null $limit      How many values to return.
532
	 *
533
	 * @return array|object|void
534
	 * @throws Exception Throws an exception if validation fails on the internal function calls.
535
	 */
536
	public function get_range_edges( $range_from = null, $range_to = null, $limit = null ) {
537
		global $wpdb;
538
539
		$this->validate_fields( array( $this->range_field ) );
540
541
		// `trim()` to make sure we don't add the statement if it's empty.
542
		$filters = trim( $this->build_filter_statement( $range_from, $range_to ) );
543
544
		$filter_statement = '';
545
		if ( ! empty( $filters ) ) {
546
			$filter_statement = "
547
				WHERE
548
					{$filters}
549
			";
550
		}
551
552
		// Only make the distinct count when we know there can be multiple entries for the range column.
553
		$distinct_count = count( $this->key_fields ) > 1 ? 'DISTINCT' : '';
554
555
		$query = "
556
			SELECT
557
			       MIN({$this->range_field}) as min_range,
558
			       MAX({$this->range_field}) as max_range,
559
			       COUNT( {$distinct_count} {$this->range_field}) as item_count
560
			FROM
561
		";
562
563
		/**
564
		 * If `$limit` is not specified, we can directly use the table.
565
		 */
566
		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...
567
			$query .= "
568
				{$this->table}
569
	            {$filter_statement}
570
			";
571
		} else {
572
			/**
573
			 * If there is `$limit` specified, we can't directly use `MIN/MAX()` as they don't work with `LIMIT`.
574
			 * That's why we will alter the query for this case.
575
			 */
576
			$limit = intval( $limit );
577
578
			$query .= "
579
				(
580
					SELECT
581
						{$distinct_count} {$this->range_field}
582
					FROM
583
						{$this->table}
584
						{$filter_statement}
585
					ORDER BY
586
						{$this->range_field} ASC
587
					LIMIT {$limit}
588
				) as ids_query
589
			";
590
		}
591
592
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
593
		$result = $wpdb->get_row( $query, ARRAY_A );
594
595
		if ( ! $result || ! is_array( $result ) ) {
596
			throw new Exception( 'Unable to get range edges' );
597
		}
598
599
		return $result;
600
	}
601
602
	/**
603
	 * Update the results to have key/checksum format.
604
	 *
605
	 * @param array $results Prepare the results for output of granular results.
606
	 */
607
	protected function prepare_results_for_output( &$results ) {
608
		// get the compound key.
609
		// only return range and compound key for granular results.
610
611
		$return_value = array();
612
613
		foreach ( $results as &$result ) {
614
			// Working on reference to save memory here.
615
616
			$key = array();
617
			foreach ( $this->key_fields as $field ) {
618
				$key[] = $result[ $field ];
619
			}
620
621
			$return_value[ implode( '-', $key ) ] = $result['checksum'];
622
		}
623
624
		return $return_value;
625
	}
626
627
	/**
628
	 * Calculate the checksum based on provided range and filters.
629
	 *
630
	 * @param int|null   $range_from          The start of the range.
631
	 * @param int|null   $range_to            The end of the range.
632
	 * @param array|null $filter_values       Additional filter values. Not used at the moment.
633
	 * @param bool       $granular_result     If the returned result should be granular or only the checksum.
634
	 * @param bool       $simple_return_value If we want to use a simple return value for non-granular results (return only the checksum, without wrappers).
635
	 *
636
	 * @return array|mixed|object|WP_Error|null
637
	 */
638
	public function calculate_checksum( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false, $simple_return_value = true ) {
639
640
		if ( ! Sync\Settings::is_checksum_enabled() ) {
641
			return new WP_Error( 'checksum_disabled', 'Checksums are currently disabled.' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'checksum_disabled'.

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...
642
		}
643
644
		try {
645
			$this->validate_input();
646
		} catch ( Exception $ex ) {
647
			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...
648
		}
649
650
		$query = $this->build_checksum_query( $range_from, $range_to, $filter_values, $granular_result );
651
652
		global $wpdb;
653
654
		if ( ! $granular_result ) {
655
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
656
			$result = $wpdb->get_row( $query, ARRAY_A );
657
658
			if ( ! is_array( $result ) ) {
659
				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...
660
			}
661
662
			if ( $simple_return_value ) {
663
				return $result['checksum'];
664
			}
665
666
			return array(
667
				'range'    => $range_from . '-' . $range_to,
668
				'checksum' => $result['checksum'],
669
			);
670
		} else {
671
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
672
			$result = $wpdb->get_results( $query, ARRAY_A );
673
			return $this->prepare_results_for_output( $result );
674
		}
675
	}
676
}
677