*/ private $settings; /** @var array */ private $data; /** @var float */ private $started_at; /** @var array */ private $memo = []; /** * @param array $settings * @param array $data */ public function __construct(int $run_id, string $run_type, array $settings, array $data) { $this->run_id = $run_id; $this->run_type = $run_type; $this->settings = $settings; $this->data = $data; $this->started_at = microtime(true); } public function run_id(): int { return $this->run_id; } public function run_type(): string { return $this->run_type; } /** * @return array */ public function settings(): array { return $this->settings; } /** * @return array */ public function data(): array { return $this->data; } /** * @param string $key * @param mixed $default * @return mixed */ public function get(string $key, $default = null) { return $this->data[$key] ?? $default; } /** * 4.0.18: memoize a live probe result for this run. The audit renders * findings twice (once per shipped language); the second pass must reuse * the first pass's network results instead of probing again, so both * languages describe the same observations and the scan is not slower. * * @param callable():mixed $producer * @return mixed */ public function memo(string $key, callable $producer) { if (!array_key_exists($key, $this->memo)) { $this->memo[$key] = $producer(); } return $this->memo[$key]; } public function elapsed_ms(): int { return (int) ((microtime(true) - $this->started_at) * 1000); } /** * Build evidence snippet according to settings. */ public function maybe_snippet(string $text, int $max = 1200): string { $ds = $this->settings['data_sharing'] ?? []; $allow = !empty($ds['allow_snippets']); if (!$allow) { return ''; } $max_chars = (int) ($ds['max_snippet_chars'] ?? $max); $max_chars = max(200, min(1200, $max_chars)); return Redactor::redact($text, $max_chars); } }