@ -86,9 +86,8 @@ class ZipDownloader extends ArchiveDownloader
* @param string $file File to extract
* @param string $path Path where to extract file
* @param bool $isLastChance If true it is called as a fallback and should throw an exception
* @return bool Success status
*/
protected function extractWithSystemUnzip($file, $path, $isLastChanc e)
private function extractWithSystemUnzip(PackageInterface $package, $file, $path, $isLastChance, $async = fals e)
{
if (!self::$hasZipArchive) {
// Force Exception throwing if the Other alternative is not available
@ -98,18 +97,47 @@ class ZipDownloader extends ArchiveDownloader
if (!self::$hasSystemUnzip & & !$isLastChance) {
// This was call as the favorite extract way, but is not available
// We switch to the alternative
return $this->extractWithZipArchive($file, $path, true);
return $this->extractWithZipArchive($package, $ file, $path, true);
}
$processError = null;
// When called after a ZipArchive failed, perhaps there is some files to overwrite
$overwrite = $isLastChance ? '-o' : '';
$command = 'unzip -qq '.$overwrite.' '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path);
if ($async) {
$self = $this;
$io = $this->io;
$tryFallback = function ($processError) use ($isLastChance, $io, $self, $file, $path, $package) {
if ($isLastChance) {
throw $processError;
}
$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 unzip command failed, falling back to ZipArchive class');
return $self->extractWithZipArchive($package, $file, $path, true);
};
try {
$promise = $this->process->executeAsync($command);
return $promise->then(function ($process) use ($tryFallback, $command, $package) {
if (!$process->isSuccessful()) {
return $tryFallback(new \RuntimeException('Failed to extract '.$package->getName().': ('.$process->getExitCode().') '.$command."\n\n".$process->getErrorOutput()));
}
});
} catch (\Exception $e) {
return $tryFallback($e);
} catch (\Throwable $e) {
return $tryFallback($e);
}
}
$processError = null;
try {
if (0 === $exitCode = $this->process->execute($command, $ignoredOutput)) {
return true;
return \React\Promise\resolve() ;
}
$processError = new \RuntimeException('Failed to execute ('.$exitCode.') '.$command."\n\n".$this->process->getErrorOutput());
@ -121,11 +149,11 @@ class ZipDownloader extends ArchiveDownloader
throw $processError;
}
$this->io->writeError(' '.$processError->getMessage());
$this->io->writeError(' < warning > '.$processError->getMessage().'< / warning > ' );
$this->io->writeError(' The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)');
$this->io->writeError(' Unzip with unzip command failed, falling back to ZipArchive class');
return $this->extractWithZipArchive($file, $path, true);
return $this->extractWithZipArchive($package, $ file, $path, true);
}
/**
@ -134,9 +162,11 @@ class ZipDownloader extends ArchiveDownloader
* @param string $file File to extract
* @param string $path Path where to extract file
* @param bool $isLastChance If true it is called as a fallback and should throw an exception
* @return bool Success status
*
* TODO v3 should make this private once we can drop PHP 5.3 support
* @protected
*/
protected function extractWithZipArchive($file, $path, $isLastChance)
public function extractWithZipArchive(PackageInterface $package, $file, $path, $isLastChance)
{
if (!self::$hasSystemUnzip) {
// Force Exception throwing if the Other alternative is not available
@ -146,7 +176,7 @@ class ZipDownloader extends ArchiveDownloader
if (!self::$hasZipArchive & & !$isLastChance) {
// This was call as the favorite extract way, but is not available
// We switch to the alternative
return $this->extractWithSystemUnzip($file, $path, true);
return $this->extractWithSystemUnzip($package, $ file, $path, true);
}
$processError = null;
@ -159,7 +189,7 @@ class ZipDownloader extends ArchiveDownloader
if (true === $extractResult) {
$zipArchive->close();
return true ;
return \React\Promise\resolve() ;
}
$processError = new \RuntimeException(rtrim("There was an error extracting the ZIP file, it is either corrupted or using an invalid format.\n"));
@ -170,16 +200,18 @@ class ZipDownloader extends ArchiveDownloader
$processError = new \RuntimeException('The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems): '.$e->getMessage(), 0, $e);
} catch (\Exception $e) {
$processError = $e;
} catch (\Throwable $e) {
$processError = $e;
}
if ($isLastChance) {
throw $processError;
}
$this->io->writeError(' '.$processError->getMessage());
$this->io->writeError(' < warning > '.$processError->getMessage().'< / warning > ' );
$this->io->writeError(' Unzip with ZipArchive class failed, falling back to unzip command');
return $this->extractWithSystemUnzip($file, $path, true);
return $this->extractWithSystemUnzip($package, $ file, $path, true);
}
/**
@ -192,10 +224,10 @@ class ZipDownloader extends ArchiveDownloader
{
// Each extract calls its alternative if not available or fails
if (self::$isWindows) {
$this->extractWithZipArchive($file, $path, false);
} else {
$this->extractWithSystemUnzip($file, $path, false);
return $this->extractWithZipArchive($package, $file, $path, false);
}
return $this->extractWithSystemUnzip($package, $file, $path, false, true);
}
/**