function ZipDownloader::extractWithSystemUnzip
extract $file to $path with "unzip" command
@phpstan-return PromiseInterface<void|null>
Parameters
string $file File to extract:
string $path Path where to extract file:
1 call to ZipDownloader::extractWithSystemUnzip()
- ZipDownloader::extract in vendor/
composer/ composer/ src/ Composer/ Downloader/ ZipDownloader.php - extract $file to $path
File
-
vendor/
composer/ composer/ src/ Composer/ Downloader/ ZipDownloader.php, line 110
Class
- ZipDownloader
- @author Jordi Boggiano <j.boggiano@seld.be>
Namespace
Composer\DownloaderCode
private function extractWithSystemUnzip(PackageInterface $package, string $file, string $path) : PromiseInterface {
static $warned7ZipLinux = false;
// Force Exception throwing if the other alternative extraction method is not available
$isLastChance = !self::$hasZipArchive;
if (0 === \count(self::$unzipCommands)) {
// This was call as the favorite extract way, but is not available
// We switch to the alternative
return $this->extractWithZipArchive($package, $file, $path);
}
$commandSpec = reset(self::$unzipCommands);
$executable = $commandSpec[0];
$command = array_slice($commandSpec, 1);
$map = [
// normalize separators to backslashes to avoid problems with 7-zip on windows
// see https://github.com/composer/composer/issues/10058
'%file%' => strtr($file, '/', DIRECTORY_SEPARATOR),
'%path%' => strtr($path, '/', DIRECTORY_SEPARATOR),
];
$command = array_map(static function ($value) use ($map) {
return strtr($value, $map);
}, $command);
if (!$warned7ZipLinux && !Platform::isWindows() && in_array($executable, [
'7z',
'7zz',
], true)) {
$warned7ZipLinux = true;
if (0 === $this->process
->execute([
$commandSpec[1],
], $output)) {
if (Preg::isMatchStrictGroups('{^\\s*7-Zip(?: \\[64\\])? ([0-9.]+)}', $output, $match) && version_compare($match[1], '21.01', '<')) {
$this->io
->writeError(' <warning>Unzipping using ' . $executable . ' ' . $match[1] . ' may result in incorrect file permissions. Install ' . $executable . ' 21.01+ or unzip to ensure you get correct permissions.</warning>');
}
}
}
$io = $this->io;
$tryFallback = function (\Throwable $processError) use ($isLastChance, $io, $file, $path, $package, $executable) : \React\Promise\PromiseInterface {
if ($isLastChance) {
throw $processError;
}
if (str_contains($processError->getMessage(), 'zip bomb')) {
throw $processError;
}
if (!is_file($file)) {
$io->writeError(' <warning>' . $processError->getMessage() . '</warning>');
$io->writeError(' <warning>This most likely is due to a custom installer plugin not handling the returned Promise from the downloader</warning>');
$io->writeError(' <warning>See https://github.com/composer/installers/commit/5006d0c28730ade233a8f42ec31ac68fb1c5c9bb for an example fix</warning>');
}
else {
$io->writeError(' <warning>' . $processError->getMessage() . '</warning>');
$io->writeError(' The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)');
$io->writeError(' Unzip with ' . $executable . ' command failed, falling back to ZipArchive class');
// additional debug data to try to figure out GH actions issues https://github.com/composer/composer/issues/11148
if (Platform::getEnv('GITHUB_ACTIONS') !== false && Platform::getEnv('COMPOSER_TESTS_ARE_RUNNING') === false) {
$io->writeError(' <warning>Additional debug info, please report to https://github.com/composer/composer/issues/11148 if you see this:</warning>');
$io->writeError('File size: ' . @filesize($file));
$io->writeError('File SHA1: ' . hash_file('sha1', $file));
$io->writeError('First 100 bytes (hex): ' . bin2hex(substr((string) file_get_contents($file), 0, 100)));
$io->writeError('Last 100 bytes (hex): ' . bin2hex(substr((string) file_get_contents($file), -100)));
if (strlen((string) $package->getDistUrl()) > 0) {
$io->writeError('Origin URL: ' . $this->processUrl($package, (string) $package->getDistUrl()));
$io->writeError('Response Headers: ' . json_encode(FileDownloader::$responseHeaders[$package->getName()] ?? []));
}
}
}
return $this->extractWithZipArchive($package, $file, $path);
};
try {
$promise = $this->process
->executeAsync($command);
return $promise->then(function (Process $process) use ($tryFallback, $command, $package, $file) {
if (!$process->isSuccessful()) {
if (isset($this->cleanupExecuted[$package->getName()])) {
throw new \RuntimeException('Failed to extract ' . $package->getName() . ' as the installation was aborted by another package operation.');
}
$output = $process->getErrorOutput();
$output = str_replace(', ' . $file . '.zip or ' . $file . '.ZIP', '', $output);
return $tryFallback(new \RuntimeException('Failed to extract ' . $package->getName() . ': (' . $process->getExitCode() . ') ' . implode(' ', $command) . "\n\n" . $output));
}
});
} catch (\Throwable $e) {
return $tryFallback($e);
}
}