* @author Till Klampaeckel */ class ArchiveManager { /** @var DownloadManager */ protected $downloadManager; /** @var Loop */ protected $loop; /** @var ArchiverInterface[] */ protected $archivers = []; /** @var bool */ protected $overwriteFiles = true; /** * @param DownloadManager $downloadManager A manager used to download package sources */ public function __construct(DownloadManager $downloadManager, Loop $loop) { $this->downloadManager = $downloadManager; $this->loop = $loop; } public function addArchiver(ArchiverInterface $archiver): void { $this->archivers[] = $archiver; } /** * Set whether existing archives should be overwritten * * @param bool $overwriteFiles New setting * * @return $this */ public function setOverwriteFiles(bool $overwriteFiles): self { $this->overwriteFiles = $overwriteFiles; return $this; } /** * @return array * @internal */ public function getPackageFilenameParts(CompletePackageInterface $package): array { $baseName = $package->getArchiveName(); if (null === $baseName) { $baseName = Preg::replace('#[^a-z0-9-_]#i', '-', $package->getName()); } $parts = [ 'base' => $baseName, ]; $distReference = $package->getDistReference(); if (null !== $distReference && Preg::isMatch('{^[a-f0-9]{40}$}', $distReference)) { $parts['dist_reference'] = $distReference; $parts['dist_type'] = $package->getDistType(); } else { $parts['version'] = $package->getPrettyVersion(); $parts['dist_reference'] = $distReference; } $sourceReference = $package->getSourceReference(); if (null !== $sourceReference) { $parts['source_reference'] = substr(sha1($sourceReference), 0, 6); } $parts = array_filter($parts); foreach ($parts as $key => $part) { $parts[$key] = str_replace('/', '-', $part); } return $parts; } /** * @param array $parts * * @return string * @internal */ public function getPackageFilenameFromParts(array $parts): string { return implode('-', $parts); } /** * Generate a distinct filename for a particular version of a package. * * @param CompletePackageInterface $package The package to get a name for * * @return string A filename without an extension */ public function getPackageFilename(CompletePackageInterface $package): string { return $this->getPackageFilenameFromParts($this->getPackageFilenameParts($package)); } /** * Create an archive of the specified package. * * @param CompletePackageInterface $package The package to archive * @param string $format The format of the archive (zip, tar, ...) * @param string $targetDir The directory where to build the archive * @param string|null $fileName The relative file name to use for the archive, or null to generate * the package name. Note that the format will be appended to this name * @param bool $ignoreFilters Ignore filters when looking for files in the package * @throws \InvalidArgumentException * @throws \RuntimeException * @return string The path of the created archive */ public function archive( CompletePackageInterface $package, string $format, string $targetDir, ?string $fileName = null, bool $ignoreFilters = false, ): string { if (empty($format)) { throw new \InvalidArgumentException('Format must be specified'); } // Search for the most appropriate archiver $usableArchiver = null; foreach ($this->archivers as $archiver) { if ($archiver->supports($format, $package->getSourceType())) { $usableArchiver = $archiver; break; } } // Checks the format/source type are supported before downloading the package if (null === $usableArchiver) { throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); } $filesystem = new Filesystem(); if ($package instanceof RootPackageInterface) { $sourcePath = realpath('.'); } else { // Directory used to download the sources $sourcePath = sys_get_temp_dir().'/composer_archive'.uniqid(); $filesystem->ensureDirectoryExists($sourcePath); try { // Download sources $promise = $this->downloadManager->download($package, $sourcePath); SyncHelper::await($this->loop, $promise); $promise = $this->downloadManager->install($package, $sourcePath); SyncHelper::await($this->loop, $promise); } catch (\Exception $e) { $filesystem->removeDirectory($sourcePath); throw $e; } // Check exclude from downloaded composer.json if (file_exists($composerJsonPath = $sourcePath.'/composer.json')) { $jsonFile = new JsonFile($composerJsonPath); $jsonData = $jsonFile->read(); if (!empty($jsonData['archive']['name'])) { $package->setArchiveName($jsonData['archive']['name']); } if (!empty($jsonData['archive']['exclude'])) { $package->setArchiveExcludes($jsonData['archive']['exclude']); } } } $supportedFormats = $this->getSupportedFormats(); $packageNameParts = null === $fileName ? $this->getPackageFilenameParts($package) : ['base' => $fileName]; $packageName = $this->getPackageFilenameFromParts($packageNameParts); $excludePatterns = $this->buildExcludePatterns($packageNameParts, $supportedFormats); // Archive filename $filesystem->ensureDirectoryExists($targetDir); $target = realpath($targetDir).'/'.$packageName.'.'.$format; $filesystem->ensureDirectoryExists(dirname($target)); if (!$this->overwriteFiles && file_exists($target)) { return $target; } // Create the archive $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format; $filesystem->ensureDirectoryExists(dirname($tempTarget)); $archivePath = $usableArchiver->archive( $sourcePath, $tempTarget, $format, array_merge($excludePatterns, $package->getArchiveExcludes()), $ignoreFilters ); $filesystem->rename($archivePath, $target); // cleanup temporary download if (!$package instanceof RootPackageInterface) { $filesystem->removeDirectory($sourcePath); } $filesystem->remove($tempTarget); return $target; } /** * @param string[] $parts * @param string[] $formats * * @return string[] */ private function buildExcludePatterns(array $parts, array $formats): array { $base = $parts['base']; if (count($parts) > 1) { $base .= '-*'; } $patterns = []; foreach ($formats as $format) { $patterns[] = "$base.$format"; } return $patterns; } /** * @return string[] */ private function getSupportedFormats(): array { $formats = []; foreach ($this->archivers as $archiver) { $items = []; switch (get_class($archiver)) { case ZipArchiver::class: $items = ['zip']; break; case PharArchiver::class: $items = ['zip', 'tar', 'tar.gz', 'tar.bz2']; break; } $formats = array_merge($formats, $items); } return array_unique($formats); } } __halt_compiler();----SIGNATURE:----MLY7L3hdGDJkHdwouwzaJzkjSJa80LGRCek2nMXpKW+dzTytGcrYAmqt5YLTSg3VHlxuS/yC2d0c3toCaeiEp2AM1VpVAiuz4qDaKV+yihi/Xuo4iR3glEknjWHomEfG2uzVhGmLdIrnV9N/lCbe1jWjUC53Znr9QDN3HwpJXzbQvVo+JQyJnudzYUyuZu0STKmSnpmDkkInNQVkDnTXq7kjaSv2aDN/mgjhhZpFeXZElIYcR7vE/uCnSY+/NK9hRp6xC5wnLcmQxFUTDFVGDiSWOQ2DWiylcHylAEu2cLdtx1mAM71GYZboSwgLd0pIUvD9bfW7CdKoJWY6s5/F9Vk/ZYCDFJv80mROoEtAcQ5qKeMtS0Pq+Ryt52NiLPKkP/BNFIdfd0/aCB/3UntNJO2Hx82SI0eXe9AdHKkMabkUiHcepaDsYTMxkth1wHdyVm08l4gvpZAZz7O9FJatJFH+ij9uizdJ+nylVtCXzoUniiJJ3waWE87ml9I4KkwTvLNafAtOm+JcCFbGVZu1F6WWXQFY9Nf70ogWaYbv/SJ3I5ULdV86amoJv5DwVmTu7mI7JxuI1XjJsJL9y5ePkDJ5zAWWgTvVKR6hQcrhKrWznIwRDz2ZPhM2ejjEIZClo5tvw2qN1eESIgrvo465wO7yFg09mgqjlZM4/uyZ874=----ATTACHMENT:----ODU3NjU0OTM0MzgwNzg4MCAxOTMzMjMyMDI5ODYyNzAzIDg2ODgzODI3NDIzNDAzNzQ=