PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.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 All 138 releases
← All changes | includes/Core/WorkFlow/Helper.php +87 -24 3.2.03.3.1 View file →
@@ -88,9 +88,16 @@
88 88 || isset($conditionalActionIds['successMsg'][(string) $msg->id])
89 89 ) {
90 90 continue;
91 91 }
92 - $returnableData = self::replaceFieldWithValue($msg->message_content, $fieldValue, true, null, true);
92 + // Translate before smart-tag replacement (identity when unhooked).
93 + $messageContent = (string) apply_filters(
94 + 'bitform_translate_form_string',
95 + (string) $msg->message_content,
96 + 'msg-content-' . $msg->id,
97 + $formId
98 + );
99 + $returnableData = self::replaceFieldWithValue($messageContent, $fieldValue, true, null, true);
93 100 $messageId = $msg->id;
94 101 if (isset($msgConfig->afterSubmit)) {
95 102 $afterSubmit = $msgConfig->afterSubmit;
96 103 }
@@ -109,8 +116,15 @@
109 116 continue;
110 117 }
111 118 $url = Utilities::jsonObj($redirectPage->integration_details ?? '')->url ?? '';
112 119 if (!empty($url)) {
120 + // Translated before smart-tag replacement: per-language redirect targets.
121 + $url = (string) apply_filters(
122 + 'bitform_translate_form_string',
123 + (string) $url,
124 + 'redirect-url-' . $redirectPage->id,
125 + $formId
126 + );
113 127 $url = self::replaceFieldWithValue($url, $fieldValue);
114 128 }
115 129 $returnableData = empty($url) ? '' : esc_url_raw($url);
116 130 break;
@@ -304,22 +318,15 @@
304 318 }
305 319 return $ids;
306 320 }
307 321
322 + /**
323 + * @deprecated misspelled duplicate of calculate(); kept because it is public API. Same
324 + * divide-by-zero guard so an external caller cannot fatal either.
325 + */
308 326 public static function calculte($firstOperand, $secondOperand, $operator)
309 327 {
310 - switch ($operator) {
311 - case '+':
312 - return $firstOperand + $secondOperand;
313 - case '-':
314 - return $firstOperand - $secondOperand;
315 - case '*':
316 - return $firstOperand * $secondOperand;
317 - case '/':
318 - return $firstOperand / $secondOperand;
319 - case '^':
320 - return $firstOperand ** $secondOperand;
321 - }
328 + return self::calculate($firstOperand, $secondOperand, $operator);
322 329 }
323 330
324 331 public static function filterMailContentType()
325 332 {
@@ -328,11 +335,26 @@
328 335
329 336 public static function evalMathExpression($stringWithFieldValue)
330 337 {
331 338 $mathExpr = $stringWithFieldValue;
332 - if (empty($mathExpr)) {
339 + if (empty($mathExpr) || !\is_scalar($mathExpr)) {
333 340 return $stringWithFieldValue;
334 341 }
342 + $mathExpr = (string) $mathExpr;
343 +
344 + // The operand/operator checks below only look at \w+ runs and operator runs, so any other
345 + // character was invisible to them. A quoted date ('2020-10-10') therefore passed as a
346 + // subtraction chain and its quotes later surfaced as a bogus operator token. Require the whole
347 + // string to be made of things a formula can contain.
348 + if (1 !== preg_match('#^[0-9.+\-*/^()\[\]{}\s]+$#', $mathExpr)) {
349 + return $stringWithFieldValue;
350 + }
351 +
352 + // A bare date is not a subtraction: 2020-10-10 must stay a date, not become 2000.
353 + if (1 === preg_match('/^\s*\d{4}-\d{1,2}-\d{1,2}\s*$/', $mathExpr)) {
354 + return $stringWithFieldValue;
355 + }
356 +
335 357 preg_match_all('/[\+\-\*\/\s]+/', $mathExpr, $isMathExpr);
336 358 if (empty($isMathExpr[0])) {
337 359 return $stringWithFieldValue;
338 360 }
@@ -354,9 +376,9 @@
354 376 $mathExpr = str_replace(' ', '', $mathExpr);
355 377 $mathExpr = preg_replace('/\{|\[|\(/', '(', $mathExpr);
356 378 $mathExpr = preg_replace('/\}|\]/', ')', $mathExpr);
357 379 $calculated = self::infixToPostfixEvalute($mathExpr);
358 - if (!is_null($calculated)) {
380 + if (!is_null($calculated) && isset($calculated[0])) {
359 381 return (string) $calculated[0];
360 382 }
361 383
362 384 return (string) $stringWithFieldValue;
@@ -380,10 +402,18 @@
380 402 }
381 403 if ('(' === $token) {
382 404 $operatorStack[] = $token;
383 405 } elseif (')' === $token) {
406 + // An unbalanced ')' used to read $operatorStack[-1] and warn on every iteration; treat
407 + // the expression as non-arithmetic instead.
408 + if (empty($operatorStack)) {
409 + return null;
410 + }
384 411 while ('(' !== $operatorStack[count($operatorStack) - 1]) {
385 412 $outputQueue[] = array_pop($operatorStack);
413 + if (empty($operatorStack)) {
414 + return null;
415 + }
386 416 if ('(' === $operatorStack[count($operatorStack) - 1]) {
387 417 array_pop($operatorStack);
388 418 break;
389 419 }
@@ -412,11 +442,22 @@
412 442 if (is_numeric($value)) {
413 443 $resultStack[] = $value;
414 444 continue;
415 445 }
446 + // A token that is neither a number nor a real operator means the input was never an
447 + // expression — e.g. a quoted date whose quotes accumulated into a token like "'2020".
448 + // Bail out so evalMathExpression() returns the caller's string untouched; pushing the
449 + // failure onto the stack would end up blanking that string.
450 + if (!\in_array($value, ['+', '-', '*', '/', '^'], true) || count($resultStack) < 2) {
451 + return null;
452 + }
416 453 $secondOperand = array_pop($resultStack);
417 454 $firstOperand = array_pop($resultStack);
418 - $resultStack[] = self::calculate($firstOperand, $secondOperand, $value);
455 + $calculated = self::calculate($firstOperand, $secondOperand, $value);
456 + if (\is_null($calculated)) {
457 + return null;
458 + }
459 + $resultStack[] = $calculated;
419 460 }
420 461 return $resultStack;
421 462 }
422 463
@@ -432,19 +473,41 @@
432 473
433 474 return isset($precedence[$operator]) ? $precedence[$operator] : 0;
434 475 }
435 476
477 + /**
478 + * Apply one arithmetic operator.
479 + *
480 + * @param mixed $firstOperand
481 + * @param mixed $secondOperand
482 + * @param string $operator
483 + *
484 + * @return float|int|null null on unknown operator, non-numeric operand, or division by zero
485 + */
436 486 public static function calculate($firstOperand, $secondOperand, $operator)
437 487 {
438 - $calculated = [
439 - '+' => $firstOperand + $secondOperand,
440 - '-' => $firstOperand - $secondOperand,
441 - '*' => $firstOperand * $secondOperand,
442 - '/' => $firstOperand / $secondOperand,
443 - '^' => $firstOperand ** $secondOperand,
444 - ];
488 + if (!is_numeric($firstOperand) || !is_numeric($secondOperand)) {
489 + return null;
490 + }
491 + $firstOperand = $firstOperand + 0;
492 + $secondOperand = $secondOperand + 0;
445 493
446 - return isset($calculated[$operator]) ? $calculated[$operator] : '';
494 + switch ($operator) {
495 + case '+':
496 + return $firstOperand + $secondOperand;
497 + case '-':
498 + return $firstOperand - $secondOperand;
499 + case '*':
500 + return $firstOperand * $secondOperand;
501 + case '/':
502 + // Compare as float, not with `0 ==`: PHP 8 made `0 == ''` false, so a
503 + // non-numeric operand slipped past the old guard into DivisionByZeroError.
504 + return 0.0 === (float) $secondOperand ? null : $firstOperand / $secondOperand;
505 + case '^':
506 + return $firstOperand ** $secondOperand;
507 + }
508 +
509 + return null;
447 510 }
448 511
449 512 /**
450 513 * Recursively sets a nested property in a given object.