bug: #3629970 Token default values like [node:field_x]{100} break the expression when the token is resolved

Closes #3629970

Problem

The cleanup that removes the {...} default value wrappers runs inside the loop over the tokens that are still unresolved after token replacement. This causes two bugs:

  1. Resolved tokens keep their wrapper. When every token resolves, the loop never runs, so [node:field_price]{100} * 2 becomes 1234.50{100} * 2. EvalMath cannot parse it, and the field is saved as 0 (2.x types) or empty (legacy field_expression type).
  2. Only the first unresolved default is applied. After the first token's default is applied, the cleanup inside the same iteration also removes the wrappers of the tokens that have not been processed yet. In [node:field_a]{5} + [node:field_b]{7} with both fields empty, {7} is removed before field_b is reached, so the result is 5 instead of 12.

Changes

The wrapper cleanup is moved out of the loop in both ExpressionItemTrait::evaluateExpression() and ExpressionItem::evaluateExpression(), so it runs once, after all default values have been applied. Curly braces have no meaning in EvalMath, so removing them unconditionally cannot break a valid expression.

Testing

Tested on Drupal 11.4 with Token 8.x-1.17, saving nodes with field_price empty and set to 1234.5:

Expression Price Before After
[node:field_price]{100} * 2 empty 200 200
[node:field_price]{100} * 2 1234.5 0 2469
[node:field_price:value]{100} * 2 1234.5 0 2469
[node:field_price]{100} * 2 + [node:field_missing]{0} 1234.5 2469 2469
[node:field_price] * 2 1234.5 2469 2469
[node:field_a]{5} + [node:field_b]{7} (both empty) any 5 12
[node:field_price]{100} * 2 (legacy type) 1234.5 empty 2469

PHPCS (Drupal, DrupalPractice) passes on the changed files.

Merge request reports

Loading
Loading