Skip to content

Commit

Permalink
Add sniff for required function parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
ernilambar committed Nov 19, 2024
1 parent fe8ea10 commit 5bc1d81
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 1 deletion.
7 changes: 6 additions & 1 deletion phpcs-rulesets/plugin-review.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
<severity>7</severity>
</rule>

<!-- Check for discouraged WordPress functions. -->
<!-- Check for discouraged WordPress functions. -->
<rule ref="WordPress.WP.DiscouragedFunctions">
<severity>6</severity>
</rule>
Expand All @@ -151,4 +151,9 @@
<severity>7</severity>
</rule>

<!-- Check for missing required function parameters. -->
<rule ref="PluginCheck.CodeAnalysis.RequiredFunctionParameters">
<severity>7</severity>
</rule>

</ruleset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php
/**
* RequiredFunctionParametersSniff
*
* Based on code from {@link https://github.com/WordPress/WordPress-Coding-Standards}
* which is licensed under {@link https://opensource.org/licenses/MIT}.
*
* @package PluginCheck
*/

namespace PluginCheckCS\PluginCheck\Sniffs\CodeAnalysis;

use PHPCSUtils\Utils\PassedParameters;
use WordPressCS\WordPress\AbstractFunctionParameterSniff;

/**
* Detect missing required function parameters.
*
* @link https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/
*
* @since 1.3.0
*/
final class RequiredFunctionParametersSniff extends AbstractFunctionParameterSniff {

/**
* Array of functions to check.
*
* @since 1.3.0
*
* @var array<string, array<string, int|string>> 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 )
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

parse_str( $param_one, $param_two ); // Good.
parse_str( $param_one ); // Bad.

$str = "first=value&arr[]=foo+bar&arr[]=baz";
parse_str($str, $output); // Good.
parse_str($str); // Bad.

parse_str("My Value=Something", $output); // Good.
parse_str("My Value=Something"); // Bad.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* Unit tests for RequiredFunctionParametersSniff.
*
* @package PluginCheck
*/

namespace PluginCheckCS\PluginCheck\Tests\CodeAnalysis;

use PluginCheckCS\PluginCheck\Sniffs\CodeAnalysis\RequiredFunctionParametersSniff;
use PluginCheckCS\PluginCheck\Tests\AbstractSniffUnitTest;
use PHP_CodeSniffer\Sniffs\Sniff;

/**
* Unit tests for RequiredFunctionParametersSniff.
*/
final class RequiredFunctionParametersUnitTest extends AbstractSniffUnitTest {

/**
* Returns the lines where errors should occur.
*
* @return array <int line number> => <int number of errors>
*/
public function getErrorList() {
return array(
4 => 1,
8 => 1,
11 => 1,
);
}

/**
* Returns the lines where warnings should occur.
*
* @return array <int line number> => <int number of warnings>
*/
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 ) {
}
}
1 change: 1 addition & 0 deletions phpcs-sniffs/PluginCheck/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
<rule ref="PluginCheck.CodeAnalysis.ImageFunctions" />
<rule ref="PluginCheck.CodeAnalysis.Localhost" />
<rule ref="PluginCheck.CodeAnalysis.Offloading" />
<rule ref="PluginCheck.CodeAnalysis.RequiredFunctionParameters" />

</ruleset>
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@

query_posts( 'cat=3' );
wp_reset_query();

parse_str( 'first=value&arr[]=foo+bar&arr[]=baz' );
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) ) );
Expand Down

0 comments on commit 5bc1d81

Please sign in to comment.