diff --git a/src/BunnyCDNAdapter.php b/src/BunnyCDNAdapter.php index 0cd10d1..7fe95a6 100644 --- a/src/BunnyCDNAdapter.php +++ b/src/BunnyCDNAdapter.php @@ -3,6 +3,8 @@ namespace PlatformCommunity\Flysystem\BunnyCDN; use Exception; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Pool; use League\Flysystem\CalculateChecksumFromStream; use League\Flysystem\ChecksumProvider; use League\Flysystem\Config; @@ -35,27 +37,8 @@ class BunnyCDNAdapter implements FilesystemAdapter, PublicUrlGenerator, Checksum { use CalculateChecksumFromStream; - /** - * Pull Zone URL - * - * @var string - */ - private string $pullzone_url; - - /** - * @var BunnyCDNClient - */ - private BunnyCDNClient $client; - - /** - * @param BunnyCDNClient $client - * @param string $pullzone_url - */ - public function __construct(BunnyCDNClient $client, string $pullzone_url = '') + public function __construct(private BunnyCDNClient $client, private string $pullzone_url = '') { - $this->client = $client; - $this->pullzone_url = $pullzone_url; - if (\func_num_args() > 2 && (string) \func_get_arg(2) !== '') { throw new \RuntimeException('PrefixPath is no longer supported directly. Use PathPrefixedAdapter instead: https://flysystem.thephpleague.com/docs/adapter/path-prefixing/'); } @@ -70,12 +53,9 @@ public function __construct(BunnyCDNClient $client, string $pullzone_url = '') public function copy($source, $destination, Config $config): void { try { - /** @var array $files */ - $files = iterator_to_array($this->getFiles($source)); - $sourceLength = \strlen($source); - foreach ($files as $file) { + foreach ($this->getFiles($source) as $file) { $this->copyFile($file, $destination.\substr($file, $sourceLength), $config); } } catch (UnableToReadFile|UnableToWriteFile $exception) { @@ -225,6 +205,34 @@ public function writeStream($path, $contents, Config $config): void $this->write($path, stream_get_contents($contents), $config); } + /** + * @param WriteBatchFile[] $writeBatches + * @param Config $config + * @return void + */ + public function writeBatch(array $writeBatches, Config $config): void + { + $concurrency = (int) $config->get('concurrency', 50); + + foreach (\array_chunk($writeBatches, $concurrency) as $batch) { + $requests = function () use ($batch) { + /** @var WriteBatchFile $file */ + foreach ($batch as $file) { + yield $this->client->getUploadRequest($file->targetPath, \file_get_contents($file->localPath)); + } + }; + + $pool = new Pool($this->client->client, $requests(), [ + 'concurrency' => $concurrency, + 'rejected' => function (RequestException $reason, int $index) { + throw UnableToWriteFile::atLocation($index, $reason->getMessage()); + }, + ]); + + $pool->promise()->wait(); + } + } + /** * @param $path * @return resource diff --git a/src/BunnyCDNClient.php b/src/BunnyCDNClient.php index bf461b9..a429236 100644 --- a/src/BunnyCDNClient.php +++ b/src/BunnyCDNClient.php @@ -11,20 +11,13 @@ class BunnyCDNClient { - public string $storage_zone_name; - - private string $api_key; - - private string $region; - public Guzzle $client; - public function __construct(string $storage_zone_name, string $api_key, string $region = BunnyCDNRegion::FALKENSTEIN) - { - $this->storage_zone_name = $storage_zone_name; - $this->api_key = $api_key; - $this->region = $region; - + public function __construct( + public string $storage_zone_name, + private string $api_key, + private string $region = BunnyCDNRegion::FALKENSTEIN + ) { $this->client = new Guzzle(); } @@ -43,27 +36,25 @@ private static function get_base_url($region): string }; } - public function createRequest(string $path, string $method = 'GET', array $options = []): Request + public function createRequest(string $path, string $method = 'GET', array $headers = [], $body = null): Request { - return new Request($method, + return new Request( + $method, self::get_base_url($this->region).Util::normalizePath('/'.$this->storage_zone_name.'/').$path, - array_merge_recursive([ - 'headers' => [ - 'Accept' => '*/*', - 'AccessKey' => $this->api_key, // Honestly... Why do I have to specify this twice... @BunnyCDN - ], - ], $options) + array_merge([ + 'Accept' => '*/*', + 'AccessKey' => $this->api_key, + ], $headers), + $body ); } /** * @throws ClientExceptionInterface */ - private function request(string $path, string $method = 'GET', array $options = []): mixed + private function request(Request $request, array $options = []): mixed { - $request = $this->createRequest($path, $method, $options); - - $contents = $this->client->sendRequest($request)->getBody()->getContents(); + $contents = $this->client->send($request, $options)->getBody()->getContents(); return json_decode($contents, true) ?? $contents; } @@ -77,7 +68,7 @@ private function request(string $path, string $method = 'GET', array $options = public function list(string $path): array { try { - $listing = $this->request(Util::normalizePath($path).'/'); + $listing = $this->request($this->createRequest(Util::normalizePath($path).'/')); // Throw an exception if we don't get back an array if (! is_array($listing)) { @@ -107,7 +98,7 @@ public function list(string $path): array public function download(string $path): string { try { - $content = $this->request($path.'?download'); + $content = $this->request($this->createRequest($path.'?download')); if (\is_array($content)) { return \json_encode($content); @@ -134,7 +125,7 @@ public function download(string $path): string public function stream(string $path) { try { - return $this->createRequest($path, 'GET', ['stream' => true])->getBody()->detach(); + return $this->request($this->createRequest($path), ['stream' => true])->getBody()->detach(); // @codeCoverageIgnoreStart } catch (GuzzleException $e) { throw match ($e->getCode()) { @@ -145,6 +136,18 @@ public function stream(string $path) // @codeCoverageIgnoreEnd } + public function getUploadRequest(string $path, $contents): Request + { + return $this->createRequest( + $path, + 'PUT', + [ + 'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8', + ], + $contents + ); + } + /** * @param string $path * @param $contents @@ -155,12 +158,7 @@ public function stream(string $path) public function upload(string $path, $contents): mixed { try { - return $this->request($path, 'PUT', [ - 'headers' => [ - 'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8', - ], - 'body' => $contents, - ]); + return $this->request($this->getUploadRequest($path, $contents)); // @codeCoverageIgnoreStart } catch (GuzzleException $e) { throw new BunnyCDNException($e->getMessage()); @@ -177,11 +175,9 @@ public function upload(string $path, $contents): mixed public function make_directory(string $path): mixed { try { - return $this->request(Util::normalizePath($path).'/', 'PUT', [ - 'headers' => [ - 'Content-Length' => 0, - ], - ]); + return $this->request($this->createRequest(Util::normalizePath($path).'/', 'PUT', [ + 'Content-Length' => 0, + ])); // @codeCoverageIgnoreStart } catch (GuzzleException $e) { throw match ($e->getCode()) { @@ -202,7 +198,7 @@ public function make_directory(string $path): mixed public function delete(string $path): mixed { try { - return $this->request($path, 'DELETE'); + return $this->request($this->createRequest($path, 'DELETE')); // @codeCoverageIgnoreStart } catch (GuzzleException $e) { throw match ($e->getCode()) { diff --git a/src/WriteBatchFile.php b/src/WriteBatchFile.php new file mode 100644 index 0000000..7462af7 --- /dev/null +++ b/src/WriteBatchFile.php @@ -0,0 +1,12 @@ +