PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.8
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.8
2.12.8 2.12.7 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 All 98 releases
← All changes | inc/database/tables/payments.php +121 -72 2.7.0 → 2.12.8 View file →
@@ -116,8 +116,9 @@
116 116 'MXN',
117 117 'SGD',
118 118 'HKD',
119 119 'NOK',
120 + 'PLN',
120 121 'TRY',
121 122 'RUB',
122 123 'INR',
123 124 'BRL',
@@ -1093,43 +1094,12 @@
1093 1094
1094 1095 // Handle additional where conditions if provided.
1095 1096 if ( ! empty( $_args['where'] ) ) {
1096 1097 foreach ( $_args['where'] as $where_group ) {
1097 - if ( is_array( $where_group ) ) {
1098 - foreach ( $where_group as $condition ) {
1099 - if ( isset( $condition['key'], $condition['compare'], $condition['value'] ) ) {
1100 - // Validate column name against whitelist.
1101 - if ( ! in_array( $condition['key'], self::ALLOWED_COLUMNS, true ) ) {
1102 - continue;
1103 - }
1104 -
1105 - // Validate and normalize the comparison operator.
1106 - $operator = strtoupper( trim( $condition['compare'] ) );
1107 -
1108 - // Skip this condition if operator is not in whitelist.
1109 - if ( ! in_array( $operator, self::ALLOWED_OPERATORS, true ) ) {
1110 - continue;
1111 - }
1112 -
1113 - $column = $condition['key'];
1114 -
1115 - if ( in_array( $operator, [ 'IN', 'NOT IN' ], true ) && is_array( $condition['value'] ) ) {
1116 - $ids = array_map( 'absint', $condition['value'] );
1117 - if ( empty( $ids ) ) {
1118 - $ids = [ 0 ];
1119 - }
1120 - $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
1121 - $where_clause .= " AND {$column} {$operator} ({$placeholders})";
1122 - foreach ( $ids as $id ) {
1123 - $params[] = $id;
1124 - }
1125 - } else {
1126 - $where_clause .= " AND {$column} {$operator} %s";
1127 - $params[] = $condition['value'];
1128 - }
1129 - }
1130 - }
1098 + if ( ! is_array( $where_group ) ) {
1099 + continue;
1131 1100 }
1101 + $where_clause .= self::build_clause_for_group( $where_group, $params );
1132 1102 }
1133 1103 }
1134 1104
1135 1105 // Order by.
@@ -1155,9 +1125,9 @@
1155 1125 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query string is built dynamically above based on conditions.
1156 1126 $query = $wpdb->prepare( $query, $params );
1157 1127 }
1158 1128
1159 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table query with dynamic preparation, caching not applicable for dynamic queries.
1129 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query with dynamic preparation; table name internal, not user input; caching not applicable for dynamic queries.
1160 1130 $results = $wpdb->get_results( $query, ARRAY_A );
1161 1131
1162 1132 return is_array( $results ) ? $results : [];
1163 1133 }
@@ -1197,45 +1167,12 @@
1197 1167
1198 1168 // Handle additional where conditions if provided.
1199 1169 if ( ! empty( $where_conditions ) ) {
1200 1170 foreach ( $where_conditions as $where_group ) {
1201 - if ( is_array( $where_group ) ) {
1202 - foreach ( $where_group as $condition ) {
1203 - if ( isset( $condition['key'], $condition['compare'], $condition['value'] ) ) {
1204 - // Validate column name against whitelist.
1205 - if ( ! in_array( $condition['key'], self::ALLOWED_COLUMNS, true ) ) {
1206 - continue;
1207 - }
1208 -
1209 - // Validate and normalize the comparison operator.
1210 - $operator = strtoupper( trim( $condition['compare'] ) );
1211 -
1212 - // Skip this condition if operator is not in whitelist.
1213 - if ( ! in_array( $operator, self::ALLOWED_OPERATORS, true ) ) {
1214 - continue;
1215 - }
1216 -
1217 - $column = $condition['key'];
1218 -
1219 - // Special handling for IN/NOT IN with arrays.
1220 - if ( in_array( $operator, [ 'IN', 'NOT IN' ], true ) && is_array( $condition['value'] ) ) {
1221 - $ids = array_map( 'absint', $condition['value'] );
1222 - // Prevent empty IN ().
1223 - if ( empty( $ids ) ) {
1224 - $ids = [ 0 ];
1225 - }
1226 - $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
1227 - $where_clause .= " AND {$column} {$operator} ({$placeholders})";
1228 - foreach ( $ids as $id ) {
1229 - $params[] = $id;
1230 - }
1231 - } else {
1232 - $where_clause .= " AND {$column} {$operator} %s";
1233 - $params[] = $condition['value'];
1234 - }
1235 - }
1236 - }
1171 + if ( ! is_array( $where_group ) ) {
1172 + continue;
1237 1173 }
1174 + $where_clause .= self::build_clause_for_group( $where_group, $params );
1238 1175 }
1239 1176 }
1240 1177
1241 1178 // Build and execute query.
@@ -1245,9 +1182,9 @@
1245 1182 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query string is built dynamically above based on conditions.
1246 1183 $query = $wpdb->prepare( $query, $params );
1247 1184 }
1248 1185
1249 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- Custom table query with dynamic preparation, caching not applicable for count operations.
1186 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom table query with dynamic preparation; table name internal, not user input; caching not applicable for count operations.
1250 1187 $result = $wpdb->get_var( $query );
1251 1188
1252 1189 return absint( $result );
1253 1190 }
@@ -1290,6 +1227,118 @@
1290 1227 return false;
1291 1228 }
1292 1229
1293 1230 return 'payment' === ( $payment['type'] ?? '' ) && ! empty( $payment['subscription_id'] );
1231 + }
1232 +
1233 + /**
1234 + * Build the SQL fragment for a single where group, handling both flat
1235 + * AND-only groups and explicit RELATION (OR/AND) groups.
1236 + *
1237 + * Flat group example:
1238 + * [ [ 'key' => 'status', 'compare' => '=', 'value' => 'paid' ] ]
1239 + * → " AND status = %s"
1240 + *
1241 + * RELATION group example:
1242 + * [
1243 + * 'RELATION' => 'OR',
1244 + * [ 'key' => 'status', 'compare' => '=', 'value' => 'canceled' ],
1245 + * [ 'key' => 'subscription_status', 'compare' => '=', 'value' => 'canceled' ],
1246 + * ]
1247 + * → " AND (status = %s OR subscription_status = %s)"
1248 + *
1249 + * Conditions with disallowed columns or operators are silently skipped.
1250 + * Always prefixes the returned fragment with " AND " so callers can append
1251 + * directly to a `WHERE 1=1` clause.
1252 + *
1253 + * @param array<int|string,mixed> $where_group Group of conditions, optionally with 'RELATION'.
1254 + * @param array<mixed> $params Reference to running params array; appended in place.
1255 + * @since 2.9.0
1256 + * @return string SQL fragment to append, or empty string if nothing valid.
1257 + */
1258 + private static function build_clause_for_group( array $where_group, array &$params ) {
1259 + if ( empty( $where_group ) ) {
1260 + return '';
1261 + }
1262 +
1263 + $is_relation_group = ! empty( $where_group['RELATION'] ) && is_string( $where_group['RELATION'] );
1264 + $relation = $is_relation_group && 'OR' === strtoupper( $where_group['RELATION'] )
1265 + ? 'OR'
1266 + : 'AND';
1267 +
1268 + $sub_clauses = [];
1269 +
1270 + foreach ( $where_group as $key => $condition ) {
1271 + if ( 'RELATION' === $key || ! is_array( $condition ) ) {
1272 + continue;
1273 + }
1274 + if ( ! isset( $condition['key'], $condition['compare'], $condition['value'] ) ) {
1275 + continue;
1276 + }
1277 + if ( ! in_array( $condition['key'], self::ALLOWED_COLUMNS, true ) ) {
1278 + continue;
1279 + }
1280 +
1281 + $operator = strtoupper( trim( (string) $condition['compare'] ) );
1282 + if ( ! in_array( $operator, self::ALLOWED_OPERATORS, true ) ) {
1283 + continue;
1284 + }
1285 +
1286 + $column = $condition['key'];
1287 +
1288 + if ( in_array( $operator, [ 'IN', 'NOT IN' ], true ) ) {
1289 + if ( ! is_array( $condition['value'] ) ) {
1290 + // A scalar used to fall through to the else branch and emit
1291 + // `col IN %s`, which is a syntax error that fails the whole
1292 + // query. Base::prepare_where_clauses() reports this and drops
1293 + // the condition; do the same rather than leave the two builders
1294 + // disagreeing on malformed input.
1295 + _doing_it_wrong(
1296 + __METHOD__,
1297 + esc_html( "{$operator} requires an array value, received " . gettype( $condition['value'] ) . '.' ),
1298 + '2.12.7'
1299 + );
1300 + continue;
1301 + }
1302 +
1303 + $ids = array_map( 'absint', $condition['value'] );
1304 + if ( [] === $ids ) {
1305 + // Same empty-list handling as Base::prepare_where_clauses(), so
1306 + // the two builders cannot disagree. An empty IN matches nothing.
1307 + // An empty NOT IN excludes nothing, and is dropped rather than
1308 + // written as a literal, because a literal true would make an
1309 + // enclosing OR group match every row.
1310 + //
1311 + // This builder serves the manage_options-gated admin payments
1312 + // listing only, through get_all_main_payments() and
1313 + // get_total_main_payments_by_status(). The payment-history
1314 + // shortcode's customer filter is compiled by
1315 + // Base::prepare_where_clauses() via Payments::get_all() -- that
1316 + // group is where an OR fail-open would actually leak, and it is
1317 + // covered by the change in base.php.
1318 + if ( 'IN' === $operator ) {
1319 + $sub_clauses[] = '1 = 0';
1320 + }
1321 + continue;
1322 + }
1323 + $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
1324 + $sub_clauses[] = "{$column} {$operator} ({$placeholders})";
1325 + foreach ( $ids as $id ) {
1326 + $params[] = $id;
1327 + }
1328 + } else {
1329 + $sub_clauses[] = "{$column} {$operator} %s";
1330 + $params[] = $condition['value'];
1331 + }
1332 + }
1333 +
1334 + if ( empty( $sub_clauses ) ) {
1335 + return '';
1336 + }
1337 +
1338 + if ( $is_relation_group ) {
1339 + return ' AND (' . implode( " {$relation} ", $sub_clauses ) . ')';
1340 + }
1341 +
1342 + return ' AND ' . implode( ' AND ', $sub_clauses );
1294 1343 }
1295 1344 }