*/ public static function eventsIn($body): array { $raw = is_array($body) && is_array($body[self::EVENTS] ?? null) ? $body[self::EVENTS] : []; $events = []; $examined = 0; foreach ($raw as $candidate) { if (++$examined > self::MAX_EVENTS) { break; } $event = self::event($candidate); if ($event !== null && !isset($events[$event->key()])) { $events[$event->key()] = $event; } } return array_values($events); } /** * One entry of the batch, or null where it is not one. * * A malformed entry is skipped rather than fatal to the batch: the request * is a fire-and-forget `sendBeacon` with nobody left on the page to see a * response, so refusing the whole flush over one bad row would silently * lose the good ones. * * @param mixed $candidate */ private static function event($candidate): ?BeaconEvent { if (!is_array($candidate)) { return null; } $optinId = $candidate['optin_id'] ?? null; $kind = $candidate['kind'] ?? null; // `Ulid::isOne()` is the ONE spelling of "this is a ULID" — the // alphabet, the length, and the anchoring all in one place. Three // callers had three copies of that pattern before it existed, and a // fourth here would be a shape check that drifts from the ids the // generator actually mints. if (!is_string($optinId) || !Ulid::isOne($optinId) || !is_string($kind)) { return null; } $stat = StatKind::fromBeacon($kind); $scope = $candidate['scope'] ?? ''; if (!is_string($scope) || strlen($scope) > 160) { return null; } return $stat === null ? null : new BeaconEvent($optinId, $stat, $scope); } }