From 621bc64e218408f367f5c61bb943ac7a1e7ab03a Mon Sep 17 00:00:00 2001 From: Mike Letellier Date: Fri, 16 Jan 2026 14:58:11 -0400 Subject: [PATCH] Update sniff to catch more variables declared before returns where they are unused --- classes/controllers/FrmAddonsController.php | 3 +- classes/controllers/FrmAppController.php | 6 +- .../controllers/FrmFormActionsController.php | 3 +- .../controllers/FrmWelcomeTourController.php | 3 +- classes/helpers/FrmAppHelper.php | 10 +- classes/helpers/FrmFieldsHelper.php | 60 +-- classes/helpers/FrmFormsHelper.php | 9 +- classes/helpers/FrmListHelper.php | 2 +- classes/helpers/FrmSubmitHelper.php | 4 +- classes/helpers/FrmXMLHelper.php | 3 +- classes/models/FrmApplicationTemplate.php | 7 +- classes/models/FrmField.php | 3 +- classes/models/FrmFormAction.php | 3 +- classes/models/FrmFormApi.php | 3 +- classes/models/FrmInbox.php | 4 +- classes/models/FrmSpamCheckUseWPComments.php | 3 +- classes/models/fields/FrmFieldCaptcha.php | 9 +- classes/models/fields/FrmFieldName.php | 3 +- .../MoveVariableBelowEarlyReturnSniff.php | 435 +++++++++++++----- square/helpers/FrmSquareLiteConnectHelper.php | 3 +- .../FrmStrpLiteActionsController.php | 5 +- .../controllers/FrmStrpLiteAppController.php | 3 +- .../helpers/FrmStrpLiteConnectApiAdapter.php | 3 +- stripe/helpers/FrmStrpLiteConnectHelper.php | 3 +- 24 files changed, 420 insertions(+), 170 deletions(-) diff --git a/classes/controllers/FrmAddonsController.php b/classes/controllers/FrmAddonsController.php index b6f51c6cf1..0eda8ee0ae 100644 --- a/classes/controllers/FrmAddonsController.php +++ b/classes/controllers/FrmAddonsController.php @@ -769,13 +769,14 @@ protected static function get_license_type() { */ public static function get_addon_for_license( $addons, $license ) { $download_id = $license->download_id; - $plugin = array(); if ( ! $download_id && ! empty( $addons ) ) { foreach ( $addons as $addon ) { if ( strtolower( $license->plugin_name ) === strtolower( $addon['title'] ) ) { return $addon; } + + $plugin = array(); } } elseif ( isset( $addons[ $download_id ] ) ) { $plugin = $addons[ $download_id ]; diff --git a/classes/controllers/FrmAppController.php b/classes/controllers/FrmAppController.php index 9c0cf6de75..ea72043c3d 100644 --- a/classes/controllers/FrmAppController.php +++ b/classes/controllers/FrmAppController.php @@ -278,13 +278,13 @@ public static function get_form_nav( $form, $show_nav = false, $title = 'show' ) * @return string */ private static function get_current_page() { - $page = FrmAppHelper::simple_get( 'page', 'sanitize_title' ); - $post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title', 'None' ); - if ( FrmAppHelper::is_view_builder_page() ) { return 'frm_display'; } + $post_type = FrmAppHelper::simple_get( 'post_type', 'sanitize_title', 'None' ); + $page = FrmAppHelper::simple_get( 'page', 'sanitize_title' ); + return isset( $_GET['page'] ) ? $page : $post_type; } diff --git a/classes/controllers/FrmFormActionsController.php b/classes/controllers/FrmFormActionsController.php index 3872d7d195..540d31fed0 100644 --- a/classes/controllers/FrmFormActionsController.php +++ b/classes/controllers/FrmFormActionsController.php @@ -333,13 +333,14 @@ public static function get_form_actions( $action = 'all' ) { $temp_actions = $temp_actions->actions; } - $actions = array(); foreach ( $temp_actions as $a ) { if ( 'all' !== $action && $a->id_base === $action ) { return $a; } + $actions = array(); + $actions[ $a->id_base ] = $a; } diff --git a/classes/controllers/FrmWelcomeTourController.php b/classes/controllers/FrmWelcomeTourController.php index 1ca581a706..cf71b2e308 100644 --- a/classes/controllers/FrmWelcomeTourController.php +++ b/classes/controllers/FrmWelcomeTourController.php @@ -279,7 +279,6 @@ public static function maybe_mark_welcome_tour_as_seen() { * @return void */ public static function render() { - $view_path = FrmAppHelper::plugin_path() . '/classes/views/welcome-tour/'; $is_tour_completed = self::is_tour_completed(); $current_form_id = self::get_current_form_id(); @@ -287,6 +286,8 @@ public static function render() { if ( ! $current_form_id ) { return; } + + $view_path = FrmAppHelper::plugin_path() . '/classes/views/welcome-tour/'; self::mark_completed_as_seen(); $steps_path = $view_path . 'steps/step-completed.php'; } else { diff --git a/classes/helpers/FrmAppHelper.php b/classes/helpers/FrmAppHelper.php index ef97802b1e..bac23761be 100644 --- a/classes/helpers/FrmAppHelper.php +++ b/classes/helpers/FrmAppHelper.php @@ -3060,27 +3060,29 @@ public static function truncate( $original_string, $length, $minword = 3, $conti return ''; } - $length = (int) $length; - $str = wp_strip_all_tags( (string) $original_string ); - $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) ); + $length = (int) $length; if ( $length === 0 ) { return ''; } + $str = wp_strip_all_tags( (string) $original_string ); + $original_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $str ) ); + if ( $length <= 10 ) { $sub = self::mb_function( array( 'mb_substr', 'substr' ), array( $str, 0, $length ) ); return $sub . ( $length < $original_len ? $continue : '' ); } $sub = ''; - $len = 0; $words = self::mb_function( array( 'mb_split', 'explode' ), array( ' ', $str ) ); if ( ! is_array( $words ) ) { return $original_string; } + $len = 0; + foreach ( $words as $word ) { $part = ( $sub !== '' ? ' ' : '' ) . $word; $total_len = self::mb_function( array( 'mb_strlen', 'strlen' ), array( $sub . $part ) ); diff --git a/classes/helpers/FrmFieldsHelper.php b/classes/helpers/FrmFieldsHelper.php index 4b19d38809..502856f70d 100644 --- a/classes/helpers/FrmFieldsHelper.php +++ b/classes/helpers/FrmFieldsHelper.php @@ -227,45 +227,46 @@ private static function context_is_safe_to_load_field_options_from_request_data( if ( isset( self::$context_is_safe_to_load_field_options_from_request_data ) ) { return self::$context_is_safe_to_load_field_options_from_request_data; } + } + + if ( ! $_POST || ! isset( $_POST['field_options'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + return false; + } + + if ( ! current_user_can( 'frm_edit_forms' ) ) { + return false; + } $function = function () { if ( ! FrmAppHelper::is_admin_page() ) { return false; - } - if ( ! $_POST || ! isset( $_POST['field_options'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing - return false; - } - - if ( ! current_user_can( 'frm_edit_forms' ) ) { - return false; - } + $action = FrmAppHelper::get_post_param( 'action', '', 'sanitize_title' ); - $action = FrmAppHelper::get_post_param( 'action', '', 'sanitize_title' ); + if ( 'frm_forms_preview' === $action ) { + // Never trigger when previewing. + return false; + } - if ( 'frm_forms_preview' === $action ) { - // Never trigger when previewing. - return false; - } + // Confirm an allowed action is being used, and that the correct nonce is being used. + if ( 'update' === $action ) { + $nonce = FrmAppHelper::get_post_param( 'frm_save_form', '', 'sanitize_text_field' ); + return wp_verify_nonce( $nonce, 'frm_save_form_nonce' ); + } - // Confirm an allowed action is being used, and that the correct nonce is being used. - if ( 'update' === $action ) { - $nonce = FrmAppHelper::get_post_param( 'frm_save_form', '', 'sanitize_text_field' ); - return wp_verify_nonce( $nonce, 'frm_save_form_nonce' ); - } + $action = FrmAppHelper::get_post_param( 'frm_action', '', 'sanitize_title' ); - $action = FrmAppHelper::get_post_param( 'frm_action', '', 'sanitize_title' ); + if ( 'update_settings' === $action ) { + $nonce = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' ); + return wp_verify_nonce( $nonce, 'process_form_nonce' ); + } + };//end if - if ( 'update_settings' === $action ) { - $nonce = FrmAppHelper::get_post_param( 'process_form', '', 'sanitize_text_field' ); - return wp_verify_nonce( $nonce, 'process_form_nonce' ); - } - }; - self::$context_is_safe_to_load_field_options_from_request_data = $function(); + self::$context_is_safe_to_load_field_options_from_request_data = $function(); - return self::$context_is_safe_to_load_field_options_from_request_data; - } + return self::$context_is_safe_to_load_field_options_from_request_data; + } /** * Fill the required message, invalid message, @@ -1529,8 +1530,6 @@ public static function get_other_val( $args ) { // phpcs:ignore SlevomatCodingSt $args = wp_parse_args( $args, $defaults ); $opt_key = $args['opt_key']; $field = $args['field']; - $parent = $args['parent']; - $pointer = $args['pointer']; $other_val = ''; // If option is an "other" option and there is a value set for this field, @@ -1539,6 +1538,9 @@ public static function get_other_val( $args ) { // phpcs:ignore SlevomatCodingSt return $other_val; } + $pointer = $args['pointer']; + $parent = $args['parent']; + // Check posted vals before checking saved values // For fields inside repeating sections - note, don't check if $pointer is true because it will often be zero if ( $parent && isset( $_POST['item_meta'][ $parent ][ $pointer ]['other'][ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing diff --git a/classes/helpers/FrmFormsHelper.php b/classes/helpers/FrmFormsHelper.php index 41e1a65165..988d354d39 100644 --- a/classes/helpers/FrmFormsHelper.php +++ b/classes/helpers/FrmFormsHelper.php @@ -1853,15 +1853,16 @@ private static function get_unsafe_params( $url ) { * @return bool|string A string with an unsafe param message or false. */ private static function create_unsafe_param_warning( $unsafe_params_in_redirect ) { - $count = count( $unsafe_params_in_redirect ); - $caution = esc_html__( 'Is this intentional?', 'formidable' ); - $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' ); - $reserved_words_link = ' ' . $reserved_words_intro . ''; + $count = count( $unsafe_params_in_redirect ); if ( $count === 0 ) { return false; } + $reserved_words_intro = esc_html__( 'See the list of reserved words in WordPress.', 'formidable' ); + $reserved_words_link = ' ' . $reserved_words_intro . ''; + $caution = esc_html__( 'Is this intentional?', 'formidable' ); + if ( $count === 1 ) { /* translators: %s: the name of a single parameter in the redirect URL */ return sprintf( esc_html__( 'The redirect URL is using the parameter "%s", which is reserved by WordPress. ', 'formidable' ), $unsafe_params_in_redirect[0] ) . $caution . $reserved_words_link; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong diff --git a/classes/helpers/FrmListHelper.php b/classes/helpers/FrmListHelper.php index c01848e6da..2144b9abd3 100644 --- a/classes/helpers/FrmListHelper.php +++ b/classes/helpers/FrmListHelper.php @@ -500,12 +500,12 @@ private function get_bulk_action( $action_name ) { */ protected function row_actions( $actions, $always_visible = false ) { $action_count = count( $actions ); - $i = 0; if ( ! $action_count ) { return ''; } + $i = 0; $out = '
'; foreach ( $actions as $action => $link ) { diff --git a/classes/helpers/FrmSubmitHelper.php b/classes/helpers/FrmSubmitHelper.php index 9148e558ce..67ef3d22ee 100644 --- a/classes/helpers/FrmSubmitHelper.php +++ b/classes/helpers/FrmSubmitHelper.php @@ -201,13 +201,13 @@ public static function remove_submit_field_from_list( &$fields ) { * @return false|object Return the last found submit field, or `false` if there is at least another field. */ public static function only_contains_submit_field( $fields ) { - $submit_field = false; - foreach ( $fields as $field ) { if ( self::FIELD_TYPE !== FrmField::get_field_type( $field ) ) { return false; } + $submit_field = false; + $submit_field = $field; } diff --git a/classes/helpers/FrmXMLHelper.php b/classes/helpers/FrmXMLHelper.php index 8896040477..549b107494 100644 --- a/classes/helpers/FrmXMLHelper.php +++ b/classes/helpers/FrmXMLHelper.php @@ -1796,7 +1796,6 @@ private static function add_image_src_to_image_options( $field ) { return; } - $updated = false; $options = $field->options; FrmAppHelper::unserialize_or_decode( $options ); @@ -1804,6 +1803,8 @@ private static function add_image_src_to_image_options( $field ) { return; } + $updated = false; + foreach ( $options as $key => $option ) { if ( is_array( $option ) && ! empty( $option['image'] ) ) { $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] ); diff --git a/classes/models/FrmApplicationTemplate.php b/classes/models/FrmApplicationTemplate.php index 467bcd1f4d..5499e3aecf 100644 --- a/classes/models/FrmApplicationTemplate.php +++ b/classes/models/FrmApplicationTemplate.php @@ -246,9 +246,8 @@ private function is_available_for_purchase() { return false; } - $license_type = ''; - $api = new FrmFormApi(); - $addons = $api->get_api_info(); + $api = new FrmFormApi(); + $addons = $api->get_api_info(); if ( ! array_key_exists( 93790, $addons ) ) { return false; @@ -260,6 +259,8 @@ private function is_available_for_purchase() { return false; } + $license_type = ''; + $license_type = strtolower( $pro['type'] ); $args = array( 'license_type' => $license_type, diff --git a/classes/models/FrmField.php b/classes/models/FrmField.php index 7be0aa6c88..9a95dc9b23 100644 --- a/classes/models/FrmField.php +++ b/classes/models/FrmField.php @@ -433,12 +433,13 @@ public static function create( $values, $return = true ) { } self::delete_form_transient( $new_values['form_id'] ); - $new_id = $wpdb->insert_id; if ( ! $return ) { return false; } + $new_id = $wpdb->insert_id; + if ( isset( $values['id'] ) ) { $frm_duplicate_ids[ $values['id'] ] = $new_id; } diff --git a/classes/models/FrmFormAction.php b/classes/models/FrmFormAction.php index 1d47ef931a..745157b9f5 100644 --- a/classes/models/FrmFormAction.php +++ b/classes/models/FrmFormAction.php @@ -459,13 +459,14 @@ private function duplicate_array_walk( $action, $subkey, $val ) { public function update_callback( $form_id ) { $this->form_id = $form_id; - $all_instances = $this->get_settings(); // We need to update the data if ( $this->updated ) { return null; } + $all_instances = $this->get_settings(); + // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( isset( $_POST[ $this->option_name ] ) && is_array( $_POST[ $this->option_name ] ) ) { // Sanitizing removes scripts and type of values. diff --git a/classes/models/FrmFormApi.php b/classes/models/FrmFormApi.php index f85bd5464e..572b9f9995 100644 --- a/classes/models/FrmFormApi.php +++ b/classes/models/FrmFormApi.php @@ -258,13 +258,14 @@ public function get_addon_for_license( $license_plugin, $addons = array() ) { } $download_id = $license_plugin->download_id; - $plugin = array(); if ( ! $download_id && ! empty( $addons ) ) { foreach ( $addons as $addon ) { if ( is_array( $addon ) && ! empty( $addon['title'] ) && strtolower( $license_plugin->plugin_name ) === strtolower( $addon['title'] ) ) { return $addon; } + + $plugin = array(); } } elseif ( isset( $addons[ $download_id ] ) ) { $plugin = $addons[ $download_id ]; diff --git a/classes/models/FrmInbox.php b/classes/models/FrmInbox.php index 0a8adeac0c..02510f27e2 100644 --- a/classes/models/FrmInbox.php +++ b/classes/models/FrmInbox.php @@ -447,13 +447,13 @@ private static function get_prepared_banner_cta( $cta ) { * @return string */ function ( $matches ) { - $url = $matches[2]; - $parts = parse_url( $url ); + $url = $matches[2]; if ( '#' === $url ) { return 'href="#"'; } + $parts = parse_url( $url ); $query = array(); if ( isset( $parts['query'] ) ) { diff --git a/classes/models/FrmSpamCheckUseWPComments.php b/classes/models/FrmSpamCheckUseWPComments.php index 420fee2d43..5360f89b6c 100644 --- a/classes/models/FrmSpamCheckUseWPComments.php +++ b/classes/models/FrmSpamCheckUseWPComments.php @@ -31,13 +31,14 @@ protected function check() { $ip_address = FrmAppHelper::get_ip_address(); $whitelist_ip = FrmAntiSpamController::get_allowed_ips(); $is_whitelist_ip = in_array( $ip_address, $whitelist_ip, true ); - $item_meta = FrmAppHelper::array_flatten( $this->values['item_meta'] ); foreach ( $spam_comments as $comment ) { if ( ! $is_whitelist_ip && $ip_address === $comment->comment_author_IP ) { return true; } + $item_meta = FrmAppHelper::array_flatten( $this->values['item_meta'] ); + foreach ( $item_meta as $value ) { if ( ! $value ) { continue; diff --git a/classes/models/fields/FrmFieldCaptcha.php b/classes/models/fields/FrmFieldCaptcha.php index 9d50b6ba84..a84ba51263 100644 --- a/classes/models/fields/FrmFieldCaptcha.php +++ b/classes/models/fields/FrmFieldCaptcha.php @@ -288,10 +288,9 @@ protected function allow_multiple( $frm_settings ) { * @return array */ protected function validate_against_api( $args ) { - $errors = array(); - $frm_settings = FrmAppHelper::get_settings(); - $resp = $this->send_api_check(); - $response = json_decode( wp_remote_retrieve_body( $resp ), true ); + $errors = array(); + $resp = $this->send_api_check(); + $response = json_decode( wp_remote_retrieve_body( $resp ), true ); if ( is_wp_error( $resp ) ) { $error_string = $resp->get_error_message(); @@ -304,6 +303,8 @@ protected function validate_against_api( $args ) { return $errors; } + $frm_settings = FrmAppHelper::get_settings(); + if ( $frm_settings->active_captcha === 'recaptcha' ) { if ( 'v3' === $frm_settings->re_type && array_key_exists( 'score', $response ) ) { $threshold = floatval( $frm_settings->re_threshold ); diff --git a/classes/models/fields/FrmFieldName.php b/classes/models/fields/FrmFieldName.php index bc2a25c922..7773aac3e5 100644 --- a/classes/models/fields/FrmFieldName.php +++ b/classes/models/fields/FrmFieldName.php @@ -148,12 +148,13 @@ protected function prepare_display_value( $value, $atts ) { return $value; } - $name_layout = $this->get_name_layout(); if ( ! empty( $atts['show'] ) ) { return $value[ $atts['show'] ] ?? ''; } + $name_layout = $this->get_name_layout(); + $value = wp_parse_args( $value, array( diff --git a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/MoveVariableBelowEarlyReturnSniff.php b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/MoveVariableBelowEarlyReturnSniff.php index e6ea4e6c53..f187943b0e 100644 --- a/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/MoveVariableBelowEarlyReturnSniff.php +++ b/phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/MoveVariableBelowEarlyReturnSniff.php @@ -36,6 +36,36 @@ * * return $style; * } + * + * Also handles variables declared before an early return that are only used after it: + * + * Bad: + * function example($id, $form_id) { + * $entry_ids = get_entries($form_id); + * $total = count($entry_ids); + * $position = array_search($id, $entry_ids); + * + * if ($position === false) { + * return; + * } + * + * $prev = $position > 0 ? $entry_ids[$position - 1] : null; + * $next = $position < $total - 1 ? $entry_ids[$position + 1] : null; + * } + * + * Good: + * function example($id, $form_id) { + * $entry_ids = get_entries($form_id); + * $position = array_search($id, $entry_ids); + * + * if ($position === false) { + * return; + * } + * + * $total = count($entry_ids); + * $prev = $position > 0 ? $entry_ids[$position - 1] : null; + * $next = $position < $total - 1 ? $entry_ids[$position + 1] : null; + * } */ class MoveVariableBelowEarlyReturnSniff implements Sniff { @@ -67,113 +97,234 @@ public function process( File $phpcsFile, $stackPtr ) { $functionOpener = $tokens[ $stackPtr ]['scope_opener']; $functionCloser = $tokens[ $stackPtr ]['scope_closer']; - // Find the first statement in the function. - $firstStatement = $phpcsFile->findNext( T_WHITESPACE, $functionOpener + 1, $functionCloser, true ); + // Find all early return if statements in the function. + $earlyReturns = $this->findEarlyReturns( $phpcsFile, $functionOpener, $functionCloser ); - if ( false === $firstStatement ) { + if ( empty( $earlyReturns ) ) { return; } - // Check if the first statement is a variable assignment. - if ( $tokens[ $firstStatement ]['code'] !== T_VARIABLE ) { - return; + // For each early return, check if there are variables before it that should be moved after. + foreach ( $earlyReturns as $earlyReturn ) { + $this->checkVariablesBeforeEarlyReturn( $phpcsFile, $stackPtr, $functionOpener, $functionCloser, $earlyReturn ); } + } - // Get the variable name. - $variableName = $tokens[ $firstStatement ]['content']; + /** + * Find all early return if statements in a function. + * + * @param File $phpcsFile The file being scanned. + * @param int $functionOpener The function scope opener. + * @param int $functionCloser The function scope closer. + * + * @return array Array of early return info: ['if' => token, 'ifOpener' => token, 'ifCloser' => token]. + */ + private function findEarlyReturns( File $phpcsFile, $functionOpener, $functionCloser ) { + $tokens = $phpcsFile->getTokens(); + $earlyReturns = array(); - // Check if this variable is a function parameter (skip those, especially by-reference params). - if ( $this->isVariableAParameter( $phpcsFile, $tokens, $stackPtr, $variableName ) ) { - return; - } + $current = $functionOpener + 1; - // Find the semicolon ending this assignment. - $assignmentEnd = $phpcsFile->findNext( T_SEMICOLON, $firstStatement + 1, $functionCloser ); + while ( $current < $functionCloser ) { + $current = $phpcsFile->findNext( T_IF, $current, $functionCloser ); - if ( false === $assignmentEnd ) { - return; - } + if ( false === $current ) { + break; + } - // Find the next statement after the assignment. - $nextStatement = $phpcsFile->findNext( T_WHITESPACE, $assignmentEnd + 1, $functionCloser, true ); + // Make sure the if has a scope. + if ( ! isset( $tokens[ $current ]['scope_opener'] ) || ! isset( $tokens[ $current ]['scope_closer'] ) ) { + ++$current; + continue; + } - if ( false === $nextStatement ) { - return; - } + $ifOpener = $tokens[ $current ]['scope_opener']; + $ifCloser = $tokens[ $current ]['scope_closer']; - // Check if the next statement is an if with an early return. - if ( $tokens[ $nextStatement ]['code'] !== T_IF ) { - return; - } + // Check if the if body contains only a return statement. + $ifFirstStatement = $phpcsFile->findNext( T_WHITESPACE, $ifOpener + 1, $ifCloser, true ); - // Make sure the if has a scope. - if ( ! isset( $tokens[ $nextStatement ]['scope_opener'] ) || ! isset( $tokens[ $nextStatement ]['scope_closer'] ) ) { - return; - } + if ( false === $ifFirstStatement || $tokens[ $ifFirstStatement ]['code'] !== T_RETURN ) { + ++$current; + continue; + } - $ifOpener = $tokens[ $nextStatement ]['scope_opener']; - $ifCloser = $tokens[ $nextStatement ]['scope_closer']; + // Check if there's anything else in the if body after the return. + $returnEnd = $phpcsFile->findNext( T_SEMICOLON, $ifFirstStatement + 1, $ifCloser ); - // Check if the if body contains only a return statement. - $ifFirstStatement = $phpcsFile->findNext( T_WHITESPACE, $ifOpener + 1, $ifCloser, true ); + if ( false === $returnEnd ) { + ++$current; + continue; + } - if ( false === $ifFirstStatement || $tokens[ $ifFirstStatement ]['code'] !== T_RETURN ) { - return; - } + $afterReturn = $phpcsFile->findNext( T_WHITESPACE, $returnEnd + 1, $ifCloser, true ); - // Check if there's anything else in the if body after the return. - $returnEnd = $phpcsFile->findNext( T_SEMICOLON, $ifFirstStatement + 1, $ifCloser ); + if ( false !== $afterReturn ) { + // There's more than just a return in the if body. + ++$current; + continue; + } - if ( false === $returnEnd ) { - return; - } + // Check if the if has an else/elseif - skip those. + $afterIfClose = $phpcsFile->findNext( T_WHITESPACE, $ifCloser + 1, $functionCloser, true ); + + if ( false !== $afterIfClose && in_array( $tokens[ $afterIfClose ]['code'], array( T_ELSE, T_ELSEIF ), true ) ) { + ++$current; + continue; + } - $afterReturn = $phpcsFile->findNext( T_WHITESPACE, $returnEnd + 1, $ifCloser, true ); + $earlyReturns[] = array( + 'if' => $current, + 'ifOpener' => $ifOpener, + 'ifCloser' => $ifCloser, + 'returnEnd' => $returnEnd, + ); - if ( false !== $afterReturn ) { - // There's more than just a return in the if body. - return; + $current = $ifCloser + 1; } - // Check if the if has an else/elseif - skip those. - $afterIfClose = $phpcsFile->findNext( T_WHITESPACE, $ifCloser + 1, $functionCloser, true ); + return $earlyReturns; + } - if ( false !== $afterIfClose && in_array( $tokens[ $afterIfClose ]['code'], array( T_ELSE, T_ELSEIF ), true ) ) { - return; - } + /** + * Check variables declared before an early return to see if they should be moved after. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr The function token position. + * @param int $functionOpener The function scope opener. + * @param int $functionCloser The function scope closer. + * @param array $earlyReturn Info about the early return. + * + * @return void + */ + private function checkVariablesBeforeEarlyReturn( File $phpcsFile, $stackPtr, $functionOpener, $functionCloser, array $earlyReturn ) { + $tokens = $phpcsFile->getTokens(); - // Now check if the variable is used in the if condition or the return statement. - $variableUsedInCondition = $this->isVariableUsedInRange( - $phpcsFile, - $tokens, - $nextStatement, - $ifOpener, - $variableName - ); - - $variableUsedInReturn = $this->isVariableUsedInRange( - $phpcsFile, - $tokens, - $ifFirstStatement, - $returnEnd, - $variableName - ); - - if ( $variableUsedInCondition || $variableUsedInReturn ) { - // The variable is used in the early return check, so it's fine where it is. - return; - } + $ifToken = $earlyReturn['if']; + $ifOpener = $earlyReturn['ifOpener']; + $ifCloser = $earlyReturn['ifCloser']; + $returnEnd = $earlyReturn['returnEnd']; - // The variable is not used in the early return - it should be moved below. - $fix = $phpcsFile->addFixableError( - 'Variable %s should be declared after the early return condition, not before.', - $firstStatement, - 'VariableBeforeEarlyReturn', - array( $variableName ) - ); + // Find all variable assignments before this early return. + $current = $functionOpener + 1; + $seenVariables = array(); + + while ( $current < $ifToken ) { + $varToken = $phpcsFile->findNext( T_VARIABLE, $current, $ifToken ); + + if ( false === $varToken ) { + break; + } + + // Skip static property assignments (self::$var, static::$var, ClassName::$var). + $prevToken = $phpcsFile->findPrevious( T_WHITESPACE, $varToken - 1, null, true ); + + if ( false !== $prevToken && $tokens[ $prevToken ]['code'] === T_DOUBLE_COLON ) { + $current = $varToken + 1; + continue; + } - if ( true === $fix ) { - $this->applyFix( $phpcsFile, $tokens, $firstStatement, $assignmentEnd, $ifCloser ); + // Check if this is an assignment (next non-whitespace is =). + $nextToken = $phpcsFile->findNext( T_WHITESPACE, $varToken + 1, null, true ); + + if ( false === $nextToken || $tokens[ $nextToken ]['code'] !== T_EQUAL ) { + $current = $varToken + 1; + continue; + } + + $variableName = $tokens[ $varToken ]['content']; + + // Skip if we've already seen this variable (only consider first declaration). + if ( isset( $seenVariables[ $variableName ] ) ) { + $current = $varToken + 1; + continue; + } + $seenVariables[ $variableName ] = true; + + // Skip if this is a function parameter. + if ( $this->isVariableAParameter( $phpcsFile, $tokens, $stackPtr, $variableName ) ) { + $current = $varToken + 1; + continue; + } + + // Find the semicolon ending this assignment. + $assignmentEnd = $phpcsFile->findNext( T_SEMICOLON, $varToken + 1, $ifToken ); + + if ( false === $assignmentEnd ) { + $current = $varToken + 1; + continue; + } + + // Check if the variable is used in the if condition or the return statement. + $variableUsedInCondition = $this->isVariableUsedInRange( + $phpcsFile, + $tokens, + $ifToken, + $ifOpener, + $variableName + ); + + $variableUsedInReturn = $this->isVariableUsedInRange( + $phpcsFile, + $tokens, + $ifOpener + 1, + $returnEnd, + $variableName + ); + + // Check if the variable is used between its declaration and the early return. + $variableUsedBefore = $this->isVariableUsedInRange( + $phpcsFile, + $tokens, + $assignmentEnd + 1, + $ifToken - 1, + $variableName + ); + + // Check if the variable is reassigned anywhere between its declaration and the early return. + // This handles cases like: $is_setup = false; if (!$x) { $is_setup = true; if (!$y) { return; } } + $variableReassignedBefore = $this->isVariableAssignedInRange( + $phpcsFile, + $tokens, + $assignmentEnd + 1, + $ifCloser - 1, + $variableName + ); + + // Check if the variable is used after the early return. + $variableUsedAfter = $this->isVariableUsedInRange( + $phpcsFile, + $tokens, + $ifCloser + 1, + $functionCloser - 1, + $variableName + ); + + if ( $variableUsedInCondition || $variableUsedInReturn || $variableUsedBefore || $variableReassignedBefore ) { + // The variable is used before or in the early return check, so it's fine where it is. + $current = $assignmentEnd + 1; + continue; + } + + if ( ! $variableUsedAfter ) { + // Variable is not used at all after declaration - skip (might be a different issue). + $current = $assignmentEnd + 1; + continue; + } + + // The variable is only used after the early return - it should be moved below. + $fix = $phpcsFile->addFixableError( + 'Variable %s should be declared after the early return condition, not before.', + $varToken, + 'VariableBeforeEarlyReturn', + array( $variableName ) + ); + + if ( true === $fix ) { + $this->applyFix( $phpcsFile, $tokens, $varToken, $assignmentEnd, $ifCloser ); + } + + $current = $assignmentEnd + 1; } } @@ -191,31 +342,85 @@ public function process( File $phpcsFile, $stackPtr ) { private function applyFix( File $phpcsFile, array $tokens, $varStart, $assignmentEnd, $ifCloser ) { $phpcsFile->fixer->beginChangeset(); - // Get the variable declaration content. - $varDeclaration = ''; + // Check if there's a comment on the line before the variable. + $varLine = $tokens[ $varStart ]['line']; + $commentStart = null; + $commentEnd = null; - for ( $i = $varStart; $i <= $assignmentEnd; $i++ ) { - $varDeclaration .= $tokens[ $i ]['content']; + // Look backwards for a comment on the previous line. + for ( $i = $varStart - 1; $i >= 0; $i-- ) { + if ( $tokens[ $i ]['line'] < $varLine - 1 ) { + break; + } + + if ( $tokens[ $i ]['line'] === $varLine - 1 && $tokens[ $i ]['code'] === T_COMMENT ) { + $commentEnd = $i; + + // Find the start of this comment line. + for ( $j = $i; $j >= 0; $j-- ) { + if ( $tokens[ $j ]['line'] < $varLine - 1 ) { + break; + } + $commentStart = $j; + } + break; + } } - // Find the start of the line for the variable (to get indentation). - $lineStart = $varStart; + // Get the indentation from the whitespace before the variable. + $indent = ''; for ( $i = $varStart - 1; $i >= 0; $i-- ) { - if ( $tokens[ $i ]['line'] < $tokens[ $varStart ]['line'] ) { + if ( $tokens[ $i ]['line'] < $varLine ) { break; } - $lineStart = $i; + + if ( $tokens[ $i ]['code'] === T_WHITESPACE ) { + $indent = $tokens[ $i ]['content']; + } + } + + // Build the content to move. + $contentToMove = ''; + + // Include the comment if found. + if ( null !== $commentStart ) { + // Get comment indentation. + $commentIndent = ''; + + for ( $i = $commentStart; $i <= $commentEnd; $i++ ) { + if ( $tokens[ $i ]['code'] === T_WHITESPACE && $tokens[ $i ]['line'] === $varLine - 1 ) { + $commentIndent = $tokens[ $i ]['content']; + } elseif ( $tokens[ $i ]['code'] === T_COMMENT ) { + $contentToMove .= $commentIndent . rtrim( $tokens[ $i ]['content'] ) . "\n"; + } + } + } + + // Add the variable declaration. + $contentToMove .= $indent; + + for ( $i = $varStart; $i <= $assignmentEnd; $i++ ) { + $contentToMove .= $tokens[ $i ]['content']; } - // Get the indentation based on the variable's column position. - // Column is 1-indexed, so column 2 means 1 character of indentation. - $column = $tokens[ $varStart ]['column']; - $tabCount = (int) floor( ( $column - 1 ) / 4 ); - $indent = str_repeat( "\t", $tabCount ); + // Determine what to remove - start from comment if present, otherwise from line start. + $removeStart = $varStart; + + if ( null !== $commentStart ) { + $removeStart = $commentStart; + } else { + // Find the start of the variable line. + for ( $i = $varStart - 1; $i >= 0; $i-- ) { + if ( $tokens[ $i ]['line'] < $varLine ) { + break; + } + $removeStart = $i; + } + } - // Remove the variable declaration line (from line start to semicolon). - for ( $i = $lineStart; $i <= $assignmentEnd; $i++ ) { + // Remove the content (comment + variable declaration). + for ( $i = $removeStart; $i <= $assignmentEnd; $i++ ) { $phpcsFile->fixer->replaceToken( $i, '' ); } @@ -229,19 +434,15 @@ private function applyFix( File $phpcsFile, array $tokens, $varStart, $assignmen } } - // Add the variable declaration after the if closing brace. - // Find the whitespace after the if closer. + // Add the content after the if closing brace. $afterIfCloser = $ifCloser + 1; if ( isset( $tokens[ $afterIfCloser ] ) && $tokens[ $afterIfCloser ]['code'] === T_WHITESPACE ) { $existingWs = $tokens[ $afterIfCloser ]['content']; - // Add the variable declaration with proper formatting. - // Keep the existing whitespace structure but insert the variable declaration. - $newContent = "\n\n" . $indent . $varDeclaration . $existingWs; + $newContent = "\n\n" . $contentToMove . $existingWs; $phpcsFile->fixer->replaceToken( $afterIfCloser, $newContent ); } else { - // No whitespace after if closer, add it. - $phpcsFile->fixer->addContent( $ifCloser, "\n\n" . $indent . $varDeclaration ); + $phpcsFile->fixer->addContent( $ifCloser, "\n\n" . $contentToMove ); } $phpcsFile->fixer->endChangeset(); @@ -268,6 +469,34 @@ private function isVariableUsedInRange( File $phpcsFile, array $tokens, $start, return false; } + /** + * Check if a variable is assigned within a range of tokens. + * + * @param File $phpcsFile The file being scanned. + * @param array $tokens The token stack. + * @param int $start Start position. + * @param int $end End position. + * @param string $variableName The variable name to look for. + * + * @return bool + */ + private function isVariableAssignedInRange( File $phpcsFile, array $tokens, $start, $end, $variableName ) { + for ( $i = $start; $i <= $end; $i++ ) { + if ( $tokens[ $i ]['code'] !== T_VARIABLE || $tokens[ $i ]['content'] !== $variableName ) { + continue; + } + + // Check if the next non-whitespace token is an assignment operator. + $nextToken = $phpcsFile->findNext( T_WHITESPACE, $i + 1, null, true ); + + if ( false !== $nextToken && $tokens[ $nextToken ]['code'] === T_EQUAL ) { + return true; + } + } + + return false; + } + /** * Check if a variable is a function parameter. * diff --git a/square/helpers/FrmSquareLiteConnectHelper.php b/square/helpers/FrmSquareLiteConnectHelper.php index 989428b7df..d945a89d86 100644 --- a/square/helpers/FrmSquareLiteConnectHelper.php +++ b/square/helpers/FrmSquareLiteConnectHelper.php @@ -176,13 +176,14 @@ private static function post_to_connect_server( $action, $additional_body = arra 'frm_square_api_mode' => FrmSquareLiteAppHelper::active_mode(), ); $body = array_merge( $body, $additional_body ); - $url = self::get_url_to_connect_server(); $headers = self::build_headers_for_post(); if ( ! $headers ) { return 'Unable to build headers for post. Is your pro license configured properly?'; } + $url = self::get_url_to_connect_server(); + // (Seconds) default timeout is 5. we want a bit more time to work with. $timeout = 45; diff --git a/stripe/controllers/FrmStrpLiteActionsController.php b/stripe/controllers/FrmStrpLiteActionsController.php index 13ed6fba3e..9c607a162e 100644 --- a/stripe/controllers/FrmStrpLiteActionsController.php +++ b/stripe/controllers/FrmStrpLiteActionsController.php @@ -425,8 +425,6 @@ public static function load_scripts( $form_id ) { return; } - $settings = FrmStrpLiteAppHelper::get_settings(); - $publishable = $settings->get_active_publishable_key(); wp_register_script( 'stripe', @@ -471,6 +469,9 @@ public static function load_scripts( $form_id ) { return; } + $settings = FrmStrpLiteAppHelper::get_settings(); + $publishable = $settings->get_active_publishable_key(); + wp_enqueue_script( 'formidable-stripe', $script_url, diff --git a/stripe/controllers/FrmStrpLiteAppController.php b/stripe/controllers/FrmStrpLiteAppController.php index 4064b344f6..6226745244 100644 --- a/stripe/controllers/FrmStrpLiteAppController.php +++ b/stripe/controllers/FrmStrpLiteAppController.php @@ -140,7 +140,6 @@ private static function maybe_add_payment_error_on_redirect( $errors, $form_id ) return $errors; } - $entry = $details['entry']; $intent = $details['intent']; $payment = $details['payment']; $payment_failed = FrmStrpLiteAuth::payment_failed( $payment, $intent ); @@ -150,6 +149,8 @@ private static function maybe_add_payment_error_on_redirect( $errors, $form_id ) return $errors; } + $entry = $details['entry']; + $cc_field_id = FrmDb::get_var( 'frm_fields', array( diff --git a/stripe/helpers/FrmStrpLiteConnectApiAdapter.php b/stripe/helpers/FrmStrpLiteConnectApiAdapter.php index eb4290a2be..5ecd33d393 100644 --- a/stripe/helpers/FrmStrpLiteConnectApiAdapter.php +++ b/stripe/helpers/FrmStrpLiteConnectApiAdapter.php @@ -23,7 +23,6 @@ public static function initialize_api() { */ public static function cancel_subscription( $sub_id ) { if ( current_user_can( 'administrator' ) ) { - $customer_id = false; } else { $user_id = get_current_user_id(); $customer = self::get_customer_by_id( $user_id ); @@ -32,6 +31,8 @@ public static function cancel_subscription( $sub_id ) { return false; } + $customer_id = false; + $customer_id = $customer->id; } diff --git a/stripe/helpers/FrmStrpLiteConnectHelper.php b/stripe/helpers/FrmStrpLiteConnectHelper.php index bfeb33c4f8..4761fcad7d 100644 --- a/stripe/helpers/FrmStrpLiteConnectHelper.php +++ b/stripe/helpers/FrmStrpLiteConnectHelper.php @@ -151,13 +151,14 @@ private static function post_to_connect_server( $action, $additional_body = arra 'frm_strp_connect_mode' => FrmStrpLiteAppHelper::active_mode(), ); $body = array_merge( $body, $additional_body ); - $url = self::get_url_to_connect_server(); $headers = self::build_headers_for_post(); if ( ! $headers ) { return 'Unable to build headers for post. Is your pro license configured properly?'; } + $url = self::get_url_to_connect_server(); + // (Seconds) default timeout is 5. we want a bit more time to work with. $timeout = 45;