From 210bca98a54c0c3a2c0c0a287b9e967b48e344fd Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 19 Jan 2026 13:22:26 -0400 Subject: [PATCH 1/6] New sniff to prefer early returns instead of small elses --- classes/controllers/FrmStylesController.php | 12 +- classes/helpers/FrmFieldsHelper.php | 23 +- classes/helpers/FrmShortcodeHelper.php | 22 +- classes/models/fields/FrmFieldType.php | 44 +- .../FlipLargeIfSmallElseSniff.php | 447 ++++++++++++++++++ phpcs-sniffs/Formidable/ruleset.xml | 3 +- stripe/helpers/FrmTransLiteAppHelper.php | 15 +- stripe/helpers/FrmTransLiteListHelper.php | 22 +- 8 files changed, 518 insertions(+), 70 deletions(-) create mode 100644 phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php diff --git a/classes/controllers/FrmStylesController.php b/classes/controllers/FrmStylesController.php index cbde3237d4..835aaaf51d 100644 --- a/classes/controllers/FrmStylesController.php +++ b/classes/controllers/FrmStylesController.php @@ -272,13 +272,13 @@ public static function get_file_name() { * @return string */ private static function get_css_version( $css_key, $version ) { - if ( 'formidable' === $css_key ) { - $this_version = get_option( 'frm_last_style_update' ); + if ( 'formidable' !== $css_key ) { + return $version; + } - if ( ! $this_version ) { - $this_version = $version; - } - } else { + $this_version = get_option( 'frm_last_style_update' ); + + if ( ! $this_version ) { $this_version = $version; } diff --git a/classes/helpers/FrmFieldsHelper.php b/classes/helpers/FrmFieldsHelper.php index 4b19d38809..5274bf89ad 100644 --- a/classes/helpers/FrmFieldsHelper.php +++ b/classes/helpers/FrmFieldsHelper.php @@ -1811,21 +1811,22 @@ public static function switch_field_ids( $val ) { unset( $old, $new ); }//end foreach - if ( is_array( $val ) ) { - foreach ( $val as $k => $v ) { - if ( is_string( $v ) ) { - if ( 'custom_html' === $k ) { - $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v ); - unset( $k, $v ); - continue; - } + if ( ! is_array( $val ) ) { + $val = str_replace( $replace, $replace_with, $val ); + return $val; + } - $val[ $k ] = str_replace( $replace, $replace_with, $v ); + foreach ( $val as $k => $v ) { + if ( is_string( $v ) ) { + if ( 'custom_html' === $k ) { + $val[ $k ] = self::switch_ids_except_strings( $replace, $replace_with, array( '[if description]', '[description]', '[/if description]' ), $v ); unset( $k, $v ); + continue; } + + $val[ $k ] = str_replace( $replace, $replace_with, $v ); + unset( $k, $v ); } - } else { - $val = str_replace( $replace, $replace_with, $val ); } return $val; diff --git a/classes/helpers/FrmShortcodeHelper.php b/classes/helpers/FrmShortcodeHelper.php index a8030c65ce..b6d5805050 100644 --- a/classes/helpers/FrmShortcodeHelper.php +++ b/classes/helpers/FrmShortcodeHelper.php @@ -120,17 +120,17 @@ public static function get_shortcode_tag( $shortcodes, $short_key, $args = array $with_tags = $args['conditional_check'] ? 3 : 2; - if ( ! empty( $shortcodes[ $with_tags ][ $short_key ] ) ) { - $tag = str_replace( '[' . $prefix, '', $shortcodes[0][ $short_key ] ); - $tag = str_replace( ']', '', $tag ); - $tag = str_replace( chr( 194 ) . chr( 160 ), ' ', $tag ); - $tags = preg_split( '/\s+/', $tag, 2 ); - - if ( is_array( $tags ) ) { - $tag = $tags[0]; - } - } else { - $tag = $shortcodes[ $with_tags - 1 ][ $short_key ]; + if ( empty( $shortcodes[ $with_tags ][ $short_key ] ) ) { + return $shortcodes[ $with_tags - 1 ][ $short_key ]; + } + + $tag = str_replace( '[' . $prefix, '', $shortcodes[0][ $short_key ] ); + $tag = str_replace( ']', '', $tag ); + $tag = str_replace( chr( 194 ) . chr( 160 ), ' ', $tag ); + $tags = preg_split( '/\s+/', $tag, 2 ); + + if ( is_array( $tags ) ) { + $tag = $tags[0]; } return $tag; diff --git a/classes/models/fields/FrmFieldType.php b/classes/models/fields/FrmFieldType.php index b1f2f268a2..95450a4934 100644 --- a/classes/models/fields/FrmFieldType.php +++ b/classes/models/fields/FrmFieldType.php @@ -1002,22 +1002,22 @@ public function prepare_field_html( $args ) { $args = $this->fill_display_field_values( $args ); - if ( $this->has_html ) { - $args['html'] = $this->before_replace_html_shortcodes( $args, FrmAppHelper::maybe_kses( FrmField::get_option( $this->field, 'custom_html' ) ) ); - $args['errors'] = is_array( $args['errors'] ) ? $args['errors'] : array(); - $args['field_obj'] = $this; - - $label = FrmFieldsHelper::label_position( $this->field['label'], $this->field, $args['form'] ); - $this->set_field_column( 'label', $label ); - - $html_shortcode = new FrmFieldFormHtml( $args ); - $html = $html_shortcode->get_html(); - $html = $this->after_replace_html_shortcodes( $args, $html ); - $html_shortcode->remove_collapse_shortcode( $html ); - } else { - $html = $this->include_front_field_input( $args, array() ); + if ( $this - <= has_html ) { + return $this->include_front_field_input( $args, array() ); } + $args['html'] = $this->before_replace_html_shortcodes( $args, FrmAppHelper::maybe_kses( FrmField::get_option( $this->field, 'custom_html' ) ) ); + $args['errors'] = is_array( $args['errors'] ) ? $args['errors'] : array(); + $args['field_obj'] = $this; + + $label = FrmFieldsHelper::label_position( $this->field['label'], $this->field, $args['form'] ); + $this->set_field_column( 'label', $label ); + + $html_shortcode = new FrmFieldFormHtml( $args ); + $html = $html_shortcode->get_html(); + $html = $this->after_replace_html_shortcodes( $args, $html ); + $html_shortcode->remove_collapse_shortcode( $html ); + return $html; } @@ -1380,15 +1380,15 @@ protected function show_hidden_values( $args ) { $selected_value = $args['field_value'] ?? $this->field['value']; $hidden = ''; - if ( is_array( $selected_value ) ) { - $args['save_array'] = true; - - foreach ( $selected_value as $selected ) { - $hidden .= $this->show_single_hidden( $selected, $args ); - } - } else { + if ( ! is_array( $selected_value ) ) { $args['save_array'] = $this->is_readonly_array(); - $hidden .= $this->show_single_hidden( $selected_value, $args ); + return $hidden . $this->show_single_hidden( $selected_value, $args ); + } + + $args['save_array'] = true; + + foreach ( $selected_value as $selected ) { + $hidden .= $this->show_single_hidden( $selected, $args ); } return $hidden; diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php new file mode 100644 index 0000000000..d3ea461d2c --- /dev/null +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php @@ -0,0 +1,447 @@ +getTokens(); + + // Must have scope opener/closer. + if ( ! isset( $tokens[ $stackPtr ]['scope_opener'] ) || ! isset( $tokens[ $stackPtr ]['scope_closer'] ) ) { + return; + } + + $ifOpener = $tokens[ $stackPtr ]['scope_opener']; + $ifCloser = $tokens[ $stackPtr ]['scope_closer']; + + // Check if there's an else after the if. + $elseToken = $phpcsFile->findNext( T_WHITESPACE, $ifCloser + 1, null, true ); + + if ( false === $elseToken || $tokens[ $elseToken ]['code'] !== T_ELSE ) { + return; + } + + // Skip if there's an elseif (we only handle simple if/else). + $afterIfCloser = $phpcsFile->findNext( T_WHITESPACE, $ifCloser + 1, null, true ); + + if ( false !== $afterIfCloser && $tokens[ $afterIfCloser ]['code'] === T_ELSEIF ) { + return; + } + + // Get the else's scope opener and closer. + if ( ! isset( $tokens[ $elseToken ]['scope_opener'] ) || ! isset( $tokens[ $elseToken ]['scope_closer'] ) ) { + return; + } + + $elseOpener = $tokens[ $elseToken ]['scope_opener']; + $elseCloser = $tokens[ $elseToken ]['scope_closer']; + + // Check what comes after the else block. + $afterElse = $phpcsFile->findNext( T_WHITESPACE, $elseCloser + 1, null, true ); + + if ( false === $afterElse ) { + return; + } + + // Must be a return statement after the if/else. + if ( $tokens[ $afterElse ]['code'] !== T_RETURN ) { + return; + } + + // Find the semicolon ending the return statement. + $returnSemicolon = $phpcsFile->findNext( T_SEMICOLON, $afterElse + 1 ); + + if ( false === $returnSemicolon ) { + return; + } + + // Check that after the return statement, there's only whitespace/comments until end of scope or file. + $afterReturn = $phpcsFile->findNext( + array( T_WHITESPACE, T_COMMENT ), + $returnSemicolon + 1, + null, + true + ); + + // The return should be the last statement before a closing brace (function/method end). + if ( false !== $afterReturn && $tokens[ $afterReturn ]['code'] !== T_CLOSE_CURLY_BRACKET ) { + return; + } + + // Count lines in if and else blocks. + $ifLineCount = $this->countLinesInScope( $phpcsFile, $ifOpener, $ifCloser ); + $elseLineCount = $this->countLinesInScope( $phpcsFile, $elseOpener, $elseCloser ); + + // Check if if is large and else is small. + if ( $ifLineCount < $this->minIfLines || $elseLineCount > $this->maxElseLines ) { + return; + } + + // Don't trigger if the else is larger than or equal to the if. + if ( $elseLineCount >= $ifLineCount ) { + return; + } + + $fix = $phpcsFile->addFixableError( + 'Large if (%d lines) with small else (%d lines) followed by return. Consider negating the condition and returning early to reduce indentation.', + $stackPtr, + 'Found', + array( $ifLineCount, $elseLineCount ) + ); + + if ( true === $fix ) { + $this->applyFix( $phpcsFile, $stackPtr, $elseToken, $afterElse, $returnSemicolon ); + } + } + + /** + * Count the number of lines inside a scope. + * + * @param File $phpcsFile The file being scanned. + * @param int $scopeOpener The scope opener position. + * @param int $scopeCloser The scope closer position. + * + * @return int + */ + private function countLinesInScope( File $phpcsFile, $scopeOpener, $scopeCloser ) { + $tokens = $phpcsFile->getTokens(); + + $startLine = $tokens[ $scopeOpener ]['line']; + $endLine = $tokens[ $scopeCloser ]['line']; + + // Subtract 2 for the opening and closing brace lines. + return max( 0, $endLine - $startLine - 1 ); + } + + /** + * Apply the fix. + * + * @param File $phpcsFile The file being scanned. + * @param int $ifToken The if token position. + * @param int $elseToken The else token position. + * @param int $returnToken The return token position. + * @param int $returnSemicolon The return semicolon position. + * + * @return void + */ + private function applyFix( File $phpcsFile, $ifToken, $elseToken, $returnToken, $returnSemicolon ) { + $tokens = $phpcsFile->getTokens(); + $fixer = $phpcsFile->fixer; + + $ifOpener = $tokens[ $ifToken ]['scope_opener']; + $ifCloser = $tokens[ $ifToken ]['scope_closer']; + $elseOpener = $tokens[ $elseToken ]['scope_opener']; + $elseCloser = $tokens[ $elseToken ]['scope_closer']; + $conditionOpener = $tokens[ $ifToken ]['parenthesis_opener']; + $conditionCloser = $tokens[ $ifToken ]['parenthesis_closer']; + + // Get the original condition. + $originalCondition = ''; + + for ( $i = $conditionOpener + 1; $i < $conditionCloser; $i++ ) { + $originalCondition .= $tokens[ $i ]['content']; + } + $originalCondition = trim( $originalCondition ); + + // Negate the condition. + $negatedCondition = $this->negateCondition( $originalCondition ); + + // Get the if body content. + $ifBodyContent = ''; + + for ( $i = $ifOpener + 1; $i < $ifCloser; $i++ ) { + $ifBodyContent .= $tokens[ $i ]['content']; + } + + // Get the else body content. + $elseBodyContent = ''; + + for ( $i = $elseOpener + 1; $i < $elseCloser; $i++ ) { + $elseBodyContent .= $tokens[ $i ]['content']; + } + + // Get the return statement. + $returnStatement = ''; + + for ( $i = $returnToken; $i <= $returnSemicolon; $i++ ) { + $returnStatement .= $tokens[ $i ]['content']; + } + + // Dedent the if body (it will become the code after the early return). + $ifBodyContent = $this->dedentCode( $ifBodyContent ); + + // Get the indentation. + $ifIndent = $this->getIndentation( $phpcsFile, $ifToken ); + + $fixer->beginChangeset(); + + // Replace the condition. + for ( $i = $conditionOpener + 1; $i < $conditionCloser; $i++ ) { + $fixer->replaceToken( $i, '' ); + } + $fixer->addContent( $conditionOpener, ' ' . $negatedCondition . ' ' ); + + // Replace the if body with the else body + return. + $newIfBody = rtrim( $elseBodyContent ) . $phpcsFile->eolChar . $ifIndent . "\t" . $returnStatement; + + for ( $i = $ifOpener + 1; $i < $ifCloser; $i++ ) { + $fixer->replaceToken( $i, '' ); + } + $fixer->addContent( $ifOpener, $phpcsFile->eolChar . $newIfBody . $phpcsFile->eolChar . $ifIndent ); + + // Remove the else keyword, braces, and content. + for ( $i = $ifCloser + 1; $i <= $elseCloser; $i++ ) { + $fixer->replaceToken( $i, '' ); + } + + // Remove the trailing return statement (we'll add it back at the end). + for ( $i = $elseCloser + 1; $i <= $returnSemicolon; $i++ ) { + $fixer->replaceToken( $i, '' ); + } + + // Add the dedented if body after the if's closing brace, followed by the return statement. + $ifBodyContent = rtrim( $ifBodyContent ); + $fixer->addContent( + $ifCloser, + $phpcsFile->eolChar . $phpcsFile->eolChar . + $ifIndent . ltrim( $ifBodyContent ) . $phpcsFile->eolChar . + $phpcsFile->eolChar . + $ifIndent . $returnStatement . $phpcsFile->eolChar + ); + + $fixer->endChangeset(); + } + + /** + * Negate a condition string. + * + * @param string $condition The original condition. + * + * @return string The negated condition. + */ + private function negateCondition( $condition ) { + $condition = trim( $condition ); + + // Check if this is a compound condition (has && or || at top level). + $isCompound = $this->hasTopLevelOperator( $condition ); + + // Only remove leading ! if it's a simple condition (not compound). + if ( ! $isCompound ) { + // If condition starts with !, remove it. + if ( strpos( $condition, '! ' ) === 0 ) { + return substr( $condition, 2 ); + } + + if ( strpos( $condition, '!' ) === 0 && strpos( $condition, '!=' ) !== 0 ) { + return substr( $condition, 1 ); + } + + // Try to flip comparison operators. + $flipped = $this->flipComparisonOperator( $condition ); + + if ( $flipped !== false ) { + return $flipped; + } + + // Simple condition, just add ! + return '! ' . $condition; + } + + // For compound conditions, wrap in parentheses and negate. + return '! ( ' . $condition . ' )'; + } + + /** + * Check if a condition has && or || at the top level (not inside parentheses). + * + * @param string $condition The condition to check. + * + * @return bool + */ + private function hasTopLevelOperator( $condition ) { + $parenDepth = 0; + $len = strlen( $condition ); + + for ( $i = 0; $i < $len; $i++ ) { + $char = $condition[ $i ]; + + if ( $char === '(' ) { + ++$parenDepth; + continue; + } + + if ( $char === ')' ) { + --$parenDepth; + continue; + } + + // Only check at top level. + if ( $parenDepth !== 0 ) { + continue; + } + + // Check for && or ||. + if ( $i < $len - 1 ) { + $twoChars = $condition[ $i ] . $condition[ $i + 1 ]; + + if ( $twoChars === '&&' || $twoChars === '||' ) { + return true; + } + } + } + + return false; + } + + /** + * Try to flip a comparison operator in a condition. + * + * @param string $condition The condition to flip. + * + * @return false|string The flipped condition, or false if not a simple comparison. + */ + private function flipComparisonOperator( $condition ) { + // Map of operators to their opposites. + $operatorMap = array( + '!==' => '===', + '===' => '!==', + '!=' => '==', + '==' => '!=', + '>=' => '<', + '<=' => '>', + '>' => '<=', + '<' => '>=', + ); + + // Check for each operator (check longer ones first). + foreach ( $operatorMap as $op => $opposite ) { + $pos = strpos( $condition, $op ); + + if ( $pos !== false ) { + // Make sure this is a simple comparison (no && or ||). + if ( strpos( $condition, '&&' ) !== false || strpos( $condition, '||' ) !== false ) { + return false; + } + + return substr( $condition, 0, $pos ) . $opposite . substr( $condition, $pos + strlen( $op ) ); + } + } + + return false; + } + + /** + * Get the indentation of a token. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr The token position. + * + * @return string The indentation string. + */ + private function getIndentation( File $phpcsFile, $stackPtr ) { + $tokens = $phpcsFile->getTokens(); + + // Find the first token on this line. + $lineStart = $stackPtr; + + while ( $lineStart > 0 && $tokens[ $lineStart - 1 ]['line'] === $tokens[ $stackPtr ]['line'] ) { + --$lineStart; + } + + // If the first token is whitespace, that's our indentation. + if ( $tokens[ $lineStart ]['code'] === T_WHITESPACE ) { + return $tokens[ $lineStart ]['content']; + } + + return ''; + } + + /** + * Remove one level of indentation from code. + * + * @param string $code The code to dedent. + * + * @return string The dedented code. + */ + private function dedentCode( $code ) { + $lines = explode( "\n", $code ); + $result = array(); + + foreach ( $lines as $line ) { + // Remove one tab or 4 spaces from the beginning. + if ( strpos( $line, "\t" ) === 0 ) { + $line = substr( $line, 1 ); + } elseif ( strpos( $line, ' ' ) === 0 ) { + $line = substr( $line, 4 ); + } + $result[] = $line; + } + + return implode( "\n", $result ); + } +} diff --git a/phpcs-sniffs/Formidable/ruleset.xml b/phpcs-sniffs/Formidable/ruleset.xml index 738062b671..171cf44d31 100644 --- a/phpcs-sniffs/Formidable/ruleset.xml +++ b/phpcs-sniffs/Formidable/ruleset.xml @@ -29,6 +29,7 @@ + @@ -42,7 +43,7 @@ - + diff --git a/stripe/helpers/FrmTransLiteAppHelper.php b/stripe/helpers/FrmTransLiteAppHelper.php index ac841050cd..5d5d71f644 100755 --- a/stripe/helpers/FrmTransLiteAppHelper.php +++ b/stripe/helpers/FrmTransLiteAppHelper.php @@ -342,16 +342,15 @@ public static function format_amount_for_currency( $currency, &$amount ) { * @return string */ public static function get_date_format() { - $date_format = 'm/d/Y'; + if ( ! class_exists( 'FrmProAppHelper' ) ) { + return get_option( 'date_format' ); + } - if ( class_exists( 'FrmProAppHelper' ) ) { - $frmpro_settings = FrmProAppHelper::get_settings(); + $date_format = 'm/d/Y'; + $frmpro_settings = FrmProAppHelper::get_settings(); - if ( $frmpro_settings ) { - $date_format = $frmpro_settings->date_format; - } - } else { - $date_format = get_option( 'date_format' ); + if ( $frmpro_settings ) { + $date_format = $frmpro_settings->date_format; } return $date_format; diff --git a/stripe/helpers/FrmTransLiteListHelper.php b/stripe/helpers/FrmTransLiteListHelper.php index 47d0fb02cd..e6481a530c 100755 --- a/stripe/helpers/FrmTransLiteListHelper.php +++ b/stripe/helpers/FrmTransLiteListHelper.php @@ -88,19 +88,19 @@ private function get_table_query() { $table_name = $this->table === 'subscriptions' ? 'frm_subscriptions' : 'frm_payments'; $form_id = FrmAppHelper::get_param( 'form', 0, 'get', 'absint' ); - if ( $form_id ) { - // @codingStandardsIgnoreStart - $query = $wpdb->prepare( - "FROM `{$wpdb->prefix}{$table_name}` p - JOIN `{$wpdb->prefix}frm_items` i ON p.item_id = i.id - WHERE i.form_id = %d", - $form_id - ); - // @codingStandardsIgnoreEnd - } else { - $query = "FROM `{$wpdb->prefix}{$table_name}` p"; + if ( ! $form_id ) { + return "FROM `{$wpdb->prefix}{$table_name}` p"; } + // @codingStandardsIgnoreStart + $query = $wpdb->prepare( + "FROM `{$wpdb->prefix}{$table_name}` p + JOIN `{$wpdb->prefix}frm_items` i ON p.item_id = i.id + WHERE i.form_id = %d", + $form_id + ); + // @codingStandardsIgnoreEnd + return $query; } From a8ffe705cdc9db189378141d81bab6148d225932 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 19 Jan 2026 13:25:58 -0400 Subject: [PATCH 2/6] Fix funny negation --- classes/models/fields/FrmFieldType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/models/fields/FrmFieldType.php b/classes/models/fields/FrmFieldType.php index 95450a4934..d8b84ada7e 100644 --- a/classes/models/fields/FrmFieldType.php +++ b/classes/models/fields/FrmFieldType.php @@ -1002,7 +1002,7 @@ public function prepare_field_html( $args ) { $args = $this->fill_display_field_values( $args ); - if ( $this - <= has_html ) { + if ( ! $this->has_html ) { return $this->include_front_field_input( $args, array() ); } From 7628e97ea1eb6d7d339859708920747b2abe9a52 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 19 Jan 2026 13:26:51 -0400 Subject: [PATCH 3/6] Update sniff to avoid funny negation --- .../Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php index d3ea461d2c..4f2880845b 100644 --- a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php @@ -388,6 +388,11 @@ private function flipComparisonOperator( $condition ) { return false; } + // Skip if this is part of -> (object operator). + if ( $op === '>' && $pos > 0 && $condition[ $pos - 1 ] === '-' ) { + continue; + } + return substr( $condition, 0, $pos ) . $opposite . substr( $condition, $pos + strlen( $op ) ); } } From f5a85006fc281a382bff24b5bb04e9902c6df96d Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 19 Jan 2026 13:31:10 -0400 Subject: [PATCH 4/6] Run rector --- classes/helpers/FrmFieldsHelper.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/classes/helpers/FrmFieldsHelper.php b/classes/helpers/FrmFieldsHelper.php index 5274bf89ad..b8ac2b31ef 100644 --- a/classes/helpers/FrmFieldsHelper.php +++ b/classes/helpers/FrmFieldsHelper.php @@ -1812,8 +1812,7 @@ public static function switch_field_ids( $val ) { }//end foreach if ( ! is_array( $val ) ) { - $val = str_replace( $replace, $replace_with, $val ); - return $val; + return str_replace( $replace, $replace_with, $val ); } foreach ( $val as $k => $v ) { From 4d08d227215974c98a5fc20f877a093e14324b11 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 19 Jan 2026 13:32:07 -0400 Subject: [PATCH 5/6] Fix field type --- classes/helpers/FrmFieldsHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/helpers/FrmFieldsHelper.php b/classes/helpers/FrmFieldsHelper.php index b8ac2b31ef..096fae40d7 100644 --- a/classes/helpers/FrmFieldsHelper.php +++ b/classes/helpers/FrmFieldsHelper.php @@ -1773,9 +1773,9 @@ public static function get_other_field_html_id( $type, $html_id, $opt_key = fals } /** - * @param string $val + * @param array|string $val * - * @return string + * @return array|string */ public static function switch_field_ids( $val ) { global $frm_duplicate_ids; From d0e294f6f12ea74906d671d16cea598320b25354 Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Mon, 19 Jan 2026 13:38:18 -0400 Subject: [PATCH 6/6] Ignore phpstan issue for now --- phpstan.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.neon b/phpstan.neon index c3b0815560..53a0a6d0c3 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -372,3 +372,4 @@ parameters: message: "#Casting to int something that's already int#" paths: - classes/helpers/FrmXMLHelper.php + - '#Method FrmShortcodeHelper::get_shortcode_tag\(\) should return string but returns array\|string#'