PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.23
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.23
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/FrmEntry.php +188 -50 6.46.23 View file →
@@ -5,13 +5,20 @@
5 5
6 6 class FrmEntry {
7 7
8 8 /**
9 + * @since 6.16.3
10 + *
11 + * @var array
12 + */
13 + private static $unique_id_match_checks = array();
14 +
15 + /**
9 16 * Create a new entry
10 17 *
11 18 * @param array $values
12 19 *
13 - * @return int | boolean $entry_id
20 + * @return bool|int $entry_id
14 21 */
15 22 public static function create( $values ) {
16 23 $entry_id = self::create_entry( $values, 'standard' );
17 24
@@ -20,18 +27,18 @@
20 27
21 28 /**
22 29 * Create a new entry with some differences depending on type
23 30 *
24 - * @param array $values
31 + * @param array $values
25 32 * @param string $type
26 33 *
27 - * @return int | boolean $entry_id
34 + * @return bool|int $entry_id
28 35 */
29 36 private static function create_entry( $values, $type ) {
30 37 $new_values = self::before_insert_entry_in_database( $values, $type );
31 38
32 39 // Don't check XML entries for duplicates
33 - if ( $type != 'xml' && self::is_duplicate( $new_values, $values ) ) {
40 + if ( $type !== 'xml' && self::is_duplicate( $new_values, $values ) ) {
34 41 return false;
35 42 }
36 43
37 44 $entry_id = self::continue_to_create_entry( $values, $new_values );
@@ -39,11 +46,26 @@
39 46 return $entry_id;
40 47 }
41 48
42 49 /**
50 + * Flag the memoized unique id check after a new entry is created.
51 + * This prevents possibly DB requests and helps avoid issues when creating repeater entries.
52 + *
53 + * @since 6.16.3
54 + *
55 + * @param string $unique_id
56 + * @return void
57 + */
58 + private static function flag_new_unique_key( $unique_id ) {
59 + if ( ! isset( self::$unique_id_match_checks[ $unique_id ] ) ) {
60 + self::$unique_id_match_checks[ $unique_id ] = false;
61 + }
62 + }
63 +
64 + /**
43 65 * Check for duplicate entries created in the last minute
44 66 *
45 - * @return boolean
67 + * @return bool
46 68 */
47 69 public static function is_duplicate( $new_values, $values ) {
48 70 $duplicate_entry_time = apply_filters( 'frm_time_to_check_duplicates', 60, $new_values );
49 71
@@ -50,10 +72,14 @@
50 72 if ( false === self::is_duplicate_check_needed( $values, $duplicate_entry_time ) ) {
51 73 return false;
52 74 }
53 75
76 + if ( self::maybe_check_for_unique_id_match( $values, $new_values['created_at'] ) ) {
77 + return true;
78 + }
79 +
54 80 $check_val = $new_values;
55 - $check_val['created_at >'] = gmdate( 'Y-m-d H:i:s', ( strtotime( $new_values['created_at'] ) - absint( $duplicate_entry_time ) ) );
81 + $check_val['created_at >'] = gmdate( 'Y-m-d H:i:s', strtotime( $new_values['created_at'] ) - absint( $duplicate_entry_time ) );
56 82
57 83 unset( $check_val['created_at'], $check_val['updated_at'], $check_val['is_draft'], $check_val['id'], $check_val['item_key'] );
58 84
59 85 if ( $new_values['item_key'] == $new_values['name'] ) {
@@ -79,8 +105,11 @@
79 105 // make sure it's a duplicate
80 106 $metas = FrmEntryMeta::get_entry_meta_info( $entry_exist );
81 107 $field_metas = array();
82 108 foreach ( $metas as $meta ) {
109 + if ( 0 === (int) $meta->field_id ) {
110 + continue;
111 + }
83 112 $field_metas[ $meta->field_id ] = $meta->meta_value;
84 113 }
85 114
86 115 $filtered_vals = array_filter( $values['item_meta'] );
@@ -106,12 +135,11 @@
106 135 continue;
107 136 }
108 137
109 138 $diff = array_diff_assoc( $field_metas, $new_meta );
110 - foreach ( $diff as $field_id => $meta_value ) {
139 + foreach ( $diff as $meta_value ) {
111 140 if ( ! empty( $meta_value ) ) {
112 141 $is_duplicate = false;
113 - continue;
114 142 }
115 143 }
116 144
117 145 if ( $is_duplicate ) {
@@ -116,9 +144,9 @@
116 144
117 145 if ( $is_duplicate ) {
118 146 break;
119 147 }
120 - }
148 + }//end foreach
121 149
122 150 $frm_vars['checking_duplicates'] = false;
123 151
124 152 return $is_duplicate;
@@ -124,8 +152,67 @@
124 152 return $is_duplicate;
125 153 }
126 154
127 155 /**
156 + * @since 6.16.3
157 + *
158 + * @param array $values POST request data.
159 + * @param string $created_at The timestamp of the entry we are checking for.
160 + * @return bool
161 + */
162 + private static function maybe_check_for_unique_id_match( $values, $created_at ) {
163 + if ( ! self::should_check_for_unique_id_match() ) {
164 + return false;
165 + }
166 +
167 + if ( empty( $values['unique_id'] ) ) {
168 + return false;
169 + }
170 +
171 + $unique_id = sanitize_key( $values['unique_id'] );
172 + if ( ! $unique_id ) {
173 + // Only continue if a unique ID was generated on form submit.
174 + return false;
175 + }
176 +
177 + if ( isset( self::$unique_id_match_checks[ $unique_id ] ) ) {
178 + return self::$unique_id_match_checks[ $unique_id ];
179 + }
180 +
181 + $timestamp = strtotime( $created_at );
182 + if ( false === $timestamp ) {
183 + $timestamp = time();
184 + }
185 +
186 + self::$unique_id_match_checks[ $unique_id ] = (bool) FrmDb::get_var(
187 + 'frm_item_metas',
188 + array(
189 + 'field_id' => 0,
190 + 'meta_value' => serialize( compact( 'unique_id' ) ),
191 + 'created_at >' => gmdate( 'Y-m-d H:i:s', $timestamp - MONTH_IN_SECONDS ),
192 + ),
193 + 'id'
194 + );
195 +
196 + return self::$unique_id_match_checks[ $unique_id ];
197 + }
198 +
199 + /**
200 + * @since 6.16.3
201 + */
202 + private static function should_check_for_unique_id_match() {
203 + /**
204 + * Allow users to opt out of the DB query, in case it causes performance issues.
205 + *
206 + * @since 6.16.3
207 + *
208 + * @param bool $should_extend
209 + */
210 + $should_check = apply_filters( 'frm_check_for_unique_id_match', true );
211 + return (bool) $should_check;
212 + }
213 +
214 + /**
128 215 * Convert form data to the actual value that would be saved into the database.
129 216 * This is important for the duplicate check as something like 'a:2:{s:5:"typed";s:0:"";s:6:"output";s:0:"";}' (a signature value) is actually an empty string and does not get saved.
130 217 *
131 218 * @param array $filter_vals
@@ -150,9 +237,9 @@
150 237 *
151 238 * @since 2.0.23
152 239 *
153 240 * @param array $values
154 - * @param int $duplicate_entry_time
241 + * @param int $duplicate_entry_time
155 242 *
156 243 * @return bool
157 244 */
158 245 private static function is_duplicate_check_needed( $values, $duplicate_entry_time ) {
@@ -212,12 +299,12 @@
212 299
213 300 /**
214 301 * Update an entry (not via XML)
215 302 *
216 - * @param int $id
303 + * @param int $id
217 304 * @param array $values
218 305 *
219 - * @return boolean|int $update_results
306 + * @return bool|int $update_results
220 307 */
221 308 public static function update( $id, $values ) {
222 309 $update_results = self::update_entry( $id, $values, 'standard' );
223 310
@@ -228,12 +315,12 @@
228 315 * Update an entry with some differences depending on the update type
229 316 *
230 317 * @since 2.0.16
231 318 *
232 - * @param int $id
319 + * @param int $id
233 320 * @param array $values
234 321 *
235 - * @return boolean|int $query_results
322 + * @return bool|int $query_results
236 323 */
237 324 private static function update_entry( $id, $values, $update_type ) {
238 325 global $wpdb;
239 326
@@ -253,9 +340,9 @@
253 340
254 341 /**
255 342 * Delete an entry.
256 343 *
257 - * @param string|int $id
344 + * @param int|string $id
258 345 * @return bool True on success, false if nothing was deleted.
259 346 */
260 347 public static function destroy( $id ) {
261 348 global $wpdb;
@@ -260,9 +347,10 @@
260 347 public static function destroy( $id ) {
261 348 global $wpdb;
262 349 $id = (int) $id;
263 350
264 - $entry = self::getOne( $id, true ); // Item meta is required for conditional logic in actions with 'delete' events.
351 + // Item meta is required for conditional logic in actions with 'delete' events.
352 + $entry = self::getOne( $id, true );
265 353 if ( ! $entry ) {
266 354 $result = false;
267 355 return $result;
268 356 }
@@ -335,11 +423,12 @@
335 423
336 424 /**
337 425 * If $entry is numeric, get the entry object
338 426 *
339 - * @param int|object $entry by reference
427 + * @since 2.0.9
340 428 *
341 - * @since 2.0.9
429 + * @param int|object $entry By reference.
430 + * @return void
342 431 */
343 432 public static function maybe_get_entry( &$entry ) {
344 433 if ( $entry && is_numeric( $entry ) ) {
345 434 $entry = self::getOne( $entry );
@@ -353,9 +442,9 @@
353 442
354 443 $query = "SELECT it.*, fr.name as form_name, fr.form_key as form_key FROM {$wpdb->prefix}frm_items it
355 444 LEFT OUTER JOIN {$wpdb->prefix}frm_forms fr ON it.form_id=fr.id WHERE ";
356 445
357 - $query .= is_numeric( $id ) ? 'it.id=%d' : 'it.item_key=%s';
446 + $query .= is_numeric( $id ) ? 'it.id=%d' : 'it.item_key=%s';
358 447 $query_args = array( $id );
359 448 $query = $wpdb->prepare( $query, $query_args ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
360 449
361 450 if ( ! $meta ) {
@@ -387,9 +476,10 @@
387 476 return;
388 477 }
389 478
390 479 FrmAppHelper::unserialize_or_decode( $entry->description );
391 - $entry = wp_unslash( $entry ); // TODO: Remove slashes on input only, not output.
480 + // TODO: Remove slashes on input only, not output.
481 + $entry = wp_unslash( $entry );
392 482 }
393 483
394 484 /**
395 485 * @since 4.02.03
@@ -466,9 +556,9 @@
466 556 $where = array( 'item_key' => $id );
467 557 }
468 558 $id = FrmDb::get_var( $wpdb->prefix . 'frm_items', $where );
469 559
470 - return ( $id && $id > 0 );
560 + return $id && $id > 0;
471 561 }
472 562
473 563 public static function getAll( $where, $order_by = '', $limit = '', $meta = false, $inc_form = true ) {
474 564 global $wpdb;
@@ -483,9 +573,9 @@
483 573 $table = $wpdb->prefix . 'frm_items it ';
484 574
485 575 if ( $inc_form ) {
486 576 $fields = 'it.*, fr.name as form_name,fr.form_key as form_key';
487 - $table .= 'LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id ';
577 + $table .= 'LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id ';
488 578 }
489 579
490 580 if ( preg_match( '/ meta_([0-9]+)/', $order_by, $order_matches ) ) {
491 581 $fields .= self::sort_by_field( $order_matches[1] );
@@ -498,9 +588,9 @@
498 588 $entries = $wpdb->get_results( $query, OBJECT_K ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
499 589 unset( $query );
500 590
501 591 FrmDb::set_cache( $cache_key, $entries, 'frm_entry' );
502 - }
592 + }//end if
503 593
504 594 if ( ! $meta || ! $entries ) {
505 595 self::prepare_entries( $entries );
506 596 return $entries;
@@ -577,13 +667,14 @@
577 667 }
578 668
579 669 // Pagination Methods
580 670 /**
581 - * @param int|array|string If int, use the form id.
671 + * @param array|int|string $where If int, use the form id.
672 + * @return int|string
582 673 */
583 674 public static function getRecordCount( $where = '' ) {
584 675 global $wpdb;
585 - $table_join = $wpdb->prefix . 'frm_items it LEFT OUTER JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id';
676 + $table_join = $wpdb->prefix . 'frm_items it JOIN ' . $wpdb->prefix . 'frm_forms fr ON it.form_id=fr.id';
586 677
587 678 if ( is_numeric( $where ) ) {
588 679 $table_join = 'frm_items';
589 680 $where = array( 'form_id' => $where );
@@ -599,8 +690,12 @@
599 690
600 691 return $count;
601 692 }
602 693
694 + /**
695 + * @param int|string $p_size
696 + * @return int
697 + */
603 698 public static function getPageCount( $p_size, $where = '' ) {
604 699 $p_size = (int) $p_size;
605 700 $count = 1;
606 701 if ( $p_size ) {
@@ -617,9 +712,9 @@
617 712 * Prepare the data before inserting it into the database
618 713 *
619 714 * @since 2.0.16
620 715 *
621 - * @param array $values
716 + * @param array $values
622 717 * @param string $type
623 718 *
624 719 * @return array $new_values
625 720 */
@@ -626,9 +721,9 @@
626 721 private static function before_insert_entry_in_database( &$values, $type ) {
627 722
628 723 self::sanitize_entry_post( $values );
629 724
630 - if ( $type != 'xml' ) {
725 + if ( $type !== 'xml' ) {
631 726 $values = apply_filters( 'frm_pre_create_entry', $values );
632 727 }
633 728
634 729 $new_values = self::package_entry_data( $values );
@@ -643,9 +738,9 @@
643 738 *
644 739 * @param array $values
645 740 * @param array $new_values
646 741 *
647 - * @return boolean|int $entry_id
742 + * @return bool|int $entry_id
648 743 */
649 744 private static function continue_to_create_entry( $values, $new_values ) {
650 745 $entry_id = self::insert_entry_into_database( $new_values );
651 746 if ( ! $entry_id ) {
@@ -661,9 +756,9 @@
661 756 * Sanitize the POST values before we use them
662 757 *
663 758 * @since 2.0
664 759 *
665 - * @param array $values The POST values by reference
760 + * @param array $values The POST values by reference.
666 761 */
667 762 public static function sanitize_entry_post( &$values ) {
668 763 $sanitize_method = array(
669 764 'form_id' => 'absint',
@@ -754,9 +849,13 @@
754 849 *
755 850 * @return int
756 851 */
757 852 private static function get_is_draft_value( $values ) {
758 - return ( ( isset( $values['frm_saving_draft'] ) && $values['frm_saving_draft'] == 1 ) || ( isset( $values['is_draft'] ) && $values['is_draft'] == 1 ) ) ? 1 : 0;
853 + if ( isset( $values['frm_saving_draft'] ) && FrmEntriesHelper::DRAFT_ENTRY_STATUS === (int) $values['frm_saving_draft'] ) {
854 + return FrmEntriesHelper::DRAFT_ENTRY_STATUS;
855 + }
856 +
857 + return isset( $values['is_draft'] ) ? absint( $values['is_draft'] ) : FrmEntriesHelper::SUBMITTED_ENTRY_STATUS;
759 858 }
760 859
761 860 /**
762 861 * Get the created_at value for a new entry
@@ -799,9 +898,9 @@
799 898 *
800 899 * @return string
801 900 */
802 901 private static function get_entry_description( $values ) {
803 - if ( isset( $values['description'] ) && ! empty( $values['description'] ) ) {
902 + if ( ! empty( $values['description'] ) ) {
804 903 $description = FrmAppHelper::maybe_json_encode( $values['description'] );
805 904 } else {
806 905 $description = json_encode(
807 906 array(
@@ -840,9 +939,9 @@
840 939 * @since 2.0.16
841 940 *
842 941 * @param array $new_values
843 942 *
844 - * @return int | boolean $entry_id
943 + * @return bool|int $entry_id
845 944 */
846 945 private static function insert_entry_into_database( $new_values ) {
847 946 global $wpdb;
848 947
@@ -879,19 +978,41 @@
879 978 *
880 979 * @since 2.0.16
881 980 *
882 981 * @param array $values
883 - * @param int $entry_id
982 + * @param int $entry_id
884 983 * @return void
885 984 */
886 985 private static function maybe_add_entry_metas( $values, $entry_id ) {
887 986 if ( isset( $values['item_meta'] ) ) {
888 987 FrmEntryMeta::update_entry_metas( $entry_id, $values['item_meta'] );
988 + self::maybe_add_unique_id_meta( $values, $entry_id );
889 989 }
890 990 self::maybe_add_captcha_meta( (int) $values['form_id'], (int) $entry_id );
891 991 }
892 992
893 993 /**
994 + * @since 6.16.3
995 + *
996 + * @param array $values
997 + * @param int $entry_id
998 + * @return void
999 + */
1000 + private static function maybe_add_unique_id_meta( $values, $entry_id ) {
1001 + if ( ! empty( $values['parent_form_id'] ) || empty( $values['unique_id'] ) || ! self::should_check_for_unique_id_match() ) {
1002 + return;
1003 + }
1004 +
1005 + // This unique ID is inserted with JS on form submit.
1006 + // It is used to check for duplicate entries.
1007 + $unique_id = sanitize_key( $values['unique_id'] );
1008 + if ( $unique_id ) {
1009 + FrmEntryMeta::add_entry_meta( $entry_id, 0, '', compact( 'unique_id' ) );
1010 + self::flag_new_unique_key( $unique_id );
1011 + }
1012 + }
1013 +
1014 + /**
894 1015 * @since 5.0.15
895 1016 *
896 1017 * @param int $form_id
897 1018 * @param int $entry_id
@@ -909,14 +1030,15 @@
909 1030 * Trigger frm_after_create_entry hooks
910 1031 *
911 1032 * @since 2.0.16
912 1033 *
913 - * @param int $entry_id
1034 + * @param int $entry_id
1035 + * @param array $values
914 1036 * @param array $new_values
915 1037 */
916 1038 private static function after_entry_created_actions( $entry_id, $values, $new_values ) {
917 - // this is a child entry
918 - $is_child = isset( $values['parent_form_id'] ) && isset( $values['parent_nonce'] ) && ! empty( $values['parent_form_id'] ) && wp_verify_nonce( $values['parent_nonce'], 'parent' );
1039 + // This is a child entry.
1040 + $is_child = isset( $values['parent_nonce'] ) && ! empty( $values['parent_form_id'] ) && wp_verify_nonce( $values['parent_nonce'], 'parent' );
919 1041
920 1042 do_action( 'frm_after_create_entry', $entry_id, $new_values['form_id'], compact( 'is_child' ) );
921 1043 do_action( 'frm_after_create_entry_' . $new_values['form_id'], $entry_id, compact( 'is_child' ) );
922 1044 }
@@ -927,9 +1049,9 @@
927 1049 * @since 2.0.16
928 1050 *
929 1051 * @param array $values
930 1052 * @param array $new_values
931 - * @param int $entry_id
1053 + * @param int $entry_id
932 1054 */
933 1055 private static function after_insert_entry_in_database( $values, $new_values, $entry_id ) {
934 1056
935 1057 self::add_new_entry_to_frm_vars( $entry_id );
@@ -945,13 +1067,13 @@
945 1067 * Perform some actions right before updating an entry
946 1068 *
947 1069 * @since 2.0.16
948 1070 *
949 - * @param int $id
950 - * @param array $values
1071 + * @param int $id
1072 + * @param array $values
951 1073 * @param string $update_type
952 1074 *
953 - * @return boolean $update
1075 + * @return bool $update
954 1076 */
955 1077 private static function before_update_entry( $id, &$values, $update_type ) {
956 1078 $update = true;
957 1079
@@ -956,13 +1078,13 @@
956 1078 $update = true;
957 1079
958 1080 global $frm_vars;
959 1081
960 - if ( isset( $frm_vars['saved_entries'] ) && is_array( $frm_vars['saved_entries'] ) && in_array( (int) $id, (array) $frm_vars['saved_entries'] ) ) {
1082 + if ( isset( $frm_vars['saved_entries'] ) && is_array( $frm_vars['saved_entries'] ) && in_array( (int) $id, $frm_vars['saved_entries'] ) ) {
961 1083 $update = false;
962 1084 }
963 1085
964 - if ( $update && $update_type != 'xml' ) {
1086 + if ( $update && $update_type !== 'xml' ) {
965 1087 $values = apply_filters( 'frm_pre_update_entry', $values, $id );
966 1088 }
967 1089
968 1090 return $update;
@@ -972,9 +1094,9 @@
972 1094 * Package the entry data for updating
973 1095 *
974 1096 * @since 2.0.16
975 1097 *
976 - * @param int $id
1098 + * @param int $id
977 1099 * @param array $values
978 1100 *
979 1101 * @return array $new_values
980 1102 */
@@ -1014,12 +1136,12 @@
1014 1136 * Perform some actions right after updating an entry
1015 1137 *
1016 1138 * @since 2.0.16
1017 1139 *
1018 - * @param boolean|int $query_results
1019 - * @param int $id
1020 - * @param array $values
1021 - * @param array $new_values
1140 + * @param bool|int $query_results
1141 + * @param int $id
1142 + * @param array $values
1143 + * @param array $new_values
1022 1144 */
1023 1145 private static function after_update_entry( $query_results, $id, $values, $new_values ) {
1024 1146 if ( $query_results ) {
1025 1147 self::clear_cache();
@@ -1047,9 +1169,9 @@
1047 1169 * @since 2.0.16
1048 1170 *
1049 1171 * @param array $values
1050 1172 *
1051 - * @return int | boolean $entry_id
1173 + * @return bool|int $entry_id
1052 1174 */
1053 1175 public static function create_entry_from_xml( $values ) {
1054 1176 $entry_id = self::create_entry( $values, 'xml' );
1055 1177
@@ -1061,12 +1183,12 @@
1061 1183 * Certain actions aren't necessary when importing (like saving sub entries and modifying other vals)
1062 1184 *
1063 1185 * @since 2.0.16
1064 1186 *
1065 - * @param int $id
1187 + * @param int $id
1066 1188 * @param array $values
1067 1189 *
1068 - * @return int | boolean $updated
1190 + * @return bool|int $updated
1069 1191 */
1070 1192 public static function update_entry_from_xml( $id, $values ) {
1071 1193 $updated = self::update_entry( $id, $values, 'xml' );
1072 1194
@@ -1081,6 +1203,22 @@
1081 1203 public static function get_id_by_key( $key ) {
1082 1204 $entry_id = FrmDb::get_var( 'frm_items', array( 'item_key' => sanitize_title( $key ) ) );
1083 1205
1084 1206 return (int) $entry_id;
1207 + }
1208 +
1209 + /**
1210 + * Get entries count.
1211 + *
1212 + * @since 6.8
1213 + *
1214 + * @return int|string
1215 + */
1216 + public static function get_entries_count() {
1217 + $args = array(
1218 + 'or' => 1,
1219 + 'parent_form_id' => null,
1220 + 'parent_form_id <' => 1,
1221 + );
1222 + return self::getRecordCount( $args );
1085 1223 }
1086 1224 }