PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.9.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.9.1
V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 2.10.2 All 137 releases
← All changes | includes/Core/Database/Model.php +35 -195 3.2.12.9.1 View file →
@@ -9,9 +9,8 @@
9 9 /**
10 10 * Undocumented class
11 11 */
12 12
13 -use BitCode\BitForm\Core\Util\Log;
14 13 use WP_Error;
15 14
16 15 class Model
17 16 {
@@ -31,16 +30,16 @@
31 30 $this->table_name = $wpdb->prefix . static::$table;
32 31 }
33 32
34 33 /**
35 - * Insert a row
34 + * Undocumented function
36 35 *
37 - * @return mixed insert id on success, WP_Error on failure
36 + * @return void
38 37 */
39 38 public function insert($data = [])
40 39 {
41 40 if (is_null($data)) {
42 - return new WP_Error('empty_data', 'Form data is empty');
41 + return new WP_Error('empty_data', __('Form data is empty', 'bit-form'));
43 42 }
44 43 $result = $this->app_db->insert(
45 44 $this->table_name,
46 45 $data
@@ -50,24 +49,22 @@
50 49
51 50 /**
52 51 * Undocumented function
53 52 *
54 - * @param string|string[] $item
55 - * @param array $condition
53 + * @param string $item
54 + * @param array $condition
56 55 *
57 56 * @return mixed
58 57 */
59 58 public function get($item = '*', $condition = [], $limit = null, $offset = null, $order_by = null, $order_follow = null)
60 59 {
61 - static $tableExistsCache = [];
62 - if (!isset($tableExistsCache[$this->table_name])) {
63 - $tableExistsCache[$this->table_name] = !is_null(
64 - $this->app_db->get_var(
65 - $this->app_db->prepare('SHOW TABLES LIKE %s', $this->table_name)
66 - )
67 - );
68 - }
69 - if (!$tableExistsCache[$this->table_name]) {
60 + $checkIfTableExists = $this->app_db->get_var(
61 + $this->app_db->prepare(
62 + 'SHOW TABLES LIKE %s',
63 + $this->table_name
64 + )
65 + );
66 + if (is_null($checkIfTableExists)) {
70 67 return [];
71 68 }
72 69 if (\is_array($item)) {
73 70 $column_to_select = implode(',', $item);
@@ -80,19 +77,9 @@
80 77 }
81 78 $order = null;
82 79 if (!\is_null($order_by)) {
83 80 $order_follow = \is_null($order_follow) ? 'ASC' : $order_follow;
84 - $direction = \is_string($order_follow) ? strtoupper(trim($order_follow)) : '';
85 - if ($this->isSafeConditionIdentifier($order_by) && \in_array($direction, ['ASC', 'DESC'], true)) {
86 - $order .= ' ORDER BY ' . $this->quoteIdentifier($order_by) . ' ' . $direction;
87 - } else {
88 - Log::debug_log([
89 - 'message' => 'Model::get() ignored an unsafe ORDER BY',
90 - 'table' => $this->table_name,
91 - 'order_by' => $order_by,
92 - 'order_follow' => $order_follow,
93 - ]);
94 - }
81 + $order .= " ORDER BY $order_by $order_follow";
95 82 }
96 83 $paginate = null;
97 84 if (!\is_null($limit)) {
98 85 $limit = \intval($limit);
@@ -148,9 +135,9 @@
148 135 $all_values = null;
149 136 }
150 137 $result = $this->app_db->query(
151 138 $this->app_db->prepare(
152 - "SELECT COUNT(*) as count FROM `{$this->table_name}`"
139 + "SELECT COUNT(*) as count FROM `$this->table_name`"
153 140 . $condition_to_check,
154 141 $all_values
155 142 )
156 143 );
@@ -158,9 +145,9 @@
158 145 if (!$result) {
159 146 if ($this->app_db->last_error) {
160 147 return new WP_Error('db_error', $this->app_db->last_error);
161 148 }
162 - return new WP_Error('db_error', 'Result is empty');
149 + return new WP_Error('db_error', __('Result is empty', 'bit-form'));
163 150 } else {
164 151 return $this->app_db->last_result;
165 152 }
166 153 }
@@ -170,9 +157,9 @@
170 157 *
171 158 * @param array $data_to_update
172 159 * @param array $condition
173 160 *
174 - * @return mixed affected-row count on success, WP_Error on failure or when no row matched
161 + * @return void
175 162 */
176 163 public function update(array $data, array $condition)
177 164 {
178 165 if (
@@ -183,9 +170,9 @@
183 170 $data_to_update = $data;
184 171 } else {
185 172 return new WP_Error(
186 173 'update_error',
187 - 'Nothing to update'
174 + __('Nothing to update', 'bit-form')
188 175 );
189 176 }
190 177 $update_condition = (!\is_null($condition) &&
191 178 array_keys($condition) !== range(0, count($condition) - 1)) ? $condition : null;
@@ -215,9 +202,9 @@
215 202 $data_to_update = $data;
216 203 } else {
217 204 return new WP_Error(
218 205 'update_error',
219 - 'Nothing to update'
206 + __('Nothing to update', 'bit-form')
220 207 );
221 208 }
222 209
223 210 $update_fields = '';
@@ -242,9 +229,9 @@
242 229 $condition_to_check = null;
243 230 }
244 231 $result = $this->app_db->query(
245 232 $this->app_db->prepare(
246 - "UPDATE `{$this->table_name}` SET $update_fields $condition_to_check",
233 + "UPDATE $this->table_name SET $update_fields $condition_to_check",
247 234 $all_values
248 235 )
249 236 );
250 237 return $this->getResult($result);
@@ -267,9 +254,9 @@
267 254 && \is_array($duplicate)
268 255 && array_keys($duplicate) === range(0, count($duplicate) - 1))) {
269 256 return new WP_Error(
270 257 'duplicate_error',
271 - 'Nothing to duplicate'
258 + __('Nothing to duplicate', 'bit-form')
272 259 );
273 260 }
274 261
275 262 $dupCol = '';
@@ -294,10 +281,10 @@
294 281 if ($formatted_conditions) {
295 282 $condition_to_check = $formatted_conditions['conditions'];
296 283 $all_values = array_merge($all_values, $formatted_conditions['values']);
297 284 }
298 - $query = "INSERT INTO `{$this->table_name}` ($insCol)
299 - SELECT $dupCol FROM `{$this->table_name}` $condition_to_check";
285 + $query = "INSERT INTO $this->table_name ($insCol)
286 + SELECT $dupCol FROM $this->table_name $condition_to_check";
300 287 $this->execute($query, $all_values);
301 288 return $this->getResult();
302 289 }
303 290
@@ -311,9 +298,9 @@
311 298 $delete_condition = $condition;
312 299 } else {
313 300 return new WP_Error(
314 301 'deletion_error',
315 - 'At least 1 condition needed'
302 + __('At least 1 condition needed', 'bit-form')
316 303 );
317 304 }
318 305 $update_condition = (!\is_null($condition) &&
319 306 array_keys($condition) !== range(0, count($condition) - 1)) ? $condition : null;
@@ -335,9 +322,9 @@
335 322 $delete_condition = $condition;
336 323 } else {
337 324 return new WP_Error(
338 325 'deletion_error',
339 - 'At least 1 condition needed'
326 + __('At least 1 condition needed', 'bit-form')
340 327 );
341 328 }
342 329 $result = $this->app_db->delete(
343 330 $this->table_name,
@@ -356,9 +343,9 @@
356 343 $delete_condition = $condition;
357 344 } else {
358 345 return new WP_Error(
359 346 'deletion_error',
360 - 'At least 1 condition needed'
347 + __('At least 1 condition needed', 'bit-form')
361 348 );
362 349 }
363 350 // $formatted_conditions = $this->getFormatedCondition($delete_condition, $check_operator);
364 351 $formatted_conditions = $this->getFormatedCondition($delete_condition);
@@ -368,14 +355,14 @@
368 355 } else {
369 356 $condition_to_check = null;
370 357 return new WP_Error(
371 358 'deletion_error',
372 - 'At least 1 condition needed'
359 + __('At least 1 condition needed', 'bit-form')
373 360 );
374 361 }
375 362 $result = $this->app_db->query(
376 363 $this->app_db->prepare(
377 - "DELETE FROM `{$this->table_name}` $condition_to_check",
364 + "DELETE FROM $this->table_name $condition_to_check",
378 365 $all_values
379 366 )
380 367 );
381 368 return $this->getResult($result);
@@ -386,88 +373,8 @@
386 373 return ('integer' === gettype($value)) ?
387 374 '%d' : (('double' === gettype($value)) ? '%f' : '%s');
388 375 }
389 376
390 - /**
391 - *
392 - * @param mixed $identifier
393 - *
394 - * @return bool
395 - */
396 - protected function isSafeConditionIdentifier($identifier)
397 - {
398 - if (!\is_string($identifier)) {
399 - return false;
400 - }
401 - $identifier = trim($identifier);
402 - if ('' === $identifier) {
403 - return false;
404 - }
405 -
406 - // A condition column may be table-qualified and backtick-quoted — the multi-table JOIN DELETE
407 - // in FormEntryModel::bulkDelete() *must* pass `wp_bitforms_form_entries`.`id`, because a bare
408 - // `id` is ambiguous across the two joined tables. Validate each segment on its own.
409 - $parts = explode('.', $identifier);
410 - if (count($parts) > 2) {
411 - return false;
412 - }
413 - foreach ($parts as $part) {
414 - $part = trim($part);
415 - if (\strlen($part) > 1 && '`' === $part[0] && '`' === substr($part, -1)) {
416 - $part = substr($part, 1, -1);
417 - }
418 - if (1 !== preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $part)) {
419 - return false;
420 - }
421 - }
422 -
423 - return true;
424 - }
425 -
426 - /**
427 - * Backtick-quote a validated identifier, leaving an already-quoted or table-qualified one alone.
428 - * Only ever call this on a value isSafeConditionIdentifier() has approved.
429 - *
430 - * @param string $identifier
431 - *
432 - * @return string
433 - */
434 - protected function quoteIdentifier($identifier)
435 - {
436 - $identifier = trim($identifier);
437 - if (false !== strpos($identifier, '`') || false !== strpos($identifier, '.')) {
438 - return $identifier;
439 - }
440 -
441 - return '`' . $identifier . '`';
442 - }
443 -
444 - /**
445 - * @param mixed $operator
446 - *
447 - * @return bool
448 - */
449 - protected function isSafeConditionOperator($operator)
450 - {
451 - static $allowed = ['=', '!=', '<>', '<', '>', '<=', '>=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'IS', 'IS NOT'];
452 -
453 - return \is_string($operator) && \in_array(strtoupper(trim($operator)), $allowed, true);
454 - }
455 -
456 - /**
457 - * A WHERE that matches nothing. Keeps a placeholder so the caller's
458 - * $wpdb->prepare($sql, $values) still has something to bind.
459 - *
460 - * @return array
461 - */
462 - private function impossibleCondition()
463 - {
464 - return [
465 - 'conditions' => ' WHERE 1=%d ',
466 - 'values' => [0],
467 - ];
468 - }
469 -
470 377 protected function getFormatedCondition($condition, $check_operator = null, $join_operator = ' AND ')
471 378 {
472 379 if (\is_null($condition)) {
473 380 return false;
@@ -476,28 +383,15 @@
476 383 $index_checker = 0;
477 384 $condition_to_check = ' WHERE ';
478 385 $all_values = [];
479 386 foreach ($condition as $key => $value) {
480 - if (!$this->isSafeConditionIdentifier($key)) {
481 - return $this->impossibleCondition();
482 - }
483 387 $value_type = '';
484 388 if (is_array($value)) {
485 - // Check for raw SQL values first
486 - if (isset($value['raw'])) {
487 - if (!\is_string($value['raw'])) {
488 - return $this->impossibleCondition();
489 - }
490 - $set_check_operator = isset($value['operator']) ? $value['operator'] : '=';
491 - $value_type = $value['raw']; // Use raw SQL directly
492 - // Don't add to $all_values since it's raw SQL
493 - } elseif (isset($value['operator'])) {
494 - // logic for operator arrays
389 + if (isset($value['operator'])) {
495 390 $set_check_operator = $value['operator'];
496 391 $value_type .= $this->getFieldFormat($value['value']);
497 392 $all_values[] = $value['value'];
498 393 } else {
499 - // logic for IN conditions
500 394 $set_check_operator = \is_null($check_operator) ? 'in' : $check_operator;
501 395 $value_type .= ' ( ';
502 396 $value_index_checker = 0;
503 397 $value_count = count($value) - 1;
@@ -511,16 +405,12 @@
511 405 }
512 406 $value_type .= ' )';
513 407 }
514 408 } else {
515 - // logic for simple values
516 409 $set_check_operator = \is_null($check_operator) ? '=' : $check_operator;
517 410 $value_type .= $this->getFieldFormat($value);
518 411 $all_values[] = $value;
519 412 }
520 - if (!$this->isSafeConditionOperator($set_check_operator)) {
521 - return $this->impossibleCondition();
522 - }
523 413 $condition_to_check = $condition_to_check . $key . " $set_check_operator " . $value_type;
524 414 if ($index_checker < $no_condition - 1) {
525 415 $condition_to_check = $condition_to_check . " $join_operator ";
526 416 }
@@ -531,24 +421,8 @@
531 421 'values' => $all_values
532 422 ];
533 423 }
534 424
535 - /**
536 - * @param array $values values about to be bound by $wpdb->prepare()
537 - *
538 - * @return string|null the offending PHP type, or null when every value is bindable
539 - */
540 - private function findUnbindableValue(array $values)
541 - {
542 - foreach ($values as $value) {
543 - if (!is_scalar($value) && !is_null($value)) {
544 - return \gettype($value);
545 - }
546 - }
547 -
548 - return null;
549 - }
550 -
551 425 protected function checkCondition(array $condition)
552 426 {
553 427 if (!is_null($condition) && array_keys($condition) === range(0, count($condition) - 1)) {
554 428 return new WP_Error(
@@ -560,31 +434,16 @@
560 434 }
561 435
562 436 protected function execute($sql, $values = null)
563 437 {
564 - // Clear the previous call's outcome before running a new query, so a failure can never be
565 - // read back by whatever this instance is used for next.
566 - $this->db_response = null;
567 438 if (is_null($values)) {
568 439 $preparedQuery = $sql;
569 440 } else {
570 - $invalid = $this->findUnbindableValue((array) $values);
571 - if (null !== $invalid) {
572 - Log::debug_log([
573 - 'message' => 'Model::execute() received an unbindable condition value',
574 - 'table' => $this->table_name,
575 - 'type' => $invalid,
576 - 'sql' => $sql,
577 - ]);
578 - $this->db_response = new WP_Error('invalid_query_value', 'Query value must be scalar, ' . $invalid . ' given');
579 -
580 - return $this;
581 - }
582 441 $preparedQuery = $this->app_db->prepare($sql, $values);
583 442 }
584 443 // echo " Q S " . $preparedQuery . " Q EE";
585 444 if (empty($preparedQuery)) {
586 - $this->db_response = new WP_Error('null_query', 'prepared query is empty');
445 + $this->db_response = new WP_Error('null_query', __('prepared query is empty', 'bit-form'));
587 446 } else {
588 447 $this->db_response = false !== stripos($preparedQuery, 'DELETE') ? $this->app_db->query($preparedQuery)
589 448 : $this->app_db->get_results($preparedQuery, OBJECT_K);
590 449 }
@@ -593,21 +452,9 @@
593 452 }
594 453
595 454 protected function getResult($db_response = null)
596 455 {
597 - // The caller's own result wins. $db_response is an instance property that only execute()
598 - // writes, and models are reused (AdminFormHandler keeps a static FormModel for the whole
599 - // request), so letting the property override an explicitly passed result made insert() and
600 - // update() report the outcome of some earlier, unrelated query on the same object.
601 - // Without this fallback, execute()->getResult() (which passes no argument) never sees the
602 - // query it just ran and every read returns 'result_empty'.
603 - if (null === $db_response) {
604 - $db_response = $this->db_response;
605 - }
606 -
607 - if (is_wp_error($db_response)) {
608 - return $db_response;
609 - }
456 + $db_response = !empty($this->db_response) ? $this->db_response : $db_response;
610 457 if (!empty($this->app_db->last_error)) {
611 458 return new WP_Error('db_error', $this->app_db->last_error);
612 459 }
613 460 if (!$db_response) {
@@ -616,9 +463,9 @@
616 463 }
617 464 if (is_wp_error($db_response)) {
618 465 $response = $db_response;
619 466 }
620 - $response = new WP_Error('result_empty', 'Result is empty');
467 + $response = new WP_Error('result_empty', __('Result is empty', 'bit-form'));
621 468 } elseif (is_array($this->app_db->last_result) && !empty($this->app_db->last_result)) {
622 469 $response = $this->app_db->last_result;
623 470 } elseif ($this->app_db->insert_id) {
624 471 $response = $this->app_db->insert_id;
@@ -628,20 +475,13 @@
628 475 $this->app_db->flush();
629 476 return $response;
630 477 }
631 478
632 - /**
633 - * Get last inserted id
634 - *
635 - * @return int
636 - */
479 + // get last entry id
637 480 public function lastId()
638 481 {
639 - $sql = "SELECT id FROM `{$this->table_name}`
482 + $sql = "SELECT id FROM {$this->table_name}
640 483 ORDER BY id DESC LIMIT 1";
641 - $result = $this->execute($sql)->getResult();
642 - if (is_wp_error($result)) {
643 - return 0;
644 - }
645 - return $result[0]->id;
484 + $result = $this->execute($sql)->getResult()[0];
485 + return $result->id;
646 486 }
647 487 }