PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.9.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.9.0
2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 All 96 releases
sureforms / inc / field-validation.php
field-validation.php
557 lines 22.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Field Validation Class
4 *
5 * Handles all field validation for SureForms
6 *
7 * @package SureForms
8 * @since 1.12.2
9 */
10
11 namespace SRFM\Inc;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Field Validation Class
19 */
20 class Field_Validation {
21 /**
22 * Add block configuration for form fields.
23 *
24 * This function processes blocks in a form and stores their configuration as post meta.
25 * It applies filters to allow extensions to modify block configs and stores processed
26 * values for blocks that need special handling (like upload fields).
27 *
28 * @param array<mixed> $blocks Array of blocks to process.
29 * @param int $form_id Form post ID.
30 * @return void
31 * @since 1.12.2
32 */
33 public static function add_block_config( $blocks, $form_id ) {
34 // Initialize array to store processed block configurations.
35 $block_config = [];
36
37 // Loop through each block.
38 foreach ( $blocks as $block ) {
39 // Ensure $block is an array and has the required structure.
40 if ( ! is_array( $block ) ) {
41 continue;
42 }
43 if ( ! isset( $block['blockName'] ) || ! isset( $block['attrs'] ) || ! is_array( $block['attrs'] ) ) {
44 continue;
45 }
46 // Validate block id.
47 if ( ! array_key_exists( 'block_id', $block['attrs'] ) || empty( $block['attrs']['block_id'] ) || ! is_string( $block['attrs']['block_id'] ) ) {
48 continue;
49 }
50
51 $block_id = sanitize_text_field( $block['attrs']['block_id'] );
52 $block_name = $block['blockName'];
53
54 // Process specific block types.
55 $processed_config = null;
56
57 switch ( $block_name ) {
58 case 'srfm/payment':
59 $processed_config = self::process_payment_block( $block['attrs'], $blocks );
60 break;
61 case 'srfm/dropdown':
62 $processed_config = self::process_dropdown_block( $block['attrs'] );
63 break;
64 case 'srfm/multi-choice':
65 $processed_config = self::process_multichoice_block( $block['attrs'] );
66 break;
67 case 'srfm/number':
68 $processed_config = self::process_number_block( $block['attrs'] );
69 break;
70 case 'srfm/textarea':
71 $processed_config = self::process_textarea_block( $block['attrs'] );
72 break;
73 }
74
75 // If block was processed, store its configuration.
76 if ( null !== $processed_config && ! empty( $processed_config ) ) {
77 $processed_config['block_name'] = $block_name;
78 // Add the slug to the configuration.
79 if ( isset( $block['attrs']['slug'] ) && ! empty( $block['attrs']['slug'] ) ) {
80 $processed_config['slug'] = sanitize_text_field( $block['attrs']['slug'] );
81 }
82
83 $block_config[ $block_id ] = $processed_config;
84 continue;
85 }
86
87 // Allow extensions to process and modify block config.
88 $config = apply_filters( 'srfm_block_config', [ 'block' => $block ] );
89
90 // If block was processed by a filter, add its processed value.
91 if ( isset( $config['processed_value'] ) && ! empty( $config['processed_value'] ) ) {
92 $block_config[ $block_id ] = $config['processed_value'];
93 continue;
94 }
95 }
96
97 // Sync the meta on every save. When $block_config is empty (e.g. a textarea
98 // whose minLength was cleared, with no other blocks needing per-block
99 // validation), we must clear the stored meta — otherwise the previously
100 // saved values keep being used by the validator.
101 if ( ! empty( $block_config ) ) {
102 update_post_meta( $form_id, '_srfm_block_config', $block_config );
103 } else {
104 delete_post_meta( $form_id, '_srfm_block_config' );
105 }
106 }
107
108 /**
109 * Retrieve or migrate the block configuration for legacy forms.
110 *
111 * This function checks if the _srfm_block_config post meta exists for the given form ID.
112 * Example: get_post_meta( 123, '_srfm_block_config', true ) might return an array of block configs.
113 * If not found, it attempts to parse the form's post content and generate the block config.
114 * Example: If a legacy form with ID 123 has no _srfm_block_config, but its post_content contains blocks,
115 * the function will parse those blocks and call add_block_config() to generate and store the config.
116 *
117 * @param int $form_id The ID of the form post.
118 * @since 1.12.2
119 * @return array|null The block configuration array, or null if not found or invalid.
120 */
121 public static function get_or_migrate_block_config_for_legacy_form( $form_id ) {
122 // Validate that $form_id is a positive integer.
123 // Example: $form_id = 123 is valid; $form_id = -1 or 'abc' is not.
124 if ( ! is_int( $form_id ) || $form_id <= 0 ) {
125 return null;
126 }
127
128 // Retrieve the block config from post meta.
129 // Example: $block_config = [ 'block-1' => [ ... ], 'block-2' => [ ... ] ].
130 $block_config = get_post_meta( $form_id, '_srfm_block_config', true );
131 if ( ! empty( $block_config ) && is_array( $block_config ) ) {
132 // If it exists and is an array, return it directly (no migration needed).
133 // Example: Returning the existing $block_config array.
134 return $block_config;
135 }
136
137 // Get the post by ID and validate.
138 // Example: $post = get_post( 123 ); $post->post_content should contain block markup.
139 $post = get_post( $form_id );
140 if ( ! ( $post instanceof \WP_Post ) || empty( $post->post_content ) ) {
141 return null;
142 }
143
144 // Parse the blocks from the post content and attempt migration.
145 // Example: $blocks = parse_blocks( $post->post_content ); $blocks is an array of block arrays.
146 if ( function_exists( 'parse_blocks' ) ) {
147 $blocks = parse_blocks( $post->post_content );
148 if ( is_array( $blocks ) && ! empty( $blocks ) ) {
149 self::add_block_config( $blocks, $form_id );
150 }
151 }
152
153 // Retrieve the block config again after migration attempt.
154 // Example: After migration, $block_config should now be an array if successful.
155 $block_config = get_post_meta( $form_id, '_srfm_block_config', true );
156
157 return ! empty( $block_config ) && is_array( $block_config ) ? $block_config : null;
158 }
159
160 /**
161 * Prepare validation data for a given form.
162 *
163 * Retrieves the form block configuration from post meta and adds a 'name_with_id'
164 * key to each block, which is a unique identifier for the field (used for validation).
165 *
166 * @param int $current_form_id The ID of the form post.
167 * @since 1.12.2
168 * @return array|null The processed form configuration array, or null if not found.
169 */
170 public static function prepared_validation_data( $current_form_id ) {
171 // Retrieve the form block configuration from post meta.
172 $get_form_config = self::get_or_migrate_block_config_for_legacy_form( $current_form_id );
173
174 // If the configuration is an array, add a 'name_with_id' key to each block.
175 if ( is_array( $get_form_config ) ) {
176 foreach ( $get_form_config as $index => $block ) {
177 // Ensure both 'blockName' and 'block_id' exist before creating the identifier.
178 if ( isset( $block['blockName'] ) ) {
179 // 'name_with_id' is used as a unique field identifier for validation.
180 // Example: 'sureforms-input-abc123' for blockName 'sureforms/input' and block_id 'abc123'
181 $name_with_id = str_replace( '/', '-', $block['blockName'] ) . '-' . $index;
182
183 // Allow custom filter based on block type.
184 $name_with_id = apply_filters(
185 'srfm_block_config_name_with_id',
186 $name_with_id,
187 $block
188 );
189
190 $get_form_config[ $index ]['name_with_id'] = $name_with_id;
191 }
192 }
193 }
194
195 // Return the processed configuration array, or an empty array if not found.
196 return is_array( $get_form_config ) ? $get_form_config : [];
197 }
198
199 /**
200 * Validate form data for a given form.
201 *
202 * This function checks each field in the submitted form data (including uploaded files)
203 * and applies the 'srfm_validate_form_data' filter to validate each field according to
204 * its configuration. Only fields with keys containing '-lbl-' (SureForms fields) are processed.
205 * If a field fails validation, its error message is added to the $not_valid_fields array.
206 *
207 * @param array<mixed> $form_data The submitted form data (sanitized).
208 * @param int|mixed $current_form_id The ID of the form being validated.
209 * @since 1.12.2
210 * @return array An array of invalid fields and their error messages. Empty if all fields are valid.
211 */
212 public static function validate_form_data( $form_data, $current_form_id ) {
213 if ( ! is_array( $form_data ) || ! is_numeric( $current_form_id ) ) {
214 return [];
215 }
216
217 // Holds fields that are not valid. Example: [ 'srfm-email-c867d9d9-lbl-email' => 'This field is required.' ].
218 $not_valid_fields = [];
219
220 // Retrieve the processed form configuration for validation.
221 $get_form_config = self::prepared_validation_data( Helper::get_integer_value( $current_form_id ) );
222
223 $form_data = apply_filters( 'srfm_field_validation_data', $form_data );
224
225 // Iterate over each field in the form data.
226 foreach ( $form_data as $key => $value ) {
227 /**
228 * Only process SureForms fields.
229 * The '-lbl-' substring is mandatory in SureForms field keys.
230 * Example: $key = 'srfm-email-c867d9d9-lbl-email'
231 */
232 if ( false === strpos( $key, '-lbl-' ) ) {
233 continue;
234 }
235
236 $get_name_with_id = explode( '-lbl-', $key );
237 // Extract the part after the last '-' in the key, if it matches the pattern.
238 // Example: $get_name_with_id[0] = "srfm-email-c867d9d9".
239 // $extracted_id = "c867d9d9".
240 $extracted_id = '';
241 if ( is_string( $key ) && preg_match( '/-([a-zA-Z0-9]+)$/', $get_name_with_id[0], $matches ) ) {
242 $extracted_id = $matches[1];
243 // Now $extracted_id contains "c867d9d9" for "srfm-email-c867d9d9".
244 }
245
246 // $get_slug will be the slug after the first hyphen in the second part.
247 // Example: $get_name_with_id[1] = "email" or "field-email", $get_slug = "email".
248 $get_slug = isset( $get_name_with_id[1] ) ? preg_replace( '/^[^-]+-/', '', $get_name_with_id[1] ) : '';
249
250 // $get_field_name is the field name without the block id.
251 // Example: "srfm-email-c867d9d9" => "srfm-email".
252 $get_field_name = str_replace( '-' . $extracted_id, '', $get_name_with_id[0] );
253
254 // Apply the validation filter for the current field.
255 // Example: Passes all relevant field data to the filter for validation.
256 $field_validated = apply_filters(
257 'srfm_validate_form_data',
258 [
259 'field_key' => $key,
260 'field_value' => $value,
261 'form_id' => $current_form_id,
262 'form_config' => $get_form_config,
263 'block_id' => $extracted_id,
264 'block_slug' => $get_slug,
265 'name_with_id' => $get_name_with_id[0],
266 'field_name' => $get_field_name,
267 ]
268 );
269
270 // Check the result of the validation.
271 // Example: $field_validated = [ 'validated' => false, 'error' => 'This field is required.' ].
272 if ( isset( $field_validated['validated'] ) ) {
273 // If the field is valid, skip to the next field.
274 if ( true === $field_validated['validated'] ) {
275 continue;
276 }
277
278 // If the field is not valid, add the error message to the result array.
279 // Example: $not_valid_fields[ 'srfm-email-c867d9d9-lbl-email' ] = 'This field is required.'.
280 if ( false === $field_validated['validated'] ) {
281 $not_valid_fields[ $key ] = $field_validated['error'] ?? __( 'Field is not valid.', 'sureforms' );
282 }
283 }
284
285 // Textarea minimum character server-side validation.
286 if ( 'srfm-textarea' === $get_field_name && is_string( $value ) && '' !== $value ) {
287 $block_config = isset( $get_form_config[ $extracted_id ] ) && is_array( $get_form_config[ $extracted_id ] ) ? $get_form_config[ $extracted_id ] : [];
288 $min_length = isset( $block_config['min_length'] ) ? absint( $block_config['min_length'] ) : 0;
289 if ( $min_length > 0 && mb_strlen( $value ) < $min_length ) {
290 $dynamic_messages = Translatable::dynamic_validation_messages();
291 $min_chars_message = isset( $dynamic_messages['srfm_textarea_min_chars'] ) && is_string( $dynamic_messages['srfm_textarea_min_chars'] ) && '' !== $dynamic_messages['srfm_textarea_min_chars']
292 ? $dynamic_messages['srfm_textarea_min_chars']
293 /* translators: %s represents the minimum number of characters required */
294 : __( 'Please enter at least %s characters.', 'sureforms' );
295 $not_valid_fields[ $key ] = sprintf( $min_chars_message, $min_length );
296 }
297 }
298 }
299
300 // Return the array of invalid fields and their error messages.
301 // Example: [ 'srfm-email-c867d9d9-lbl-email' => 'This field is required.' ].
302 return $not_valid_fields;
303 }
304
305 /**
306 * Process payment block configuration.
307 *
308 * @param array<mixed> $attrs Block attributes.
309 * @param array<mixed> $blocks All blocks.
310 * @return array Processed payment configuration.
311 * @since 2.3.0
312 */
313 private static function process_payment_block( $attrs, $blocks ) {
314 $payment_config = [];
315
316 // Extract payment type (single or subscription).
317 $payment_config['payment_type'] = isset( $attrs['paymentType'] ) && is_string( $attrs['paymentType'] ) ? sanitize_text_field( $attrs['paymentType'] ) : 'one-time';
318
319 // Persist subscription plan (interval + billing cycles) for any form that
320 // has a subscription path. The admin picks a single value for each in the
321 // editor; the server uses these stored values as the source of truth on
322 // submit so a tampered interval/cycles in form data cannot redirect Stripe
323 // to a different billing cadence.
324 if ( in_array( $payment_config['payment_type'], [ 'subscription', 'both' ], true ) && isset( $attrs['subscriptionPlan'] ) && is_array( $attrs['subscriptionPlan'] ) ) {
325 if ( isset( $attrs['subscriptionPlan']['interval'] ) && is_string( $attrs['subscriptionPlan']['interval'] ) ) {
326 $payment_config['subscription_interval'] = sanitize_text_field( $attrs['subscriptionPlan']['interval'] );
327 }
328 if ( isset( $attrs['subscriptionPlan']['billingCycles'] ) ) {
329 // billingCycles is either an integer count or the string 'ongoing'.
330 $cycles_raw = $attrs['subscriptionPlan']['billingCycles'];
331 $payment_config['subscription_billing_cycles'] = is_numeric( $cycles_raw ) ? intval( $cycles_raw ) : sanitize_text_field( (string) $cycles_raw );
332 }
333 }
334
335 // Extract amount type (fixed or minimum).
336 $payment_config['amount_type'] = isset( $attrs['amountType'] ) && is_string( $attrs['amountType'] ) ? sanitize_text_field( $attrs['amountType'] ) : 'fixed';
337
338 $payment_config['fixed_amount'] = isset( $attrs['fixedAmount'] ) ? floatval( $attrs['fixedAmount'] ) : 10;
339
340 $payment_config['minimum_amount'] = isset( $attrs['minimumAmount'] ) ? floatval( $attrs['minimumAmount'] ) : 0;
341
342 // Extract variable amount field reference.
343 if ( isset( $attrs['variableAmountField'] ) ) {
344 $variable_amount_slug = sanitize_text_field( $attrs['variableAmountField'] );
345 $payment_config['variable_amount_field'] = $variable_amount_slug;
346
347 // Find and add the block name from which the variable amount field comes from.
348 if ( ! empty( $variable_amount_slug ) && is_array( $blocks ) ) {
349 foreach ( $blocks as $block ) {
350 if ( isset( $block['attrs']['slug'] ) && $block['attrs']['slug'] === $variable_amount_slug ) {
351 $payment_config['variable_amount_field_block_name'] = $block['blockName'];
352 break;
353 }
354 }
355 }
356 }
357
358 // BOTH MODE: store per-type amount configs so server-side validation can
359 // use the correct config based on which flow the user actually chose.
360 if ( 'both' === $payment_config['payment_type'] ) {
361 $payment_config['one_time_amount_type'] = isset( $attrs['oneTimeAmountType'] ) && is_string( $attrs['oneTimeAmountType'] ) ? sanitize_text_field( $attrs['oneTimeAmountType'] ) : 'fixed';
362 $payment_config['one_time_fixed_amount'] = isset( $attrs['oneTimeFixedAmount'] ) ? floatval( $attrs['oneTimeFixedAmount'] ) : 10;
363 $payment_config['one_time_minimum_amount'] = isset( $attrs['oneTimeMinimumAmount'] ) ? floatval( $attrs['oneTimeMinimumAmount'] ) : 0;
364
365 if ( isset( $attrs['oneTimeVariableAmountField'] ) ) {
366 $ot_slug = sanitize_text_field( $attrs['oneTimeVariableAmountField'] );
367 $payment_config['one_time_variable_amount_field'] = $ot_slug;
368 if ( ! empty( $ot_slug ) && is_array( $blocks ) ) {
369 foreach ( $blocks as $block ) {
370 if ( isset( $block['attrs']['slug'] ) && $block['attrs']['slug'] === $ot_slug ) {
371 $payment_config['one_time_variable_amount_field_block_name'] = $block['blockName'];
372 break;
373 }
374 }
375 }
376 }
377
378 $payment_config['subscription_amount_type'] = isset( $attrs['subscriptionAmountType'] ) && is_string( $attrs['subscriptionAmountType'] ) ? sanitize_text_field( $attrs['subscriptionAmountType'] ) : 'fixed';
379 $payment_config['subscription_fixed_amount'] = isset( $attrs['subscriptionFixedAmount'] ) ? floatval( $attrs['subscriptionFixedAmount'] ) : 10;
380 $payment_config['subscription_minimum_amount'] = isset( $attrs['subscriptionMinimumAmount'] ) ? floatval( $attrs['subscriptionMinimumAmount'] ) : 0;
381
382 if ( isset( $attrs['subscriptionVariableAmountField'] ) ) {
383 $sub_slug = sanitize_text_field( $attrs['subscriptionVariableAmountField'] );
384 $payment_config['subscription_variable_amount_field'] = $sub_slug;
385 if ( ! empty( $sub_slug ) && is_array( $blocks ) ) {
386 foreach ( $blocks as $block ) {
387 if ( isset( $block['attrs']['slug'] ) && $block['attrs']['slug'] === $sub_slug ) {
388 $payment_config['subscription_variable_amount_field_block_name'] = $block['blockName'];
389 break;
390 }
391 }
392 }
393 }
394 }
395
396 return $payment_config;
397 }
398
399 /**
400 * Process dropdown block configuration.
401 *
402 * @param array<mixed> $attrs Block attributes.
403 * @return array Processed dropdown configuration.
404 * @since 2.3.0
405 */
406 private static function process_dropdown_block( $attrs ) {
407 $dropdown_config = [];
408
409 // Extract required field.
410 $dropdown_config['required'] = isset( $attrs['required'] ) && ! empty( $attrs['required'] ) ? true : false;
411
412 // Extract options with their full structure (label, icon, value).
413 if ( isset( $attrs['options'] ) && is_array( $attrs['options'] ) ) {
414 $sanitized_options = [];
415 foreach ( $attrs['options'] as $option ) {
416 if ( is_array( $option ) ) {
417 $sanitized_options[] = [
418 'label' => isset( $option['label'] ) ? sanitize_text_field( $option['label'] ) : '',
419 'icon' => isset( $option['icon'] ) ? sanitize_text_field( $option['icon'] ) : '',
420 'value' => isset( $option['value'] ) ? sanitize_text_field( $option['value'] ) : '',
421 ];
422 }
423 }
424 $dropdown_config['options'] = $sanitized_options;
425 }
426
427 // Extract showValues flag.
428 $dropdown_config['show_values'] = isset( $attrs['showValues'] ) ? rest_sanitize_boolean( $attrs['showValues'] ) : false;
429
430 // Extract multiSelect flag.
431 if ( isset( $attrs['multiSelect'] ) ) {
432 $dropdown_config['multi_select'] = rest_sanitize_boolean( $attrs['multiSelect'] );
433 }
434
435 // Extract minValue for multi-select validation.
436 if ( isset( $attrs['minValue'] ) ) {
437 $dropdown_config['min_value'] = absint( $attrs['minValue'] );
438 }
439
440 // Extract maxValue for multi-select validation.
441 if ( isset( $attrs['maxValue'] ) ) {
442 $dropdown_config['max_value'] = absint( $attrs['maxValue'] );
443 }
444
445 return $dropdown_config;
446 }
447
448 /**
449 * Process multi-choice block configuration.
450 *
451 * @param array<mixed> $attrs Block attributes.
452 * @return array Processed multi-choice configuration.
453 * @since 2.3.0
454 */
455 private static function process_multichoice_block( $attrs ) {
456 $multichoice_config = [];
457
458 // Extract required field.
459 $multichoice_config['required'] = isset( $attrs['required'] ) && ! empty( $attrs['required'] ) ? true : false;
460
461 // Extract singleSelection flag.
462 if ( isset( $attrs['singleSelection'] ) ) {
463 $multichoice_config['single_selection'] = rest_sanitize_boolean( $attrs['singleSelection'] );
464 }
465
466 // Extract minValue for validation.
467 if ( isset( $attrs['minValue'] ) ) {
468 $multichoice_config['min_value'] = absint( $attrs['minValue'] );
469 }
470
471 // Extract maxValue for validation.
472 if ( isset( $attrs['maxValue'] ) ) {
473 $multichoice_config['max_value'] = absint( $attrs['maxValue'] );
474 }
475
476 // Extract options with their full structure (label, icon, value).
477 if ( isset( $attrs['options'] ) && is_array( $attrs['options'] ) ) {
478 $sanitized_options = [];
479 foreach ( $attrs['options'] as $option ) {
480 if ( is_array( $option ) ) {
481 $sanitized_options[] = [
482 'label' => isset( $option['optionTitle'] ) ? trim( sanitize_text_field( $option['optionTitle'] ) ) : '',
483 'icon' => isset( $option['icon'] ) ? sanitize_text_field( $option['icon'] ) : '',
484 'value' => isset( $option['value'] ) ? sanitize_text_field( $option['value'] ) : '',
485 ];
486 }
487 }
488 $multichoice_config['options'] = $sanitized_options;
489 }
490
491 // Extract showValues flag.
492 if ( isset( $attrs['showValues'] ) ) {
493 $multichoice_config['show_values'] = rest_sanitize_boolean( $attrs['showValues'] );
494 }
495
496 return $multichoice_config;
497 }
498
499 /**
500 * Process textarea block configuration.
501 *
502 * @param array<mixed> $attrs Block attributes.
503 * @return array Processed textarea configuration.
504 * @since 2.8.2
505 */
506 private static function process_textarea_block( $attrs ) {
507 // Always emit a min_length key so a cleared/invalid value overwrites any
508 // previously stored config on save instead of falling back to stale data.
509 // Rich-text editors submit HTML markup which would skew mb_strlen counts,
510 // so they're treated as "no min-length validation".
511 if ( ! empty( $attrs['isRichText'] ) ) {
512 return [ 'min_length' => 0 ];
513 }
514
515 $min_length = isset( $attrs['minLength'] ) && is_numeric( $attrs['minLength'] ) ? absint( $attrs['minLength'] ) : 0;
516 $max_length = isset( $attrs['maxLength'] ) && is_numeric( $attrs['maxLength'] ) ? absint( $attrs['maxLength'] ) : 0;
517
518 // Misconfiguration guard — drop min when it exceeds max so the form stays submittable.
519 if ( $max_length > 0 && $min_length > $max_length ) {
520 $min_length = 0;
521 }
522
523 return [ 'min_length' => $min_length ];
524 }
525
526 /**
527 * Process number block configuration.
528 *
529 * @param array<mixed> $attrs Block attributes.
530 * @return array Processed number configuration.
531 * @since 2.4.0
532 */
533 private static function process_number_block( $attrs ) {
534 $number_config = [];
535
536 // Extract required field.
537 if ( isset( $attrs['required'] ) ) {
538 $number_config['required'] = ! empty( $attrs['required'] ) ? true : false;
539 }
540
541 // Extract format type (us-style or eu-style).
542 $number_config['format_type'] = isset( $attrs['formatType'] ) && is_string( $attrs['formatType'] ) ? sanitize_text_field( $attrs['formatType'] ) : 'us-style';
543
544 // Extract min value.
545 if ( isset( $attrs['min'] ) ) {
546 $number_config['min'] = floatval( $attrs['min'] );
547 }
548
549 // Extract max value.
550 if ( isset( $attrs['max'] ) ) {
551 $number_config['max'] = floatval( $attrs['max'] );
552 }
553
554 return $number_config;
555 }
556 }
557