write($token, $state); return $token; } public function get(string $token): ?array { $path = $this->path($token); if (!is_file($path)) { return null; } $contents = file_get_contents($path); if ($contents === false || $contents === '') { return null; } $state = json_decode($contents, true); if (!is_array($state)) { throw new RuntimeException('The import job state is corrupt.'); } return $state; } public function save(string $token, array $state): void { $state['token'] = $token; $this->write($token, $state); } public function delete(string $token): void { $path = $this->path($token); if (is_file($path)) { @unlink($path); } } public function pathForToken(string $token): string { return $this->path($token); } private function write(string $token, array $state): void { $path = $this->path($token); $dir = dirname($path); if (!is_dir($dir) && !mkdir($dir, 0777, true) && !is_dir($dir)) { throw new RuntimeException('Unable to create the import job storage directory.'); } $json = json_encode($state, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $tmpPath = $path . '.' . bin2hex(random_bytes(6)) . '.tmp'; if (file_put_contents($tmpPath, $json) === false) { throw new RuntimeException('Unable to write the import job state.'); } if (!@rename($tmpPath, $path)) { @unlink($tmpPath); throw new RuntimeException('Unable to persist the import job state.'); } } private function path(string $token): string { $safeToken = preg_replace('/[^a-zA-Z0-9_-]/', '', $token); if ($safeToken === '') { throw new RuntimeException('Invalid import job token.'); } $storageDir = $this->storageDir !== '' ? $this->storageDir : sys_get_temp_dir() . '/warner-import-jobs'; return rtrim($storageDir, '/\\') . '/' . $safeToken . '.json'; } }