PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | classes/models/FrmEntryValidate.php +196 -73 6.25 → trunk View file →
@@ -14,8 +14,9 @@
14 14
15 15 /**
16 16 * @param array $values
17 17 * @param bool|string[] $exclude
18 + *
18 19 * @return array
19 20 */
20 21 public static function validate( $values, $exclude = false ) {
21 22 FrmEntry::sanitize_entry_post( $values );
@@ -22,13 +23,12 @@
22 23 $errors = array();
23 24
24 25 if ( ! isset( $values['form_id'] ) || ! isset( $values['item_meta'] ) ) {
25 26 $errors['form'] = __( 'There was a problem with your submission. Please try again.', 'formidable' );
26 -
27 27 return $errors;
28 28 }
29 29
30 - if ( FrmAppHelper::is_admin() && is_user_logged_in() && ( ! isset( $values[ 'frm_submit_entry_' . $values['form_id'] ] ) || ! wp_verify_nonce( $values[ 'frm_submit_entry_' . $values['form_id'] ], 'frm_submit_entry_nonce' ) ) ) {
30 + if ( FrmAppHelper::is_admin() && is_user_logged_in() && ( ! isset( $values[ 'frm_submit_entry_' . $values['form_id'] ] ) || ! wp_verify_nonce( $values[ 'frm_submit_entry_' . $values['form_id'] ], 'frm_submit_entry_nonce' ) ) ) { // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
31 31 $frm_settings = FrmAppHelper::get_settings();
32 32 $errors['form'] = $frm_settings->admin_permission;
33 33 }
34 34
@@ -44,9 +44,9 @@
44 44 self::validate_field( $posted_field, $errors, $values, $args );
45 45 unset( $posted_field );
46 46 }
47 47
48 - if ( empty( $errors ) ) {
48 + if ( ! $errors ) {
49 49 self::spam_check( $exclude, $values, $errors );
50 50 }
51 51
52 52 /**
@@ -83,16 +83,30 @@
83 83 $_POST['item_meta'] = array();
84 84 }
85 85 }
86 86
87 + /**
88 + * @param array $values
89 + *
90 + * @return void
91 + */
87 92 private static function set_item_key( &$values ) {
88 - if ( ! isset( $values['item_key'] ) || $values['item_key'] == '' ) {
89 - global $wpdb;
90 - $values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
91 - $_POST['item_key'] = $values['item_key'];
93 + // phpcs:ignore Universal.Operators.StrictComparisons
94 + if ( isset( $values['item_key'] ) && $values['item_key'] != '' ) {
95 + return;
92 96 }
97 +
98 + global $wpdb;
99 + $values['item_key'] = FrmAppHelper::get_unique_key( '', $wpdb->prefix . 'frm_items', 'item_key' );
100 + $_POST['item_key'] = $values['item_key'];
93 101 }
94 102
103 + /**
104 + * @param array $values
105 + * @param array|string $exclude
106 + *
107 + * @return array
108 + */
95 109 private static function get_fields_to_validate( $values, $exclude ) {
96 110 $where = apply_filters( 'frm_posted_field_ids', array( 'fi.form_id' => $values['form_id'] ) );
97 111
98 112 // Don't get subfields
@@ -98,9 +112,9 @@
98 112 // Don't get subfields
99 113 $where['fr.parent_form_id'] = array( null, 0 );
100 114
101 115 // Don't get excluded fields (like file upload fields in the ajax validation)
102 - if ( ! empty( $exclude ) ) {
116 + if ( $exclude ) {
103 117 $where['fi.type not'] = $exclude;
104 118 }
105 119
106 120 $fields = FrmField::getAll( $where, 'field_order' );
@@ -115,8 +129,16 @@
115 129 */
116 130 return apply_filters( 'frm_fields_to_validate', $fields, compact( 'values', 'exclude', 'where' ) );
117 131 }
118 132
133 + /**
134 + * @param object $posted_field
135 + * @param array $errors
136 + * @param array $values
137 + * @param array $args
138 + *
139 + * @return void
140 + */
119 141 public static function validate_field( $posted_field, &$errors, $values, $args = array() ) {
120 142 $defaults = array(
121 143 'id' => $posted_field->id,
122 144 // The id of the repeat or embed form.
@@ -126,23 +148,18 @@
126 148 // Exclude these field types from validation.
127 149 'exclude' => array(),
128 150
129 151 );
130 - $args = wp_parse_args( $args, $defaults );
152 + $args = wp_parse_args( $args, $defaults );
153 + $value = ! empty( $args['parent_field_id'] ) ? $values : ( $values['item_meta'][ $args['id'] ] ?? '' );
131 154
132 - if ( empty( $args['parent_field_id'] ) ) {
133 - $value = $values['item_meta'][ $args['id'] ] ?? '';
134 - } else {
135 - // value is from a nested form
136 - $value = $values;
137 - }
138 -
139 155 // Check for values in "Other" fields
140 156 FrmEntriesHelper::maybe_set_other_validation( $posted_field, $value, $args );
141 157
142 158 self::maybe_clear_value_for_default_blank_setting( $posted_field, $value );
143 159
144 - $should_trim = is_array( $value ) && count( $value ) == 1 && isset( $value[0] ) && $posted_field->type !== 'checkbox';
160 + $should_trim = is_array( $value ) && count( $value ) === 1 && isset( $value[0] ) && $posted_field->type !== 'checkbox';
161 +
145 162 if ( $should_trim ) {
146 163 $value = reset( $value );
147 164 }
148 165
@@ -149,8 +166,9 @@
149 166 if ( ! is_array( $value ) ) {
150 167 $value = trim( $value );
151 168 }
152 169
170 + // phpcs:ignore Universal.Operators.StrictComparisons
153 171 if ( $posted_field->required == '1' && FrmAppHelper::is_empty_value( $value ) ) {
154 172 $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $posted_field, 'blank' );
155 173 } elseif ( ! isset( $_POST['item_name'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
156 174 self::maybe_add_item_name( $value, $posted_field );
@@ -164,8 +182,9 @@
164 182 // Field might want to modify value before other parts of the system
165 183 // e.g. trim off excess values like in the case of fields with limit.
166 184 $value = apply_filters( 'frm_modify_posted_field_value', $value, $errors, $posted_field, $args );
167 185
186 + // phpcs:ignore Universal.Operators.StrictComparisons
168 187 if ( $value != '' ) {
169 188 self::validate_phone_field( $errors, $posted_field, $value, $args );
170 189 }
171 190
@@ -215,16 +234,18 @@
215 234 *
216 235 * @param stdClass $field
217 236 * @param array|string $value
218 237 * @param array $options
238 + *
219 239 * @return bool
220 240 */
221 - private static function option_is_valid( $field, $value, $options ) {
241 + private static function option_is_valid( $field, $value, $options ) { // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
222 242 if ( '' === $value ) {
223 243 return true;
224 244 }
225 245
226 246 $field_object = FrmFieldFactory::get_field_type( $field->type, $field );
247 +
227 248 if ( ! $field_object->field_type_has_options_settings() ) {
228 249 return true;
229 250 }
230 251
@@ -246,9 +267,9 @@
246 267 foreach ( $value as $current_value ) {
247 268 $match = false;
248 269
249 270 foreach ( $options as $key => $option ) {
250 - if ( strpos( $key, 'other_' ) === 0 ) {
271 + if ( str_starts_with( $key, 'other_' ) ) {
251 272 // Always return true if an other option is found.
252 273 return true;
253 274 }
254 275
@@ -258,29 +279,38 @@
258 279 } else {
259 280 $option_value = $option;
260 281 }
261 282
283 + /**
284 + * @var string $current_value
285 + */
262 286 $match = trim( $current_value ) === trim( $option_value );
287 +
263 288 if ( $match ) {
264 289 break;
265 290 }
266 291
267 292 $match = trim( $current_value ) === trim( do_shortcode( $option_value ) );
293 +
268 294 if ( $match ) {
269 295 break;
270 296 }
271 297
272 298 $match = self::is_filtered_match( $current_value, $option_value );
299 +
273 300 if ( $match ) {
274 301 break;
275 302 }
276 303
277 - if ( is_numeric( $current_value ) ) {
278 - $match = (int) $current_value === (int) $option_value;
279 - if ( $match ) {
280 - break;
281 - }
304 + if ( ! is_numeric( $current_value ) ) {
305 + continue;
282 306 }
307 +
308 + $match = (int) $current_value === (int) $option_value;
309 +
310 + if ( $match ) {
311 + break;
312 + }
283 313 }//end foreach
284 314
285 315 if ( ! $match ) {
286 316 return self::options_are_dynamic_based_on_hook( $field, $value );
@@ -298,20 +328,25 @@
298 328 * @since 6.22
299 329 *
300 330 * @param string $value
301 331 * @param string $option_value
332 + *
302 333 * @return bool
303 334 */
304 335 private static function is_filtered_match( $value, $option_value ) {
305 336 // First remove the wpautop filter so it doesn't add extra tags to $option_value.
306 337 $filter_priority = has_filter( 'the_content', 'wpautop' );
338 +
307 339 if ( is_numeric( $filter_priority ) ) {
308 340 remove_filter( 'the_content', 'wpautop', $filter_priority );
309 341 }
342 +
310 343 $filtered_option = apply_filters( 'the_content', $option_value );
344 +
311 345 if ( is_numeric( $filter_priority ) ) {
312 346 add_filter( 'the_content', 'wpautop', $filter_priority );
313 347 }
348 +
314 349 return trim( $value ) === trim( $filtered_option );
315 350 }
316 351
317 352 /**
@@ -319,8 +354,11 @@
319 354 * This is to help avoid issues where the options could be based on a URL param for example.
320 355 *
321 356 * @since 6.21
322 357 *
358 + * @param object $field_object The field object.
359 + * @param array|string $value The value to validate.
360 + *
323 361 * @return bool
324 362 */
325 363 private static function options_are_dynamic_based_on_hook( $field_object, $value ) {
326 364 $values = (array) $field_object;
@@ -333,10 +371,9 @@
333 371 $option_value = $separate_value ? $option['value'] : $option['label'];
334 372 } else {
335 373 $option_value = $option;
336 374 }
337 - $option_value = do_shortcode( $option_value );
338 - return $option_value;
375 + return do_shortcode( $option_value );
339 376 };
340 377
341 378 $values_options = array_map( $map_callback, $values['options'] );
342 379 $field_object_options = array_map( $map_callback, $field_object->options );
@@ -350,11 +387,14 @@
350 387 * @since 5.2.02
351 388 *
352 389 * @param array|string $value Field value.
353 390 * @param object $field Field object.
391 + *
392 + * @return void
354 393 */
355 394 private static function maybe_add_item_name( $value, $field ) {
356 395 $item_name = false;
396 +
357 397 if ( 'name' === $field->type ) {
358 398 $field_obj = FrmFieldFactory::get_field_object( $field );
359 399 $item_name = $field_obj->get_display_value( $value );
360 400 } elseif ( 'text' === $field->type ) {
@@ -362,9 +402,9 @@
362 402 }
363 403
364 404 if ( false !== $item_name ) {
365 405 // Item name has a max length of 255 characters so truncate it so it doesn't fail to save in the database.
366 - $_POST['item_name'] = substr( $item_name, 0, 255 );
406 + $_POST['item_name'] = FrmAppHelper::truncate( $item_name, 255, 1, '', true );
367 407 }
368 408 }
369 409
370 410 /**
@@ -371,11 +411,14 @@
371 411 * Set $value to an empty string if it matches its label
372 412 *
373 413 * @param object $field
374 414 * @param string $value
415 + *
416 + * @return void
375 417 */
376 418 private static function maybe_clear_value_for_default_blank_setting( $field, &$value ) {
377 419 $position = FrmField::get_option( $field, 'label' );
420 +
378 421 if ( ! $position ) {
379 422 $position = FrmStylesController::get_style_val( 'position', $field->form_id );
380 423 }
381 424
@@ -383,8 +426,16 @@
383 426 $value = '';
384 427 }
385 428 }
386 429
430 + /**
431 + * @param array $errors
432 + * @param object $posted_field
433 + * @param mixed $value
434 + * @param array $args
435 + *
436 + * @return void
437 + */
387 438 public static function validate_field_types( &$errors, $posted_field, $value, $args ) {
388 439 $field_obj = FrmFieldFactory::get_field_object( $posted_field );
389 440 $args['value'] = $value;
390 441 $args['errors'] = $errors;
@@ -389,25 +440,41 @@
389 440 $args['value'] = $value;
390 441 $args['errors'] = $errors;
391 442
392 443 $new_errors = $field_obj->validate( $args );
393 - if ( ! empty( $new_errors ) ) {
444 +
445 + if ( $new_errors ) {
394 446 $errors = array_merge( $errors, $new_errors );
395 447 }
396 448 }
397 449
450 + /**
451 + * @param array $errors
452 + * @param object $field
453 + * @param string $value
454 + * @param array $args
455 + *
456 + * @return void
457 + */
398 458 public static function validate_phone_field( &$errors, $field, $value, $args ) {
399 459 $format_value = FrmField::get_option( $field, 'format' );
400 460
401 - if ( $field->type === 'phone' || ( $field->type === 'text' && $format_value && ! FrmCurrencyHelper::is_currency_format( $format_value ) ) ) {
402 - $pattern = self::phone_format( $field );
461 + if ( $field->type !== 'phone' && ( $field->type !== 'text' || ! $format_value || FrmCurrencyHelper::is_currency_format( $format_value ) ) ) {
462 + return;
463 + }
403 464
404 - if ( ! preg_match( $pattern, $value ) ) {
405 - $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
406 - }
465 + $pattern = self::phone_format( $field );
466 +
467 + if ( ! preg_match( $pattern, $value ) ) {
468 + $errors[ 'field' . $args['id'] ] = FrmFieldsHelper::get_error_msg( $field, 'invalid' );
407 469 }
408 470 }
409 471
472 + /**
473 + * @param object $field
474 + *
475 + * @return string
476 + */
410 477 public static function phone_format( $field ) {
411 478 if ( FrmField::is_option_empty( $field, 'format' ) ) {
412 479 $pattern = self::default_phone_format();
413 480 } else {
@@ -419,19 +486,19 @@
419 486 $pattern = html_entity_decode( $pattern );
420 487 $pattern = apply_filters( 'frm_phone_pattern', $pattern, $field );
421 488
422 489 // Create a regexp if format is not already a regexp
423 - if ( strpos( $pattern, '^' ) !== 0 ) {
490 + if ( ! str_starts_with( $pattern, '^' ) ) {
424 491 $pattern = self::create_regular_expression_from_format( $pattern );
425 492 }
426 493
427 - $pattern = '/' . $pattern . '/';
428 -
429 - return $pattern;
494 + return '/' . $pattern . '/';
430 495 }
431 496
432 497 /**
433 498 * @since 3.01
499 + *
500 + * @return string
434 501 */
435 502 private static function default_phone_format() {
436 503 return '^((\+\d{1,3}(-|.| )?\(?\d\)?(-| |.)?\d{1,5})|(\(?\d{2,6}\)?))(-|.| )?(\d{3,4})(-|.| )?(\d{4})(( x| ext)\d{1,5}){0,1}$';
437 504 }
@@ -457,22 +524,22 @@
457 524 $pattern = str_replace( 'a', '[a-zA-Z]', $pattern );
458 525 $pattern = str_replace( '*', 'w', $pattern );
459 526 $pattern = str_replace( '/', '\/', $pattern );
460 527
461 - if ( strpos( $pattern, '\?' ) !== false ) {
528 + if ( str_contains( $pattern, '\?' ) ) {
462 529 $parts = explode( '\?', $pattern );
463 530 $pattern = '';
531 +
464 532 foreach ( $parts as $part ) {
465 - if ( empty( $pattern ) ) {
533 + if ( $pattern ) {
534 + $pattern .= '(' . $part . ')?';
535 + } else {
466 536 $pattern .= $part;
467 - } else {
468 - $pattern .= '(' . $part . ')?';
469 537 }
470 538 }
471 539 }
472 - $pattern = '^' . $pattern . '$';
473 540
474 - return $pattern;
541 + return '^' . $pattern . '$';
475 542 }
476 543
477 544 /**
478 545 * Check for spam.
@@ -479,8 +546,10 @@
479 546 *
480 547 * @param bool $exclude
481 548 * @param array $values
482 549 * @param array $errors By reference.
550 + *
551 + * @return void
483 552 */
484 553 public static function spam_check( $exclude, $values, &$errors ) {
485 554 if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
486 555 // Do not check spam on importing.
@@ -486,15 +555,16 @@
486 555 // Do not check spam on importing.
487 556 return;
488 557 }
489 558
490 - if ( ! empty( $exclude ) || empty( $values['item_meta'] ) || ! empty( $errors ) ) {
491 - // only check spam if there are no other errors
559 + if ( $exclude || empty( $values['item_meta'] ) || $errors ) {
560 + // Only check spam if there are no other errors
492 561 return;
493 562 }
494 563
495 564 $antispam_check = self::is_antispam_check( $values['form_id'] );
496 565 $spam_msg = FrmAntiSpamController::get_default_spam_message();
566 +
497 567 if ( is_string( $antispam_check ) ) {
498 568 $errors['spam'] = $antispam_check;
499 569 } elseif ( self::is_honeypot_spam( $values ) || self::is_spam_bot() ) {
500 570 $errors['spam'] = $spam_msg;
@@ -499,8 +569,9 @@
499 569 } elseif ( self::is_honeypot_spam( $values ) || self::is_spam_bot() ) {
500 570 $errors['spam'] = $spam_msg;
501 571 } else {
502 572 $is_spam = FrmAntiSpamController::is_spam( $values );
573 +
503 574 if ( $is_spam ) {
504 575 $errors['spam'] = $is_spam;
505 576 }
506 577 }
@@ -519,14 +590,17 @@
519 590 *
520 591 * @since 5.0.13
521 592 *
522 593 * @param array $values The values.
594 + *
523 595 * @return bool
524 596 */
525 597 private static function form_is_in_progress( $values ) {
598 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
526 599 return FrmAppHelper::pro_is_installed() &&
527 600 ( isset( $values[ 'frm_page_order_' . $values['form_id'] ] ) || FrmAppHelper::get_post_param( 'frm_next_page' ) ) &&
528 601 FrmField::get_all_types_in_form( $values['form_id'], 'break' );
602 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
529 603 }
530 604
531 605 /**
532 606 * @param int $form_id
@@ -539,8 +613,9 @@
539 613 }
540 614
541 615 /**
542 616 * @param array $values
617 + *
543 618 * @return bool
544 619 */
545 620 private static function is_honeypot_spam( $values ) {
546 621 $honeypot = new FrmHoneypot( $values['form_id'] );
@@ -550,31 +625,29 @@
550 625 /**
551 626 * @return bool
552 627 */
553 628 private static function is_spam_bot() {
554 - $ip = FrmAppHelper::get_ip_address();
555 -
556 - return empty( $ip );
629 + return ! FrmAppHelper::get_ip_address();
557 630 }
558 631
559 632 /**
560 633 * @param array $values
634 + *
561 635 * @return bool
562 636 */
563 637 private static function is_akismet_spam( $values ) {
564 638 global $wpcom_api_key;
565 -
566 - return ( is_callable( 'Akismet::http_post' ) && ( get_option( 'wordpress_api_key' ) || $wpcom_api_key ) && self::akismet( $values ) );
639 + return is_callable( 'Akismet::http_post' ) && ( get_option( 'wordpress_api_key' ) || $wpcom_api_key ) && self::akismet( $values );
567 640 }
568 641
569 642 /**
570 643 * @param int $form_id
644 + *
571 645 * @return bool
572 646 */
573 647 private static function is_akismet_enabled_for_user( $form_id ) {
574 648 $form = FrmForm::getOne( $form_id );
575 -
576 - return ( ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] !== 'logged' || ! is_user_logged_in() ) );
649 + return ! empty( $form->options['akismet'] ) && ( $form->options['akismet'] !== 'logged' || ! is_user_logged_in() );
577 650 }
578 651
579 652 /**
580 653 * Checks spam using WordPress disallowed words and Frm denylist.
@@ -589,8 +662,10 @@
589 662
590 663 /**
591 664 * Check entries for Akismet spam
592 665 *
666 + * @param array $values Entry values.
667 + *
593 668 * @return bool true if is spam
594 669 */
595 670 public static function akismet( $values ) {
596 671 if ( empty( $values['item_meta'] ) ) {
@@ -613,13 +688,18 @@
613 688
614 689 $query_string = _http_build_query( $datas, '', '&' );
615 690 $response = Akismet::http_post( $query_string, 'comment-check' );
616 691
617 - return ( is_array( $response ) && $response[1] === 'true' );
692 + return is_array( $response ) && $response[1] === 'true';
618 693 }
619 694
620 695 /**
621 696 * @since 2.0
697 + *
698 + * @param array $datas The array of values being sent to Akismet.
699 + * @param array $values Entry values.
700 + *
701 + * @return void
622 702 */
623 703 private static function parse_akismet_array( &$datas, $values ) {
624 704 self::add_site_info_to_akismet( $datas );
625 705 self::add_server_values_to_akismet( $datas );
@@ -630,8 +710,13 @@
630 710 self::add_user_info_to_akismet( $datas, $values );
631 711 self::add_comment_content_to_akismet( $datas, $values );
632 712 }
633 713
714 + /**
715 + * @param array $datas
716 + *
717 + * @return void
718 + */
634 719 private static function add_site_info_to_akismet( &$datas ) {
635 720 $datas['blog'] = FrmAppHelper::site_url();
636 721 $datas['user_ip'] = preg_replace( '/[^0-9., ]/', '', FrmAppHelper::get_ip_address() );
637 722 $datas['user_agent'] = FrmAppHelper::get_server_value( 'HTTP_USER_AGENT' );
@@ -643,8 +728,14 @@
643 728 $datas['is_test'] = 'true';
644 729 }
645 730 }
646 731
732 + /**
733 + * @param array $datas
734 + * @param array $values
735 + *
736 + * @return void
737 + */
647 738 private static function add_user_info_to_akismet( &$datas, $values ) {
648 739 $user_info = self::get_spam_check_user_info( $values );
649 740 $datas = $datas + $user_info;
650 741
@@ -659,8 +750,9 @@
659 750 * @since 5.0.13 Separate code for guest. Handle value of embedded|repeater.
660 751 * @since 6.21 This changed from private to public.
661 752 *
662 753 * @param array $values Entry values after running through {@see FrmEntryValidate::prepare_values_for_spam_check()}.
754 + *
663 755 * @return array
664 756 */
665 757 public static function get_spam_check_user_info( $values ) {
666 758 if ( ! is_user_logged_in() ) {
@@ -683,8 +775,9 @@
683 775 *
684 776 * @since 5.0.13
685 777 *
686 778 * @param array $values Entry values after flattened.
779 + *
687 780 * @return array
688 781 */
689 782 private static function get_spam_check_user_info_for_guest( $values ) {
690 783 $datas = array(
@@ -716,8 +809,10 @@
716 809 *
717 810 * @param array $datas Guest data.
718 811 * @param array $values The values.
719 812 * @param int|null $custom_index Custom index (or field ID).
813 + *
814 + * @return void
720 815 */
721 816 private static function recursive_add_akismet_guest_info( &$datas, $values, $custom_index = null ) {
722 817 foreach ( $values as $index => $value ) {
723 818 if ( ! $datas['missing_keys'] ) {
@@ -730,15 +825,19 @@
730 825 continue;
731 826 }
732 827
733 828 $field_id = ! is_null( $custom_index ) ? $custom_index : $index;
829 +
734 830 foreach ( $datas['missing_keys'] as $key_index => $key ) {
735 831 $found = self::is_akismet_guest_info_value( $key, $value, $field_id, $datas['name_field_ids'], $values );
736 - if ( $found ) {
737 - $datas[ $key ] = $value;
738 - $datas['frm_duplicated'][] = $field_id;
739 - unset( $datas['missing_keys'][ $key_index ] );
832 +
833 + if ( ! $found ) {
834 + continue;
740 835 }
836 +
837 + $datas[ $key ] = $value;
838 + $datas['frm_duplicated'][] = $field_id;
839 + unset( $datas['missing_keys'][ $key_index ] );
741 840 }
742 841 }//end foreach
743 842 }
744 843
@@ -761,12 +860,12 @@
761 860 }
762 861
763 862 switch ( $key ) {
764 863 case 'comment_author_email':
765 - return strpos( $value, '@' ) && is_email( $value );
864 + return str_contains( $value, '@' ) && is_email( $value );
766 865
767 866 case 'comment_author_url':
768 - return 0 === strpos( $value, 'http' );
867 + return str_starts_with( $value, 'http' );
769 868
770 869 case 'comment_author':
771 870 if ( $name_field_ids && in_array( $field_id, $name_field_ids, true ) ) {
772 871 // If there is name field in the form, we should always use it as author name.
@@ -771,8 +870,9 @@
771 870 if ( $name_field_ids && in_array( $field_id, $name_field_ids, true ) ) {
772 871 // If there is name field in the form, we should always use it as author name.
773 872 return true;
774 873 }
874 +
775 875 $form_id = FrmAppHelper::get_post_param( 'form_id', 0, 'absint' );
776 876 $fields = self::get_name_text_fields( $form_id );
777 877
778 878 foreach ( $fields as $index => $field ) {
@@ -778,12 +878,14 @@
778 878 foreach ( $fields as $index => $field ) {
779 879 if ( 'Name' !== $field->name ) {
780 880 continue;
781 881 }
882 +
782 883 if ( isset( $fields[ $index + 1 ] ) && 'Last' === $fields[ $index + 1 ]->name ) {
783 884 if ( empty( $values[ absint( $fields[ $index + 1 ]->id ) ] ) ) {
784 885 continue;
785 886 }
887 +
786 888 $value .= ' ' . $values[ $fields[ $index + 1 ]->id ];
787 889 return true;
788 890 }
789 891 }
@@ -797,15 +899,18 @@
797 899 *
798 900 * @since 6.17
799 901 *
800 902 * @param int $form_id
903 + *
801 904 * @return array
802 905 */
803 906 private static function get_name_text_fields( $form_id ) {
804 907 $name_text_fields_is_initialized = is_array( self::$name_text_fields );
908 +
805 909 if ( $name_text_fields_is_initialized && isset( self::$name_text_fields[ $form_id ] ) ) {
806 910 return self::$name_text_fields[ $form_id ];
807 911 }
912 +
808 913 if ( ! $name_text_fields_is_initialized ) {
809 914 self::$name_text_fields = array();
810 915 }
811 916 self::$name_text_fields[ $form_id ] = FrmDb::get_results(
@@ -821,8 +926,13 @@
821 926
822 927 return self::$name_text_fields[ $form_id ];
823 928 }
824 929
930 + /**
931 + * @param array $datas
932 + *
933 + * @return void
934 + */
825 935 private static function add_server_values_to_akismet( &$datas ) {
826 936 foreach ( $_SERVER as $key => $value ) {
827 937 $include_value = is_string( $value ) && ! preg_match( '/^HTTP_COOKIE/', $key ) && preg_match( '/^(HTTP_|REMOTE_ADDR|REQUEST_URI|DOCUMENT_URI)/', $key );
828 938
@@ -840,8 +950,10 @@
840 950 * @since 5.0.09
841 951 *
842 952 * @param array $datas The array of values being sent to Akismet.
843 953 * @param array $values Entry values.
954 + *
955 + * @return void
844 956 */
845 957 private static function add_comment_content_to_akismet( &$datas, $values ) {
846 958 if ( isset( $datas['frm_duplicated'] ) ) {
847 959 foreach ( $datas['frm_duplicated'] as $index ) {
@@ -862,22 +974,28 @@
862 974 *
863 975 * @since 5.0.09
864 976 *
865 977 * @param array $values Entry values.
978 + *
979 + * @return void
866 980 */
867 981 private static function skip_adding_values_to_akismet( &$values ) {
868 982 $skipped_fields = self::get_akismet_skipped_field_ids( $values );
983 +
869 984 foreach ( $skipped_fields as $skipped_field ) {
870 985 if ( ! isset( $values['item_meta'][ $skipped_field->id ] ) ) {
871 986 continue;
872 987 }
873 988
874 - if ( self::should_really_skip_field( $skipped_field, $values ) ) {
875 - unset( $values['item_meta'][ $skipped_field->id ] );
876 - if ( isset( $values['item_meta']['other'][ $skipped_field->id ] ) ) {
877 - unset( $values['item_meta']['other'][ $skipped_field->id ] );
878 - }
989 + if ( ! self::should_really_skip_field( $skipped_field, $values ) ) {
990 + continue;
879 991 }
992 +
993 + unset( $values['item_meta'][ $skipped_field->id ] );
994 +
995 + if ( isset( $values['item_meta']['other'][ $skipped_field->id ] ) ) {
996 + unset( $values['item_meta']['other'][ $skipped_field->id ] );
997 + }
880 998 }
881 999 }
882 1000
883 1001 /**
@@ -886,8 +1004,9 @@
886 1004 * @since 5.02.04
887 1005 *
888 1006 * @param object $field_data Object contains `id` and `options`.
889 1007 * @param array $values Entry values.
1008 + *
890 1009 * @return bool
891 1010 */
892 1011 private static function should_really_skip_field( $field_data, $values ) {
893 1012 if ( empty( $field_data->options ) ) {
@@ -895,18 +1014,18 @@
895 1014 return true;
896 1015 }
897 1016
898 1017 FrmAppHelper::unserialize_or_decode( $field_data->options );
1018 +
899 1019 if ( ! $field_data->options ) {
900 1020 // Check if an error happens when unserializing, or empty options.
901 1021 return true;
902 1022 }
903 1023
904 - end( $field_data->options );
905 - $last_key = key( $field_data->options );
1024 + $last_key = array_key_last( $field_data->options );
906 1025
907 1026 // If a choice field has no Other option.
908 - if ( is_numeric( $last_key ) || 0 !== strpos( $last_key, 'other_' ) ) {
1027 + if ( is_numeric( $last_key ) || ! str_starts_with( $last_key, 'other_' ) ) {
909 1028 return true;
910 1029 }
911 1030
912 1031 // If a choice field has Other option, but Other is not selected.
@@ -915,9 +1034,10 @@
915 1034 }
916 1035
917 1036 // Check if submitted value is same as one of field option.
918 1037 foreach ( $field_data->options as $option ) {
919 - $option_value = ! is_array( $option ) ? $option : ( $option['value'] ?? '' );
1038 + $option_value = is_array( $option ) ? ( $option['value'] ?? '' ) : $option;
1039 +
920 1040 if ( $values['item_meta']['other'][ $field_data->id ] === $option_value ) {
921 1041 return true;
922 1042 }
923 1043 }
@@ -932,8 +1052,9 @@
932 1052 * @since 5.0.13 Move out get_all_form_ids_and_flatten_meta() call and get `form_ids` from `$values`.
933 1053 * @since 5.2.04 This method returns array of object contains `id` and `options` instead of array of `id` only.
934 1054 *
935 1055 * @param array $values Entry values after running through {@see FrmEntryValidate::prepare_values_for_spam_check()}.
1056 + *
936 1057 * @return array
937 1058 */
938 1059 private static function get_akismet_skipped_field_ids( $values ) {
939 1060 if ( empty( $values['form_ids'] ) ) {
@@ -959,12 +1080,13 @@
959 1080 * @since 5.0.13
960 1081 * @since 6.21 This changed from private to public.
961 1082 *
962 1083 * @param array $values Entry values.
1084 + *
1085 + * @return void
963 1086 */
964 1087 public static function prepare_values_for_spam_check( &$values ) {
965 - $form_ids = self::get_all_form_ids_and_flatten_meta( $values );
966 - $values['form_ids'] = $form_ids;
1088 + $values['form_ids'] = self::get_all_form_ids_and_flatten_meta( $values );
967 1089 }
968 1090
969 1091 /**
970 1092 * Gets all form IDs (include child form IDs) and flatten item_meta array. Used for skipping values sent to Akismet.
@@ -973,15 +1095,17 @@
973 1095 * @since 5.0.09
974 1096 * @since 5.0.13 Convert name field value to string.
975 1097 *
976 1098 * @param array $values Entry values.
1099 + *
977 1100 * @return array Form IDs.
978 1101 */
979 - private static function get_all_form_ids_and_flatten_meta( &$values ) {
1102 + private static function get_all_form_ids_and_flatten_meta( &$values ) { // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
980 1103 $values['name_field_ids'] = array();
981 1104
982 1105 // Blacklist check for File field in the old version doesn't contain `form_id`.
983 1106 $form_ids = isset( $values['form_id'] ) ? array( absint( $values['form_id'] ) ) : array();
1107 +
984 1108 foreach ( $values['item_meta'] as $field_id => $value ) {
985 1109 if ( ! is_numeric( $field_id ) ) {
986 1110 // Maybe `other`.
987 1111 continue;
@@ -1015,10 +1139,9 @@
1015 1139 }
1016 1140
1017 1141 // Convert name array to string.
1018 1142 if ( isset( $subsubvalue['first'] ) && isset( $subsubvalue['last'] ) ) {
1019 - $subsubvalue = trim( implode( ' ', $subsubvalue ) );
1020 -
1143 + $subsubvalue = trim( implode( ' ', $subsubvalue ) );
1021 1144 $values['name_field_ids'][] = $subsubindex;
1022 1145 }
1023 1146
1024 1147 if ( is_array( $values['item_meta'][ $subsubindex ] ) ) {