*/ private array $implHash = []; private const DEFAULT_STORAGE = 'EspoUploadDir'; private Factory $factory; /** * @var array * @phpstan-ignore-next-line Used to prevent deleting from memory. */ private $resourceMap = []; public function __construct(Factory $factory) { $this->factory = $factory; } /** * Whether a file exists in a storage. */ public function exists(AttachmentEntity $attachment): bool { $implementation = $this->getImplementation($attachment); return $implementation->exists(self::wrapAttachmentEntity($attachment)); } /** * Get a file size. */ public function getSize(AttachmentEntity $attachment): int { $implementation = $this->getImplementation($attachment); return $implementation->getSize(self::wrapAttachmentEntity($attachment)); } /** * Get file contents. */ public function getContents(AttachmentEntity $attachment): string { $implementation = $this->getImplementation($attachment); return $implementation->getStream(self::wrapAttachmentEntity($attachment))->getContents(); } /** * Get a file contents stream. */ public function getStream(AttachmentEntity $attachment): StreamInterface { $implementation = $this->getImplementation($attachment); return $implementation->getStream(self::wrapAttachmentEntity($attachment)); } /** * Store file contents represented as a stream. */ public function putStream(AttachmentEntity $attachment, StreamInterface $stream): void { $implementation = $this->getImplementation($attachment); $implementation->putStream(self::wrapAttachmentEntity($attachment), $stream); } /** * Store file contents. */ public function putContents(AttachmentEntity $attachment, string $contents): void { $implementation = $this->getImplementation($attachment); $stream = Utils::streamFor($contents); $implementation->putStream(self::wrapAttachmentEntity($attachment), $stream); } /** * Remove a file. */ public function unlink(AttachmentEntity $attachment): void { $implementation = $this->getImplementation($attachment); $implementation->unlink(self::wrapAttachmentEntity($attachment)); } /** * Whether an attachment storage is local. */ public function isLocal(AttachmentEntity $attachment): bool { $implementation = $this->getImplementation($attachment); return $implementation instanceof Local; } /** * Get a local file path. If a file is not stored locally, a temporary file will be created. */ public function getLocalFilePath(AttachmentEntity $attachment): string { $implementation = $this->getImplementation($attachment); if ($implementation instanceof Local) { return $implementation->getLocalFilePath(self::wrapAttachmentEntity($attachment)); } $contents = $this->getContents($attachment); $resource = tmpfile(); if ($resource === false) { throw new RuntimeException("Could not create temp file."); } fwrite($resource, $contents); $path = stream_get_meta_data($resource)['uri']; // To prevent deleting. $this->resourceMap[$path] = $resource; return $path; } private static function wrapAttachmentEntity(AttachmentEntity $attachment): AttachmentEntityWrapper { return new AttachmentEntityWrapper($attachment); } private function getImplementation(AttachmentEntity $attachment): Storage { $storage = $attachment->getStorage(); if (!$storage) { $storage = self::DEFAULT_STORAGE; } if (!array_key_exists($storage, $this->implHash)) { $this->implHash[$storage] = $this->factory->create($storage); } return $this->implHash[$storage]; } }