getResource($event->getDeliveryUri()); try { $this->archive($compiledDelivery); } catch (DeliverArchiveExistingException $e) { common_Logger::i($e->getMessage()); } catch (DeliveryZipException $e) { common_Logger::e($e->getMessage()); } } /** * @param DeliveryRemovedEvent $event * @throws ServiceNotFoundException */ public function catchDeliveryRemoved(DeliveryRemovedEvent $event) { $compiledDelivery = $this->getResource($event->getDeliveryUri()); $this->deleteArchive($compiledDelivery); } /** * @param \core_kernel_classes_Resource $compiledDelivery * @param bool $force * @return string * @throws DeliverArchiveExistingException * @throws DeliveryZipException */ public function archive($compiledDelivery, $force = false) { $fileName = $this->getArchiveFileName($compiledDelivery); if (!$force && $this->getArchiveFileSystem()->has($fileName)) { throw new DeliverArchiveExistingException('Delivery archive already created: ' . $compiledDelivery->getUri()); } $this->generateNewTmpPath($fileName); $localZipName = $this->getLocalZipPathName($fileName); $zip = new \ZipArchive(); if (($errorCode = $zip->open($localZipName, \ZipArchive::CREATE)) !== true) { throw new DeliveryZipException('Cannot open zip archive: ' . $localZipName . ' error code: ' . $errorCode); } $directories = $compiledDelivery->getPropertyValues( $this->getProperty(DeliveryAssemblyService::PROPERTY_DELIVERY_DIRECTORY) ); foreach ($directories as $directoryId) { /** @var tao_models_classes_service_StorageDirectory $directory */ $directory = $this->getServiceLocator()->get(tao_models_classes_service_FileStorage::SERVICE_ID)->getDirectoryById($directoryId); $directories = $directory->getFlyIterator(Directory::ITERATOR_FILE | Directory::ITERATOR_RECURSIVE); /** @var File $item */ foreach ($directories as $item) { $zip->addFromString($item->getFileSystemId() . '/' . $item->getPrefix(), $item->read()); } } $zip = $this->refreshArchiveProcessed($zip); $zip->close(); $fileName = $this->uploadZip($compiledDelivery); $this->deleteTmpFile($localZipName); return $fileName; } /** * @param \core_kernel_classes_Resource $compiledDelivery * @param bool $force * @return string * @throws DeliveryArchiveNotExistingException * @throws ServiceNotFoundException * @throws DeliveryZipException */ public function unArchive($compiledDelivery, $force = false) { $fileName = $this->getArchiveFileName($compiledDelivery); if (!$this->getArchiveFileSystem()->has($fileName)) { throw new DeliveryArchiveNotExistingException('Delivery archive not exist please generate: ' . $compiledDelivery->getUri()); } $this->generateNewTmpPath($fileName); $zipPath = $this->download($compiledDelivery); $zip = new \ZipArchive(); if (($errorCode = $zip->open($zipPath, \ZipArchive::CREATE)) !== true) { throw new DeliveryZipException('Cannot open zip archive: ' . $zipPath . ' error code: ' . $errorCode); } if ($force || !$this->isArchivedProcessed($zip, $fileName)) { $this->copyFromZip($zip); $this->setArchiveProcessed($zip, $fileName); $zip->close(); $fileName = $this->uploadZip($compiledDelivery); } else { $zip->close(); } $this->deleteTmpFile($zipPath); return $fileName; } /** * @param \core_kernel_classes_Resource $compiledDelivery * @return string * @throws ServiceNotFoundException */ public function deleteArchive($compiledDelivery) { $fileName = $this->getArchiveFileName($compiledDelivery); if ($this->getArchiveFileSystem()->has($fileName)) { $this->getArchiveFileSystem()->delete($fileName); } return $fileName; } /** * @inheritdoc */ public function deleteDeliveryData(DeliveryDeleteRequest $request) { return $this->deleteArchive($request->getDeliveryResource()); } /** * @param $zip \ZipArchive * @return bool * @throws ServiceNotFoundException */ private function copyFromZip($zip) { /** @var FileSystemService $fileSystem */ $fileSystem = $this->getServiceLocator()->get(FileSystemService::SERVICE_ID); for ($index = 0; $index < $zip->numFiles; ++$index) { $zipEntryName = $zip->getNameIndex($index); if (!$this->isZipDirectory($zipEntryName)) { $parts = explode('/', $zipEntryName); $bucketDestination = $parts[0]; unset($parts[0]); if (in_array($bucketDestination, ['public', 'private',])) { $entryName = implode('/', $parts); $stream = $zip->getStream($zipEntryName); if (is_resource($stream)) { $fileSystem->getFileSystem($bucketDestination)->putStream($entryName, $stream); } } } } return true; } /** * @param \core_kernel_classes_Resource $compiledDelivery * @return string * @throws ServiceNotFoundException * @throws DeliveryZipException */ private function uploadZip($compiledDelivery) { $fileName = $this->getArchiveFileName($compiledDelivery); $zipPath = $this->getLocalZipPathName($fileName); if (!file_exists($zipPath) || ($stream = fopen($zipPath, 'r')) === false) { throw new DeliveryZipException('Cannot open local tmp zip archive'); } $this->getArchiveFileSystem()->putStream($fileName, $stream); fclose($stream); return $fileName; } /** * @return FileSystem * @throws ServiceNotFoundException */ private function getArchiveFileSystem() { return $this->getServiceLocator()->get(FileSystemService::SERVICE_ID)->getFileSystem(static::BUCKET_DIRECTORY); } /** * @param \core_kernel_classes_Resource $compiledDelivery * @return string * @throws DeliveryZipException */ private function download($compiledDelivery) { $fileName = $this->getArchiveFileName($compiledDelivery); $zipPath = $this->getLocalZipPathName($fileName); $streamDestination = fopen($zipPath, 'w'); $stream = $this->getArchiveFileSystem()->readStream($fileName); if ($stream === false) { throw new DeliveryZipException('Cannot open zip archive'); } if ($streamDestination === false) { throw new DeliveryZipException('Cannot write to local tmp zip archive'); } stream_copy_to_stream($stream, $streamDestination); fclose($stream); fclose($streamDestination); return $zipPath; } /** * @param \core_kernel_classes_Resource $compiledDelivery * @return string */ private function getArchiveFileName($compiledDelivery) { return md5($compiledDelivery->getUri()) . '.zip'; } /** * @param $fileName * @return string */ private function getLocalZipPathName($fileName) { return $this->getTmpPath() . $fileName; } /** * @return mixed */ private function getTmpPath() { return $this->tmpDir; } /** * generate unique tmp folder based on delivery. * @param $fileName */ private function generateNewTmpPath($fileName) { $folder = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "tmp" . md5($fileName . uniqid('', true)) . DIRECTORY_SEPARATOR; if (!file_exists($folder)) { mkdir($folder); } $this->tmpDir = $folder; } /** * @param $tmpZipPath */ private function deleteTmpFile($tmpZipPath) { unlink($tmpZipPath); if (\helpers_File::emptyDirectory($this->tmpDir)) { rmdir($this->tmpDir); } } /** * @param $zipEntryName * @return bool */ private function isZipDirectory($zipEntryName) { return substr($zipEntryName, -1) === '/'; } /** * @param $fileName * @return string */ private function getUniqueProcessedName($fileName) { return md5(gethostname()) . $fileName; } /** * @param \ZipArchive $zip * @param $fileName * @return \ZipArchive */ private function setArchiveProcessed($zip, $fileName) { $stats = json_decode($zip->getArchiveComment(), true); if (is_null($stats)) { $stats = ['processed' => []]; } $stats['processed'][] = $this->getUniqueProcessedName($fileName); $zip->setArchiveComment(json_encode($stats)); return $zip; } /** * @param \ZipArchive $zip * @return \ZipArchive */ private function refreshArchiveProcessed($zip) { $stats = ['processed' => []]; $zip->setArchiveComment(json_encode($stats)); return $zip; } /** * @param \ZipArchive $zip * @return bool */ private function isArchivedProcessed($zip, $fileName) { $stats = json_decode($zip->getArchiveComment(), true); if (is_null($stats) || !isset($stats['processed'])) { $stats = ['processed' => []]; } return in_array($this->getUniqueProcessedName($fileName), $stats['processed']); } }