diff --git a/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingParamTypeFromCallsSniff.php b/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingParamTypeFromCallsSniff.php index 214864811a..cf52152318 100644 --- a/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingParamTypeFromCallsSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/Commenting/AddMissingParamTypeFromCallsSniff.php @@ -68,13 +68,6 @@ public function process( File $phpcsFile, $stackPtr ) { $functionName = $tokens[ $functionNamePtr ]['content']; - // Check if function has a docblock. - $docblock = $this->findDocblock( $phpcsFile, $stackPtr ); - - if ( false === $docblock ) { - return; - } - // Get function parameters. $params = $this->getParameters( $phpcsFile, $stackPtr ); @@ -82,8 +75,15 @@ public function process( File $phpcsFile, $stackPtr ) { return; } - // Get existing @param tags with their types. - $existingParams = $this->getExistingParamTags( $phpcsFile, $docblock ); + // Check if function has a docblock. + $docblock = $this->findDocblock( $phpcsFile, $stackPtr ); + $hasDocblock = ( false !== $docblock ); + $existingParams = array(); + + if ( $hasDocblock ) { + // Get existing @param tags with their types. + $existingParams = $this->getExistingParamTags( $phpcsFile, $docblock ); + } // Find parameters that are missing @param comments entirely. $missingParamComments = array(); @@ -145,6 +145,21 @@ public function process( File $phpcsFile, $stackPtr ) { return; } + // If no docblock exists, create one with the detected @param tags. + if ( ! $hasDocblock ) { + $fix = $phpcsFile->addFixableError( + 'Missing docblock with @param tags (detected types from call site literals)', + $stackPtr, + 'MissingDocblockFromCalls' + ); + + if ( $fix ) { + $this->addDocblock( $phpcsFile, $stackPtr, $paramsToAdd ); + } + + return; + } + // Report and fix each missing @param. foreach ( $paramsToAdd as $paramName => $info ) { $typeString = implode( '|', $info['types'] ); @@ -513,4 +528,73 @@ private function addParamTag( File $phpcsFile, $docblock, $type, $paramName ) { $phpcsFile->fixer->replaceToken( $lastParamEnd, $newContent ); $phpcsFile->fixer->endChangeset(); } + + /** + * Add a new docblock with @param tags. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr The function token position. + * @param array $paramsToAdd Array of param name => array('types' => array, 'index' => int). + * + * @return void + */ + private function addDocblock( File $phpcsFile, $stackPtr, $paramsToAdd ) { + $tokens = $phpcsFile->getTokens(); + + // Find the position to insert the docblock (before any modifiers or the function keyword). + $insertBefore = $stackPtr; + + $prev = $phpcsFile->findPrevious( + array( T_WHITESPACE, T_STATIC, T_PUBLIC, T_PRIVATE, T_PROTECTED, T_ABSTRACT, T_FINAL ), + $stackPtr - 1, + null, + true + ); + + if ( false !== $prev ) { + // Find the first modifier/keyword after $prev. + $next = $phpcsFile->findNext( T_WHITESPACE, $prev + 1, $stackPtr, true ); + + if ( false !== $next ) { + $insertBefore = $next; + } + } + + // Determine indentation from the function line. + $indent = ''; + + for ( $i = $insertBefore - 1; $i >= 0; $i-- ) { + if ( $tokens[ $i ]['code'] === T_WHITESPACE ) { + if ( strpos( $tokens[ $i ]['content'], "\n" ) !== false || strpos( $tokens[ $i ]['content'], "\r" ) !== false ) { + // Get the part after the newline. + $indent = preg_replace( '/^.*[\r\n]/', '', $tokens[ $i ]['content'] ); + break; + } + } else { + break; + } + } + + // Build the docblock. + $docblock = "/**\n"; + + // Sort params by index to maintain order. + uasort( + $paramsToAdd, + function ( $a, $b ) { + return $a['index'] - $b['index']; + } + ); + + foreach ( $paramsToAdd as $paramName => $info ) { + $typeString = implode( '|', $info['types'] ); + $docblock .= $indent . "\t * @param " . $typeString . ' ' . $paramName . "\n"; + } + + $docblock .= $indent . "\t */\n" . $indent . "\t"; + + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->addContentBefore( $insertBefore, $docblock ); + $phpcsFile->fixer->endChangeset(); + } } diff --git a/tests/phpunit/emails/test_FrmEmail.php b/tests/phpunit/emails/test_FrmEmail.php index 34d5edbbb9..12e2f51f68 100644 --- a/tests/phpunit/emails/test_FrmEmail.php +++ b/tests/phpunit/emails/test_FrmEmail.php @@ -428,6 +428,9 @@ public function test_trigger_email_six() { $this->check_senders( $expected, $mock_email ); } + /** + * @param string $subject + */ protected function prepare_subject( $subject ) { return wp_specialchars_decode( strip_tags( stripslashes( $subject ) ), ENT_QUOTES ); } @@ -747,6 +750,10 @@ public function test_plain_text_message() { } } + /** + * @param string $setting_name + * @param string $property + */ private function check_private_properties( $settings, $setting_name, $property = '' ) { if ( ! $property ) { $property = $setting_name; diff --git a/tests/phpunit/entries/test_FrmEntriesController.php b/tests/phpunit/entries/test_FrmEntriesController.php index d57a0331e7..82f7a03a3d 100644 --- a/tests/phpunit/entries/test_FrmEntriesController.php +++ b/tests/phpunit/entries/test_FrmEntriesController.php @@ -34,6 +34,9 @@ public function test_delete_entry_after_save() { $this->assertEquals( 'publish', $post->post_status ); } + /** + * @param array $options + */ private function create_form( $options = array() ) { return $this->factory->form->create_and_get( array( diff --git a/tests/phpunit/entries/test_FrmShowEntryShortcode.php b/tests/phpunit/entries/test_FrmShowEntryShortcode.php index cf36da8510..d732245d55 100644 --- a/tests/phpunit/entries/test_FrmShowEntryShortcode.php +++ b/tests/phpunit/entries/test_FrmShowEntryShortcode.php @@ -700,6 +700,9 @@ public function test_json_format() { $this->assertSame( $expected_json, $actual_json ); } + /** + * @param bool $include_meta + */ protected function get_test_entry( $include_meta ) { $new_entry = array( 'form_id' => FrmForm::get_id_by_key( 'free_field_types' ), @@ -1025,10 +1028,16 @@ protected function get_field_value( $entry, $field, $atts ) { return $field_value; } + /** + * @param string $type + */ protected function set_included_fields( $type ) { $this->include_fields = $this->set_field_array( $type ); } + /** + * @param string $type + */ protected function set_excluded_fields( $type ) { $this->exclude_fields = $this->set_field_array( $type ); } @@ -1043,6 +1052,9 @@ protected function set_field_array( $type ) { return $fields; } + /** + * @param string $type + */ protected function get_single_included_field( $type ) { $include_fields = array( 'free-text-field' => 'free-text-field', diff --git a/tests/phpunit/fields/test_FrmFieldGridHelper.php.php b/tests/phpunit/fields/test_FrmFieldGridHelper.php.php index 515a401942..cdfae8d07d 100644 --- a/tests/phpunit/fields/test_FrmFieldGridHelper.php.php +++ b/tests/phpunit/fields/test_FrmFieldGridHelper.php.php @@ -27,6 +27,9 @@ public function test_get_size_of_class() { $this->assertEquals( 12, $this->get_size_of_class( 'frm_full' ) ); } + /** + * @param string $class + */ private function get_size_of_class( $class ) { return $this->run_private_method( array( 'FrmFieldGridHelper', 'get_size_of_class' ), array( $class ) ); } @@ -57,6 +60,10 @@ public function test_basic_grouping() { ob_end_clean(); } + /** + * @param false|int $assert_size + * @param string $assert_message + */ private function sync_current_field_once( $assert_size = false, $assert_message = '' ) { $this->helper->maybe_begin_field_wrapper(); $this->helper->sync_list_size(); @@ -70,6 +77,10 @@ private function assert_current_list_size( $expected, $message = '' ) { $this->assertEquals( $expected, $this->get_private_property( $this->helper, 'current_list_size' ), $message ); } + /** + * @param string $type + * @param string $classes + */ private function create_field_with_classes( $type, $classes = '' ) { return $this->factory->field->create_and_get( array( @@ -129,6 +140,9 @@ public function test_with_sections() { ob_end_clean(); } + /** + * @param int $expected + */ private function assert_section_helper_size( $expected ) { $this->assertEquals( $expected, $this->get_private_property( $this->section_helper, 'current_list_size' ) ); } diff --git a/tests/phpunit/fields/test_FrmFieldValidate.php b/tests/phpunit/fields/test_FrmFieldValidate.php index d4d0f7fee8..d7bb8ca232 100644 --- a/tests/phpunit/fields/test_FrmFieldValidate.php +++ b/tests/phpunit/fields/test_FrmFieldValidate.php @@ -253,10 +253,16 @@ protected function set_required_field( $field ) { } } + /** + * @param string $field_type + */ protected function get_field_key( $field_type ) { return $field_type . '-form' . $this->form->id; } + /** + * @param array $item_meta + */ protected function check_single_value( $item_meta ) { $_POST = array( 'form_id' => $this->form->id, diff --git a/tests/phpunit/forms/test_FrmForm.php b/tests/phpunit/forms/test_FrmForm.php index 602a51fe75..bb56d7410a 100644 --- a/tests/phpunit/forms/test_FrmForm.php +++ b/tests/phpunit/forms/test_FrmForm.php @@ -169,12 +169,20 @@ public function test_sanitize_field_opt() { $this->assert_sanitize_field_opt_calc( $original_value, $original_value ); } + /** + * @param string $expected + * @param string $original_value + * @param string $message + */ private function assert_sanitize_field_opt_calc( $expected, $original_value, $message = '' ) { $value = $original_value; $this->sanitize_field_opt( 'calc', $value ); $this->assertEquals( $expected, $value, $message ); } + /** + * @param string $opt + */ private function sanitize_field_opt( $opt, &$value ) { return $this->run_private_method( array( 'FrmForm', 'sanitize_field_opt' ), @@ -196,6 +204,9 @@ public function test_normalize_calc_spaces() { $this->assertEquals( '1 <= 2 && 3 <= 4 && 5 <= 6', $this->normalize_calc_spaces( '1<=2 && 3<=4 && 5<=6' ) ); } + /** + * @param string $calc + */ private function normalize_calc_spaces( $calc ) { return $this->run_private_method( array( 'FrmForm', 'normalize_calc_spaces' ), array( $calc ) ); } diff --git a/tests/phpunit/forms/test_FrmFormsController.php b/tests/phpunit/forms/test_FrmFormsController.php index 45258fc6da..6a1c89803a 100644 --- a/tests/phpunit/forms/test_FrmFormsController.php +++ b/tests/phpunit/forms/test_FrmFormsController.php @@ -152,6 +152,9 @@ public function test_front_head() { $this->assertEquals( FrmAppHelper::plugin_url() . '/js/' . $file, $formidable_js->src, $file . ' was not loaded' ); } + /** + * @param array $post_content + */ private function create_on_submit_action( $form_id, $post_content ) { $post_data = array( 'post_type' => FrmFormActionsController::$action_post_type, @@ -306,6 +309,9 @@ public function test_message_with_form_after_create() { $this->run_message_after_create( 1 ); } + /** + * @param int $show_form + */ public function run_message_after_create( $show_form = 0 ) { $form = $this->factory->form->create_and_get( array( diff --git a/tests/phpunit/forms/test_FrmFormsHelper.php b/tests/phpunit/forms/test_FrmFormsHelper.php index 989c55da6b..c52ce14a65 100644 --- a/tests/phpunit/forms/test_FrmFormsHelper.php +++ b/tests/phpunit/forms/test_FrmFormsHelper.php @@ -64,6 +64,11 @@ public function test_maybe_add_sanitize_url_attr() { ); } + /** + * @param string $expected + * @param string $url + * @param string $message + */ private function assert_maybe_add_sanitize_url_attr( $expected, $url, $message = '' ) { $this->assertEquals( $expected, FrmFormsHelper::maybe_add_sanitize_url_attr( $url, (int) $this->form->id ), $message ); } @@ -78,6 +83,10 @@ public function test_get_plan_required() { $this->assert_get_plan_required( 'Plus', array( 'Plus', 'Category2' ) ); } + /** + * @param string $expected + * @param array $categories + */ private function assert_get_plan_required( $expected, $categories ) { $link = compact( 'categories' ); $this->assertEquals( $expected, FrmFormsHelper::get_plan_required( $link ) ); @@ -125,6 +134,9 @@ public function test_get_form_style() { $this->assertEquals( '0', FrmFormsHelper::get_form_style( $form ) ); } + /** + * @param string $custom_style + */ private function create_form_with_custom_style_value( $custom_style ) { return $this->factory->form->create_and_get( array( diff --git a/tests/phpunit/misc/test_FrmAppController.php b/tests/phpunit/misc/test_FrmAppController.php index ca9c8cb6e6..523d28b12f 100644 --- a/tests/phpunit/misc/test_FrmAppController.php +++ b/tests/phpunit/misc/test_FrmAppController.php @@ -38,6 +38,9 @@ public function test_block_menu() { $this->check_menu( 'block' ); } + /** + * @param string $allow + */ private function check_menu( $allow = 'allow' ) { $url = get_option( 'siteurl', true ); do_action( 'admin_menu' ); diff --git a/tests/phpunit/misc/test_FrmAppHelper.php b/tests/phpunit/misc/test_FrmAppHelper.php index 0f6b39b21b..d460ce2388 100644 --- a/tests/phpunit/misc/test_FrmAppHelper.php +++ b/tests/phpunit/misc/test_FrmAppHelper.php @@ -382,6 +382,9 @@ public function test_is_a_valid_color() { $this->assertFalse( $this->is_a_valid_color( 'Not a color' ) ); } + /** + * @param string $value + */ private function is_a_valid_color( $value ) { return $this->run_private_method( array( 'FrmAppHelper', 'is_a_valid_color' ), array( $value ) ); } diff --git a/tests/phpunit/misc/test_FrmInbox.php b/tests/phpunit/misc/test_FrmInbox.php index 42b2349dff..086f4e7809 100644 --- a/tests/phpunit/misc/test_FrmInbox.php +++ b/tests/phpunit/misc/test_FrmInbox.php @@ -23,6 +23,10 @@ public function test_add_message() { $this->assert_message_count( $initial_count + 1, 'Message count should not go up after an invalid message is added.' ); } + /** + * @param int $count + * @param string $message + */ private function assert_message_count( $count, $message ) { $this->assertEquals( $count, $this->get_message_count(), $message ); } diff --git a/tests/phpunit/misc/test_FrmSpamCheckDenylist.php b/tests/phpunit/misc/test_FrmSpamCheckDenylist.php index 71979645e4..310a26e90d 100644 --- a/tests/phpunit/misc/test_FrmSpamCheckDenylist.php +++ b/tests/phpunit/misc/test_FrmSpamCheckDenylist.php @@ -90,6 +90,9 @@ public function setUp(): void { ); } + /** + * @param array $denylist_data + */ private function set_denylist_data( $denylist_data ) { $this->set_private_property( $this->spam_check, 'denylist', $denylist_data ); } diff --git a/tests/phpunit/stripe/test_FrmStripeLiteAuth.php b/tests/phpunit/stripe/test_FrmStripeLiteAuth.php index a56f14512f..062e6e2321 100644 --- a/tests/phpunit/stripe/test_FrmStripeLiteAuth.php +++ b/tests/phpunit/stripe/test_FrmStripeLiteAuth.php @@ -38,6 +38,9 @@ public function test_maybe_add_statement_descriptor() { ); } + /** + * @param array $intent_data + */ private function maybe_add_statement_descriptor( $intent_data ) { return $this->run_private_method( array( 'FrmStrpLiteAuth', 'maybe_add_statement_descriptor' ), array( $intent_data ) ); } diff --git a/tests/phpunit/styles/test_FrmStyle.php b/tests/phpunit/styles/test_FrmStyle.php index a9f05e3beb..3462a488b0 100644 --- a/tests/phpunit/styles/test_FrmStyle.php +++ b/tests/phpunit/styles/test_FrmStyle.php @@ -107,6 +107,9 @@ public function test_strip_invalid_characters() { $this->assertEquals( 'calc(100%/6)', $this->strip_invalid_characters( 'calc(100%/6)' ) ); } + /** + * @param string $input + */ private function strip_invalid_characters( $input ) { $frm_style = new FrmStyle(); return $this->run_private_method( array( $frm_style, 'strip_invalid_characters' ), array( $input ) ); diff --git a/tests/phpunit/styles/test_FrmStylesCardHelper.php b/tests/phpunit/styles/test_FrmStylesCardHelper.php index 73fdba471d..73bfd36db4 100644 --- a/tests/phpunit/styles/test_FrmStylesCardHelper.php +++ b/tests/phpunit/styles/test_FrmStylesCardHelper.php @@ -19,6 +19,10 @@ public function test_has_dark_background() { $this->assert_bg_is_dark( 'rgba(130,36,227,0)', false ); } + /** + * @param string $color + * @param bool $expected + */ private function assert_bg_is_dark( $color, $expected = true ) { $style = new stdClass(); $style->post_content = array(