$config * @param array $context */ public function resolve(array $config, array $context): string { /** @var array> $rawRules */ $rawRules = $config['rules'] ?? []; $rules = collect($rawRules)->sortBy('priority'); foreach ($rules as $rule) { if ($this->ruleMatches($rule, $context)) { return $rule['target']; } } return $config['fallback'] ?? ''; } /** * @param array $rule * @param array $context */ private function ruleMatches(array $rule, array $context): bool { $conditions = $rule['conditions'] ?? []; $matchMode = $rule['match'] ?? 'all'; if (empty($conditions)) { return false; } $results = array_map( fn ($condition) => $this->evaluateCondition($condition, $context), $conditions ); return $matchMode === 'all' ? ! in_array(false, $results, true) : in_array(true, $results, true); } /** * @param array $condition * @param array $context */ private function evaluateCondition(array $condition, array $context): bool { if (! isset($condition['field'], $condition['op'], $condition['value'])) { return false; } $field = $condition['field']; $op = $condition['op']; $expected = $condition['value']; $actual = $context[$field] ?? null; return match ($op) { 'equals' => $actual === $expected, 'not_equals' => $actual !== $expected, 'in' => in_array($actual, (array) $expected, true), 'not_in' => ! in_array($actual, (array) $expected, true), 'contains' => str_contains((string) $actual, (string) $expected), 'starts_with' => str_starts_with((string) $actual, (string) $expected), 'between' => is_array($expected) && count($expected) === 2 && $actual >= $expected[0] && $actual <= $expected[1], 'greater_than' => $actual > $expected, 'less_than' => $actual < $expected, default => false, }; } }