Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sniff for required function parameter #787

Merged
merged 5 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading