self::MAX_LENGTH ? mb_substr($name, 0, self::MAX_LENGTH) : $name; } /** * The stored name, or '' when nothing is known about this file – which is the normal case for * everything uploaded before sidecars existed. * * The absence is checked rather than suppressed with `@`: index.php installs an error handler * that turns every warning into an exception, and it does not consult `error_reporting()`, so * `@` does not silence it. A missing sidecar would otherwise fail the whole listing. */ public static function read(string $dir, string $file): string { $path = $dir . '/' . $file . '.json'; if (!is_file($path)) { return ''; } $raw = file_get_contents($path); if ($raw === false) { return ''; } $data = json_decode($raw, true); return is_array($data) && is_string($data['name'] ?? null) ? self::sanitize($data['name']) : ''; } /** Writes the sidecar; an empty name writes nothing and removes nothing. */ public static function write(string $dir, string $file, string $name): void { $clean = self::sanitize($name); if ($clean === '') { return; } $json = json_encode(['name' => $clean], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($json === false) { return; } try { AtomicFile::write($dir . '/' . $file . '.json', $json, AtomicFile::SERVED, 'name file'); } catch (RuntimeException) { // A nameless file is a small loss; failing the upload over it would be worse. } } /** Removing a file that has no sidecar is the normal case, not a failure. */ public static function delete(string $dir, string $file): void { $path = $dir . '/' . $file . '.json'; if (is_file($path)) { unlink($path); } } }