Repository: WordPress/WordPress-Coding-Standards (php)

Issue:
Internal error in WordPress/Sniffs/WP/PostsPerPageSniff.php
<!--
PLEASE FILL OUT THE TEMPLATE COMPLETELY.
BUG REPORTS WHICH CANNOT BE REPRODUCED BASED ON THE INFORMATION PROVIDED WILL BE CLOSED.
-->

## Bug Description
<!--
Please provide a clear and concise description of what the bug is.

What did you expect to happen? What actually happened?
-->
This code fails if the `$val` is an empty string.
https://github.com/WordPress/WordPress-Coding-Standards/blob/e02529fec40ff219653182d43866e6d6d9e3b47d/WordPress/Sniffs/WP/PostsPerPageSniff.php#L81
```
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1 | ERROR | An error occurred during processing; checking has been aborted. The error message was: Uninitialized string offset 0 in /tmp/test/vendor/wp-coding-standards/wpcs/WordPress/Sniffs/WP/PostsPerPageSniff.php on line
   |       | 81 (Internal.Exception)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
```

## Minimal Code Snippet
<!-- Please provide example code that allows us to reproduce the issue. Do NOT paste screenshots of code! -->

The issue happens when running this command:
```bash
vendor/bin/phpcs -s --standard=WordPress --sniffs=WordPress.WP.PostsPerPage test.php
```

... over a file containing this code:
```php
<?php

$query = 'posts_per_page=' . (int) $_POST['limit'];
```

## Error Code
<!--
The error code for the sniff that is (or should be) being triggered (you
can see the sniff error codes by running `phpcs` with the `-s` flag).
e.g. `WordPress.PHP.NoSilencedErrors.Discouraged`

Note: only report issues for error codes starting with `WordPress` to this repo!
If the error code starts with `Universal`, `NormalizedArrays` or `Modernize` report the issue to PHPCSExtra.
If the error code starts with `Generic`, `PEAR`, `PSR1`, `PSR2`, `PSR12`, `Squiz` or `Zend` report the issue to PHP_CodeSniffer itself.

You can leave this section empty if you are reporting a false negative.
-->
WordPress.WP.PostsPerPage

## Environment
<!--
To find out the versions used:
* PHP: run `php -v`.
* Packages: run `composer info` or run `composer [global] info` to see the installed versions.
-->

| Question                 | Answer
| ------------------------ | -------
| PHP version              | 8.2.10
| PHP_CodeSniffer version  | 3.7.2
| WordPressCS version      | dev-develop e02529f, also tried with 3.0.1
| PHPCSUtils version       | 1.0.8
| PHPCSExtra version       | 1.1.1
| WordPressCS install type | Composer project local
| IDE (if relevant)        | None


## Additional Context (optional)
<!-- Add any other context about the problem here. -->

## Tested Against `develop` Branch?
- [x] I have verified the issue still exists in the `develop` branch of WordPressCS.


Pull request:
Add defensive coding in posts per page sniff
This will prevent the undefined offset internal error.

Closes #2390 

Patch:
diff --git a/WordPress/Sniffs/WP/PostsPerPageSniff.php b/WordPress/Sniffs/WP/PostsPerPageSniff.php
index a3f8ce45..e2575cbd 100644
--- a/WordPress/Sniffs/WP/PostsPerPageSniff.php
+++ b/WordPress/Sniffs/WP/PostsPerPageSniff.php
@@ -67,6 +67,10 @@ final class PostsPerPageSniff extends AbstractArrayAssignmentRestrictionsSniff {
 	public function callback( $key, $val, $line, $group ) {
 		$stripped_val = TextStrings::stripQuotes( $val );
 
+		if ( '' === $stripped_val ) {
+			return false;
+		}
+
 		if ( $val !== $stripped_val ) {
 			// The value was a text string. For text strings, we only accept purely numeric values.
 			if ( preg_match( '`^[0-9]+$`', $stripped_val ) !== 1 ) {


Test patch:
diff --git a/WordPress/Tests/DB/SlowDBQueryUnitTest.inc b/WordPress/Tests/DB/SlowDBQueryUnitTest.inc
index fea13795..883673d2 100644
--- a/WordPress/Tests/DB/SlowDBQueryUnitTest.inc
+++ b/WordPress/Tests/DB/SlowDBQueryUnitTest.inc
@@ -21,3 +21,7 @@ $query = 'foo=bar&meta_key=foo&meta_value=bar';
 if ( ! isset( $widget['params'][0] ) ) {
 	$widget['params'][0] = array();
 }
+
+$query = 'foo=bar&meta_key=&meta_value=bar';
+$query = 'foo=bar&meta_key=foo&meta_value=';
+$query = 'foo=bar&meta_key=&meta_value=';
diff --git a/WordPress/Tests/DB/SlowDBQueryUnitTest.php b/WordPress/Tests/DB/SlowDBQueryUnitTest.php
index e0832726..46ba3204 100644
--- a/WordPress/Tests/DB/SlowDBQueryUnitTest.php
+++ b/WordPress/Tests/DB/SlowDBQueryUnitTest.php
@@ -44,6 +44,9 @@ final class SlowDBQueryUnitTest extends AbstractSniffUnitTest {
 			15 => 1,
 			16 => 1,
 			19 => 2,
+			25 => 2,
+			26 => 2,
+			27 => 2,
 		);
 	}
 }
diff --git a/WordPress/Tests/WP/PostsPerPageUnitTest.inc b/WordPress/Tests/WP/PostsPerPageUnitTest.inc
index 7474ba6c..bb216bdf 100644
--- a/WordPress/Tests/WP/PostsPerPageUnitTest.inc
+++ b/WordPress/Tests/WP/PostsPerPageUnitTest.inc
@@ -124,3 +124,17 @@ $args = array(
 	'posts_per_page' => 75.0, // OK (75).
 	'posts_per_page' => 150.000, // Bad (150).
 );
+
+$query = 'posts_per_page=' . (int) $_POST['limit']; // OK.
+
+$args = array(
+	'posts_per_page' => '', // OK.
+);
+
+_query_posts( 'nopaging=true&posts_per_page=' ); // OK.
+
+$query_args['posts_per_page'] = ''; // OK.
+
+$query_args[
+	'posts_per_page'
+] = ''; // OK.