resolver->resolve(data_get($config, 'field', ''), $run->context ?? []); $operator = Operator::from(data_get($config, 'operator', 'equals')); $value = (string) data_get($config, 'value', ''); $matched = match ($operator) { Operator::Contains => str_contains($field, $value), Operator::NotContains => ! str_contains($field, $value), Operator::Equals => $field === $value, Operator::NotEquals => $field !== $value, Operator::Matches => $this->safeRegexMatch($value, $field), Operator::GreaterThan => is_numeric($field) && is_numeric($value) && (float) $field > (float) $value, Operator::LessThan => is_numeric($field) && is_numeric($value) && (float) $field < (float) $value, }; return NodeRunResult::completed( output: ['condition' => ['resolved_field' => $field, 'matched' => $matched]], nextHandle: $matched ? 'yes' : 'no', ); } private function safeRegexMatch(string $pattern, string $subject): bool { if (strlen($pattern) > self::MAX_REGEX_LENGTH) { return false; } $escaped = str_replace('~', '\~', $pattern); $regex = "~{$escaped}~u"; try { $result = @preg_match($regex, $subject); } catch (\Throwable) { return false; } if ($result === false || preg_last_error() !== PREG_NO_ERROR) { return false; } return $result === 1; } }