Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. ZipDownloader.php

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\Downloader

Code

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);
    }
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal