diff --git a/phpcs-rulesets/plugin-review.xml b/phpcs-rulesets/plugin-review.xml
index b4d5eedec..8a78cb564 100644
--- a/phpcs-rulesets/plugin-review.xml
+++ b/phpcs-rulesets/plugin-review.xml
@@ -141,7 +141,7 @@
7
-
+
6
@@ -151,4 +151,9 @@
7
+
+
+ 7
+
+
diff --git a/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/LocalhostSniff.php b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/LocalhostSniff.php
index fb28251f9..63d54d11f 100644
--- a/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/LocalhostSniff.php
+++ b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/LocalhostSniff.php
@@ -55,7 +55,7 @@ public function process_token( $stackPtr ) {
'Do not use Localhost/127.0.0.1 in your code. Found: %s',
$this->find_token_in_multiline_string( $stackPtr, $content, $match[1] ),
'Found',
- [ $match[0] ]
+ array( $match[0] )
);
}
}
diff --git a/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/RequiredFunctionParametersSniff.php b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/RequiredFunctionParametersSniff.php
new file mode 100644
index 000000000..61200990a
--- /dev/null
+++ b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/RequiredFunctionParametersSniff.php
@@ -0,0 +1,81 @@
+> Function name as key, array with target parameter and name as value.
+ */
+ protected $target_functions = array(
+ 'parse_str' => array(
+ 'position' => 2,
+ 'name' => 'result',
+ ),
+ );
+
+ /**
+ * Processes this test, when one of its tokens is encountered.
+ *
+ * @since 1.3.0
+ *
+ * @param int $stackPtr The position of the current token in the stack.
+ * @return int|void Integer stack pointer to skip forward or void to continue normal file processing.
+ */
+ public function process_token( $stackPtr ) {
+ if ( isset( $this->target_functions[ strtolower( $this->tokens[ $stackPtr ]['content'] ) ] ) ) {
+ // Disallow excluding function groups for this sniff.
+ $this->exclude = array();
+
+ return parent::process_token( $stackPtr );
+ }
+ }
+
+ /**
+ * Process the parameters of a matched function call.
+ *
+ * @since 1.3.0
+ *
+ * @param int $stackPtr The position of the current token in the stack.
+ * @param string $group_name The name of the group which was matched.
+ * @param string $matched_content The token content (function name) which was matched in lowercase.
+ * @param array $parameters Array with information about the parameters.
+ * @return void
+ */
+ public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) {
+ $target_param = $this->target_functions[ $matched_content ];
+
+ $found_param = PassedParameters::getParameterFromStack( $parameters, $target_param['position'], $target_param['name'] );
+
+ if ( false === $found_param ) {
+ $this->phpcsFile->addError(
+ 'The "%s" parameter for function %s() is missing.',
+ $stackPtr,
+ 'Missing',
+ array( $target_param['name'], $matched_content )
+ );
+ }
+ }
+}
diff --git a/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/RequiredFunctionParametersUnitTest.inc b/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/RequiredFunctionParametersUnitTest.inc
new file mode 100644
index 000000000..a115ffa0c
--- /dev/null
+++ b/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/RequiredFunctionParametersUnitTest.inc
@@ -0,0 +1,11 @@
+ =>
+ */
+ public function getErrorList() {
+ return array(
+ 4 => 1,
+ 8 => 1,
+ 11 => 1,
+ );
+ }
+
+ /**
+ * Returns the lines where warnings should occur.
+ *
+ * @return array =>
+ */
+ public function getWarningList() {
+ return array();
+ }
+
+ /**
+ * Returns the fully qualified class name (FQCN) of the sniff.
+ *
+ * @return string The fully qualified class name of the sniff.
+ */
+ protected function get_sniff_fqcn() {
+ return RequiredFunctionParametersSniff::class;
+ }
+
+ /**
+ * Sets the parameters for the sniff.
+ *
+ * @throws \RuntimeException If unable to set the ruleset parameters required for the test.
+ *
+ * @param Sniff $sniff The sniff being tested.
+ */
+ public function set_sniff_parameters( Sniff $sniff ) {
+ }
+}
diff --git a/phpcs-sniffs/PluginCheck/ruleset.xml b/phpcs-sniffs/PluginCheck/ruleset.xml
index e07fe0d20..4243c3285 100644
--- a/phpcs-sniffs/PluginCheck/ruleset.xml
+++ b/phpcs-sniffs/PluginCheck/ruleset.xml
@@ -7,5 +7,6 @@
+
diff --git a/tests/phpunit/testdata/plugins/test-plugin-review-phpcs-errors/load.php b/tests/phpunit/testdata/plugins/test-plugin-review-phpcs-errors/load.php
index 96e8669a5..d069d1b81 100644
--- a/tests/phpunit/testdata/plugins/test-plugin-review-phpcs-errors/load.php
+++ b/tests/phpunit/testdata/plugins/test-plugin-review-phpcs-errors/load.php
@@ -24,3 +24,5 @@
query_posts( 'cat=3' );
wp_reset_query();
+
+parse_str( 'first=value&arr[]=foo+bar&arr[]=baz' );
diff --git a/tests/phpunit/tests/Checker/Checks/Plugin_Review_PHPCS_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/Plugin_Review_PHPCS_Check_Tests.php
index 29171a712..1a27c5dae 100644
--- a/tests/phpunit/tests/Checker/Checks/Plugin_Review_PHPCS_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/Plugin_Review_PHPCS_Check_Tests.php
@@ -38,6 +38,9 @@ public function test_run_with_errors() {
$this->assertArrayHasKey( 'code', $errors['load.php'][12][5][0] );
$this->assertEquals( 'WordPress.WP.DeprecatedFunctions.the_author_emailFound', $errors['load.php'][12][5][0]['code'] );
+ // Check for PluginCheck.CodeAnalysis.RequiredFunctionParameters.Missing error on Line no 28 and column no at 1.
+ $this->assertSame( 'PluginCheck.CodeAnalysis.RequiredFunctionParameters.Missing', $errors['load.php'][28][1][0]['code'] );
+
// Check for WordPress.Security.ValidatedSanitizedInput warnings on Line no 15 and column no at 27.
$this->assertCount( 1, wp_list_filter( $warnings['load.php'][15][27], array( 'code' => 'WordPress.Security.ValidatedSanitizedInput.InputNotValidated' ) ) );
$this->assertCount( 1, wp_list_filter( $warnings['load.php'][15][27], array( 'code' => 'WordPress.Security.ValidatedSanitizedInput.MissingUnslash' ) ) );