client = $client; $this->tempUrlGenerator = $tempUrlGenerator; } public function getLastModified(StoredObject $document): DateTimeInterface { if ($this->hasCache($document)) { $response = $this->getResponseFromCache($document); } else { try { $response = $this ->client ->request( Request::METHOD_HEAD, $this ->tempUrlGenerator ->generate( Request::METHOD_HEAD, $document->getFilename() ) ->url ); } catch (TransportExceptionInterface $exception) { throw StoredObjectManagerException::errorDuringHttpRequest($exception); } } return $this->extractLastModifiedFromResponse($response); } public function read(StoredObject $document): string { $response = $this->getResponseFromCache($document); try { $data = $response->getContent(); } catch (Throwable $e) { throw StoredObjectManagerException::unableToGetResponseContent($e); } if (false === $this->hasKeysAndIv($document)) { return $data; } $clearData = openssl_decrypt( $data, self::ALGORITHM, // TODO: Why using this library and not use base64_decode() ? Base64Url::decode($document->getKeyInfos()['k']), OPENSSL_RAW_DATA, pack('C*', ...$document->getIv()) ); if (false === $clearData) { throw StoredObjectManagerException::unableToDecrypt(openssl_error_string()); } return $clearData; } public function write(StoredObject $document, string $clearContent): void { if ($this->hasCache($document)) { unset($this->inMemory[$document->getUuid()->toString()]); } $encryptedContent = $this->hasKeysAndIv($document) ? openssl_encrypt( $clearContent, self::ALGORITHM, // TODO: Why using this library and not use base64_decode() ? Base64Url::decode($document->getKeyInfos()['k']), OPENSSL_RAW_DATA, pack('C*', ...$document->getIv()) ) : $clearContent; try { $response = $this ->client ->request( Request::METHOD_PUT, $this ->tempUrlGenerator ->generate( Request::METHOD_PUT, $document->getFilename() ) ->url, [ 'body' => $encryptedContent, ] ); } catch (TransportExceptionInterface $exception) { throw StoredObjectManagerException::errorDuringHttpRequest($exception); } if ($response->getStatusCode() !== Response::HTTP_CREATED) { throw StoredObjectManagerException::invalidStatusCode($response->getStatusCode()); } } private function extractLastModifiedFromResponse(ResponseInterface $response): DateTimeImmutable { $lastModifiedString = (($response->getHeaders()['last-modified'] ?? [])[0] ?? ''); $date = DateTimeImmutable::createFromFormat( DateTimeImmutable::RFC7231, $lastModifiedString, new DateTimeZone('GMT') ); if (false === $date) { throw new RuntimeException('the date from remote storage could not be parsed: ' . $lastModifiedString); } return $date; } private function fillCache(StoredObject $document): void { try { $response = $this ->client ->request( Request::METHOD_GET, $this ->tempUrlGenerator ->generate( Request::METHOD_GET, $document->getFilename() ) ->url ); } catch (Throwable $e) { throw StoredObjectManagerException::errorDuringHttpRequest($e); } if ($response->getStatusCode() !== Response::HTTP_OK) { throw StoredObjectManagerException::invalidStatusCode($response->getStatusCode()); } $this->inMemory[$document->getUuid()->toString()] = $response; } private function getResponseFromCache(StoredObject $document): ResponseInterface { if (!$this->hasCache($document)) { $this->fillCache($document); } return $this->inMemory[$document->getUuid()->toString()]; } private function hasCache(StoredObject $document): bool { return array_key_exists($document->getUuid()->toString(), $this->inMemory); } private function hasKeysAndIv(StoredObject $storedObject): bool { return ([] !== $storedObject->getKeyInfos()) && ([] !== $storedObject->getIv()); } }