Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
477 changes: 477 additions & 0 deletions classes/controllers/FrmAddressesController.php

Large diffs are not rendered by default.

189 changes: 189 additions & 0 deletions classes/controllers/FrmComboFieldsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

if ( ! defined( 'ABSPATH' ) ) {
die( 'You are not allowed to call this page directly.' );
}

/**
* Base controller for combo fields (fields with multiple sub-fields like Address).
*
* @since x.x
*/
class FrmComboFieldsController {

/**
* Fill values with defaults.
*
* @since x.x
*
* @param mixed $value
* @param array $defaults
*
* @return void
*/
public static function fill_values( &$value, $defaults ) {
$value = $value ? array_merge( $defaults, (array) $value ) : $defaults;
}

/**
* Include placeholder attribute for sub-field.
*
* @since x.x
*
* @param mixed $default_value
* @param string $sub_field
* @param array $field
*
* @return void
*/
public static function include_placeholder( $default_value, $sub_field, $field = array() ) {
if ( ! is_array( $default_value ) ) {
$default_value = array();
}

if ( $field ) {
$use_placeholder = ! FrmField::is_option_empty( $field, 'placeholder' );

if ( ( ! $use_placeholder || empty( $default_value[ $sub_field ] ) ) && $sub_field === 'line1' ) {
// Allow for 'inside' label position.
$default_value[ $sub_field ] = FrmFieldsController::get_default_value_from_name( $field );
$use_placeholder = $default_value[ $sub_field ] !== '';
}

if ( ! $use_placeholder ) {
return;
}
}

if ( ! $default_value ) {
return;
}

$placeholder = $default_value[ $sub_field ] ?? '';
echo ' placeholder="' . esc_attr( $placeholder ) . '" data-placeholder="' . esc_attr( $placeholder ) . '"';
}

/**
* Get dropdown label for select sub-fields.
*
* @since x.x
*
* @param array $atts
*
* @return string
*/
public static function get_dropdown_label( $atts ) {
$default = $atts['sub_field']['placeholder'] ?? ' ';

if ( ! is_string( $default ) ) {
$default = '';
}

/**
* @since x.x
*
* @param string $default
* @param array $atts
*/
$result = apply_filters( 'frm_combo_dropdown_label', $default, $atts );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function apply_filters invoked with 3 parameters, 2 required


The function call is not valid, which will result in a fatal runtime error.


return is_string( $result ) ? $result : $default;
}

/**
* Add attributes to input field.
*
* @since x.x
*
* @param array $atts
*
* @return void
*/
public static function add_atts_to_input( $atts ) {
$placeholder = $atts['field']['placeholder'] ?? '';
$default_value = $atts['field']['default_value'];
$field_obj = FrmFieldFactory::get_field_type( $atts['field']['type'], $atts['field'] );

if ( $atts['field']['type'] === 'address' ) {
/**
* @var FrmFieldAddress $field_obj
*/
$placeholder = $field_obj->address_string_to_array( $placeholder );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

$default_value = $field_obj->address_string_to_array( $default_value );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call to an undefined method FrmFieldType::address_string_to_array()


The method you are trying to call is not defined, which can result in a fatal error.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

self::include_placeholder( $placeholder, $atts['key'], $atts['field'] );
$atts['field']['placeholder'] = '';

$atts['field']['default_value'] = $default_value[ $atts['key'] ] ?? '';

Comment on lines +115 to +119

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align sub-field key contract (name vs key) across combo helpers.

These helpers read $atts['key'], but downstream callers pass name, so placeholder/default/error lookup can silently fail and produce undefined-index notices.

Suggested fix
 public static function add_atts_to_input( $atts ) {
+	$sub_field_key = $atts['key'] ?? $atts['name'] ?? '';
+	if ( '' === $sub_field_key ) {
+		return;
+	}
+
 	$placeholder   = $atts['field']['placeholder'] ?? '';
 	$default_value = $atts['field']['default_value'];
 	$field_obj     = FrmFieldFactory::get_field_type( $atts['field']['type'], $atts['field'] );
@@
-	self::include_placeholder( $placeholder, $atts['key'], $atts['field'] );
+	self::include_placeholder( $placeholder, $sub_field_key, $atts['field'] );
 	$atts['field']['placeholder'] = '';
 
-	$atts['field']['default_value'] = $default_value[ $atts['key'] ] ?? '';
+	$atts['field']['default_value'] = $default_value[ $sub_field_key ] ?? '';
@@
 public static function maybe_add_error_class( $atts ) {
+	$sub_field_key = $atts['key'] ?? $atts['name'] ?? '';
+	if ( '' === $sub_field_key ) {
+		return;
+	}
+
 	$temp_id   = ! empty( $atts['atts']['field_id'] ) ? $atts['atts']['field_id'] : $atts['field']['id'];
-	$has_error = isset( $atts['errors'][ 'field' . $temp_id . '-' . $atts['key'] ] );
+	$has_error = isset( $atts['errors'][ 'field' . $temp_id . '-' . $sub_field_key ] );

Also applies to: 179-180

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@classes/controllers/FrmComboFieldsController.php` around lines 112 - 116, The
FrmComboFieldsController is using $atts['key'] to look up sub-field identifiers
in methods like include_placeholder and when accessing the default_value array,
but downstream callers are passing the sub-field identifier under the 'name' key
instead, causing silent lookup failures and undefined index notices. Replace all
references to $atts['key'] with $atts['name'] in the combo field helper methods
to align the sub-field key contract, specifically in the include_placeholder
function call and the default_value array access around lines 112-116, and apply
the same fix to the corresponding code at lines 179-180.

if ( 'select' === $atts['sub_field']['type'] && ! empty( $atts['field']['read_only'] ) ) {
$atts['sub_field']['atts']['disabled'] = 'disabled';
}

if ( ! empty( $atts['sub_field']['optional'] ) ) {
add_filter( 'frm_field_classes', 'FrmAddressesController::add_optional_class', 20, 2 );
do_action( 'frm_field_input_html', $atts['field'] );
remove_filter( 'frm_field_classes', 'FrmAddressesController::add_optional_class', 20 );
} else {
do_action( 'frm_field_input_html', $atts['field'] );
}

if ( ! isset( $atts['sub_field']['atts'] ) ) {
return;
}

foreach ( $atts['sub_field']['atts'] as $att_name => $att_value ) {
echo ' ' . esc_attr( $att_name ) . '="' . esc_attr( $att_value ) . '"';
}
}

/**
* Include sub-label for field.
*
* @since x.x
*
* @param array $atts
*
* @return void
*/
public static function include_sub_label( $atts ) {
self::show_sub_label( $atts );
}

/**
* Show sub-label for field.
*
* @since x.x
*
* @param array $atts
*
* @return void
*/
public static function show_sub_label( $atts ) {
$field = $atts['field'];
$option_name = $atts['option_name'];

if ( $field[ $option_name ] !== '' ) {
echo '<div class="frm_description">' . wp_kses_post( $field[ $option_name ] ) . '</div>';
Comment on lines +167 to +168

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard sub-label array access before dereferencing.

$field[ $option_name ] is accessed directly; when that key is absent, PHP notices leak into rendering.

Suggested fix
-	if ( $field[ $option_name ] !== '' ) {
+	if ( isset( $field[ $option_name ] ) && '' !== $field[ $option_name ] ) {
 		echo '<div class="frm_description">' . wp_kses_post( $field[ $option_name ] ) . '</div>';
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( $field[ $option_name ] !== '' ) {
echo '<div class="frm_description">' . wp_kses_post( $field[ $option_name ] ) . '</div>';
if ( isset( $field[ $option_name ] ) && '' !== $field[ $option_name ] ) {
echo '<div class="frm_description">' . wp_kses_post( $field[ $option_name ] ) . '</div>';
}
🧰 Tools
🪛 ast-grep (0.44.0)

[warning] 164-164: Avoid side effects in a file that defines symbols
Context: echo '

' . wp_kses_post( $field[ $option_name ] ) . '
';
Note: [CWE-710] Improper Adherence to Coding Standards.

(no-side-effect)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@classes/controllers/FrmComboFieldsController.php` around lines 164 - 165, The
code directly accesses `$field[ $option_name ]` without first verifying that the
key exists in the array, which causes PHP notices when the key is absent. Guard
the array access by using isset() to check if the key exists before attempting
to access it. Modify the condition to first verify that the key is present in
the $field array, and only then check if its value is not empty. This will
prevent undefined array key notices from being rendered.

}
}

/**
* Add error class if field has error.
*
* @since x.x
*
* @param array $atts
*
* @return void
*/
public static function maybe_add_error_class( $atts ) {
$temp_id = ! empty( $atts['atts']['field_id'] ) ? $atts['atts']['field_id'] : $atts['field']['id'];
$has_error = isset( $atts['errors'][ 'field' . $temp_id . '-' . $atts['key'] ] );

if ( $has_error ) {
echo ' frm_blank_field';
}
}
}
4 changes: 2 additions & 2 deletions classes/controllers/FrmFieldsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,10 @@ public static function load_single_field_settings( $atts ) {

$show_upsell_for_unique_value = in_array(
$field['type'],
array( 'address', 'checkbox', 'email', 'name', 'number', 'phone', 'radio', 'text', 'textarea', 'url' ),
array( 'checkbox', 'email', 'name', 'number', 'phone', 'radio', 'text', 'textarea', 'url' ),
true
);
$show_upsell_for_read_only = in_array( $field['type'], array( 'email', 'hidden', 'number', 'phone', 'radio', 'text', 'textarea', 'url' ), true );
$show_upsell_for_read_only = in_array( $field['type'], array( 'address', 'email', 'hidden', 'number', 'phone', 'radio', 'text', 'textarea', 'url' ), true );
$show_upsell_for_before_after_contents = in_array( $field['type'], array( 'email', 'number', 'phone', 'quantity', 'select', 'tag', 'text', 'total', 'url' ), true );
$show_upsell_for_autocomplete = in_array( $field['type'], array( 'text', 'email', 'number' ), true );
$show_upsell_for_visibility = $field['type'] !== 'hidden';
Expand Down
1 change: 1 addition & 0 deletions classes/factories/FrmFieldFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private static function get_field_type_class( $field_type ) {
'captcha' => 'FrmFieldCaptcha',
'name' => 'FrmFieldName',
'credit_card' => 'FrmFieldCreditCard',
'address' => 'FrmFieldAddress',
// Submit button field.
FrmSubmitHelper::FIELD_TYPE => 'FrmFieldSubmit',
FrmFieldGdprHelper::FIELD_TYPE => FrmFieldGdprHelper::get_gdpr_field_class( $field_type ),
Expand Down
5 changes: 5 additions & 0 deletions classes/models/FrmField.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public static function field_selection() {
'name' => __( 'Payment', 'formidable' ),
'icon' => 'frmfont frm_credit_card2_icon',
),
'address' => array(
'name' => __( 'Address', 'formidable' ),
'icon' => 'frmfont frm_location2_icon',
),
FrmSubmitHelper::FIELD_TYPE => array(
'name' => __( 'Submit', 'formidable' ),
'hide' => true,
Expand Down Expand Up @@ -439,6 +443,7 @@ public static function remove_moved_field_types_from_pro( &$pro_fields ) {
unset( $pro_fields['product'] );
unset( $pro_fields['quantity'] );
unset( $pro_fields['total'] );
unset( $pro_fields['address'] );
}

/**
Expand Down
Loading
Loading