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..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;
@@ -1811,21 +1811,21 @@ 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 ) ) {
+ return str_replace( $replace, $replace_with, $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..d8b84ada7e 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..4f2880845b
--- /dev/null
+++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipLargeIfSmallElseSniff.php
@@ -0,0 +1,452 @@
+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;
+ }
+
+ // 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 ) );
+ }
+ }
+
+ 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/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#'
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;
}