$extensions Plugins und Themes * @param array{transport?: ?string, host?: ?string, sender?: ?string} $mailer * @param list}> $sites Seiten des Netzwerks, sonst die eine Website * @param ?string $source erster Hostname der Installation (DomainCollector::hostnames, sortiert) * @param array{php?: array, database?: array, server?: array, settings?: array} $environment Umgebung (Environment, Schalter der Plattform) * @return array */ public static function build( ?string $wordpressVersion, ?string $phpVersion, array $extensions, array $mailer, array $sites, bool $maintenance, ?string $source = null, array $environment = [], ): array { $payload = [ 'platform' => 'wordpress', 'platform_version' => self::version($wordpressVersion), 'php_version' => self::version($phpVersion), 'maintenance' => $maintenance, 'extensions' => self::extensions($extensions), 'mailer' => array_filter([ 'transport' => self::text($mailer['transport'] ?? null, 32), 'host' => self::text($mailer['host'] ?? null, 253), 'sender' => self::text($mailer['sender'] ?? null, 190), ], static fn (?string $value) => $value !== null), 'channels' => array_values(array_map(static fn (array $site) => array_filter([ 'name' => mb_substr($site['name'], 0, 190), 'domains' => array_values(array_slice($site['domains'] ?? [], 0, 50)), ], static fn ($value) => $value !== [] && $value !== ''), array_slice($sites, 0, self::MAX_CHANNELS))), 'agent' => 'wordpress', 'agent_version' => self::AGENT_VERSION, ]; $environment = self::environment($environment); if ($environment !== []) { $payload['environment'] = $environment; } if ($source !== null && $source !== '') { // welche Installation meldet: Zwei Systeme unter derselben Domain haben in DomainWarn je eine Innensicht $payload['source'] = strtolower($source); } return $payload; } /** * WordPress nennt Versionen auch „6.7-beta2“ oder „6.7.1-RC1“; die Meldung trägt die Zahlenfolge. */ private static function version(?string $raw): ?string { if ($raw === null) { return null; } $raw = ltrim(trim($raw), 'vV'); return preg_match('/^\d+(?:\.\d+){0,3}/', $raw, $m) === 1 ? $m[0] : null; } /** * @param list> $extensions * @return list> */ private static function extensions(array $extensions): array { $out = []; foreach ($extensions as $extension) { $name = self::text($extension['name'] ?? null, 190); if ($name === null) { continue; } $out[] = array_filter([ 'name' => $name, 'label' => self::text($extension['label'] ?? null, 190), 'version' => self::version(is_string($extension['version'] ?? null) ? $extension['version'] : null), // WordPress kennt die verfügbare Version aus den Update-Transients; ohne Update bleibt das Feld leer 'latest_version' => self::version(is_string($extension['latest_version'] ?? null) ? $extension['latest_version'] : null), 'active' => $extension['active'] ?? null, ], static fn ($value) => $value !== null); } // stabile Reihenfolge: die Meldung soll sich nicht allein durch die Reihenfolge der Dateien ändern usort($out, static fn (array $a, array $b) => strcmp((string) $a['name'], (string) $b['name'])); // lieber die ersten 300 melden als eine Meldung, die die API insgesamt ablehnt return array_slice($out, 0, self::MAX_EXTENSIONS); } /** * Die Umgebung in der Form, die die API annimmt: Zahlen als Zahlen, Schalter nur als Wahrheitswert, kleine Zahl oder * kurzes Wort. Was nicht passt, fällt weg, statt die ganze Meldung scheitern zu lassen. * * @param array $raw * @return array */ public static function environment(array $raw): array { $out = []; $php = is_array($raw['php'] ?? null) ? $raw['php'] : []; $int = static fn (mixed $v): ?int => is_int($v) ? $v : null; $section = array_filter([ 'sapi' => is_string($php['sapi'] ?? null) && preg_match('/^[a-z0-9-]{1,32}$/', $php['sapi']) === 1 ? $php['sapi'] : null, 'memory_limit' => $int($php['memory_limit'] ?? null), 'max_execution_time' => $int($php['max_execution_time'] ?? null), 'upload_max_filesize' => $int($php['upload_max_filesize'] ?? null), 'opcache' => is_bool($php['opcache'] ?? null) ? $php['opcache'] : null, 'extensions' => is_array($php['extensions'] ?? null) ? Environment::extensions(array_values(array_filter($php['extensions'], is_string(...)))) : null, ], static fn ($v) => $v !== null); if ($section !== []) { $out['php'] = $section; } foreach (['database' => ['type', 'version'], 'server' => ['software', 'version']] as $key => $fields) { $values = is_array($raw[$key] ?? null) ? $raw[$key] : []; $section = []; foreach ($fields as $field) { $value = $values[$field] ?? null; if (is_string($value) && preg_match($field === 'version' ? '/^\d+(?:\.\d+){0,3}$/' : '/^[a-z0-9_.-]{1,32}$/i', $value) === 1) { $section[$field] = $value; } } if ($section !== []) { $out[$key] = $section; } } $settings = []; foreach (is_array($raw['settings'] ?? null) ? $raw['settings'] : [] as $name => $value) { if (! is_string($name) || preg_match('/^[a-z_]{1,40}$/', $name) !== 1) { continue; } if (is_bool($value) || (is_int($value) && $value >= 0 && $value <= 100000) || (is_string($value) && preg_match('/^[a-z]{1,16}$/', $value) === 1)) { $settings[$name] = $value; } } if ($settings !== []) { $out['settings'] = array_slice($settings, 0, 40, true); } return $out; } private static function text(mixed $value, int $max): ?string { if (! is_string($value)) { return null; } $value = trim($value); return $value === '' ? null : mb_substr($value, 0, $max); } }