boundedPrefix = new PathPrefixer($root); } private function guard(int $bytes = 0): void { if ($bytes > $this->maxBytes) { throw new \RuntimeException('Remote response exceeds verification size.'); }if (microtime(true) >= $this->deadline) { throw new \RuntimeException('Remote transfer budget expired.'); } } private function connection(): SFTP { $this->guard(); $connection = $this->boundedProvider->provideConnection(); $this->guard(); $connection->setTimeout(min(20, max(1, $this->deadline - microtime(true)))); return $connection; } public function readStream(string $path) { $connection = $this->connection(); $stream = fopen('php://temp', 'w+b'); $bytes = 0; try { $ok = $connection->get($this->boundedPrefix->prefixPath($path), function (string $chunk) use ($stream, &$bytes): void { $bytes += strlen($chunk); $this->guard($bytes); if (fwrite($stream, $chunk) !== strlen($chunk)) { throw new \RuntimeException('Could not verify remote data.'); } }, 0, $this->maxBytes + 1); $this->guard($bytes); if ($ok === false) { throw new \RuntimeException('Could not verify remote data.'); }rewind($stream); return $stream; } catch (\Throwable $e) { fclose($stream); throw $e; } } public function read(string $path): string { $stream = $this->readStream($path); try { return stream_get_contents($stream); } finally { fclose($stream); } } public function write(string $path, string $contents, Config $config): void { $stream = fopen('php://temp', 'w+b'); try { fwrite($stream, $contents); rewind($stream); $this->writeStream($path, $stream, $config); } finally { fclose($stream); } } public function writeStream(string $path, $contents, Config $config): void { $connection = $this->connection(); $this->createDirectory(dirname($path), new Config(['visibility' => 'private'])); $this->guard(); $ok = $connection->put($this->boundedPrefix->prefixPath($path), $contents, SFTP::SOURCE_STRING, -1, -1, fn ($bytes) => $this->guard((int) $bytes)); $this->guard(); if ($ok === false) { throw new \RuntimeException('Could not deliver remote data.'); }$this->connection(); $this->setVisibility($path, 'private'); } }