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

Breadcrumb

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

class DiagnoseCommand

@author Jordi Boggiano <j.boggiano@seld.be>

Hierarchy

  • class \Symfony\Component\Console\Command\Command
    • class \Composer\Command\BaseCommand extends \Symfony\Component\Console\Command\Command
      • class \Composer\Command\DiagnoseCommand extends \Composer\Command\BaseCommand

Expanded class hierarchy of DiagnoseCommand

File

vendor/composer/composer/src/Composer/Command/DiagnoseCommand.php, line 55

Namespace

Composer\Command
View source
class DiagnoseCommand extends BaseCommand {
    
    /** @var HttpDownloader */
    protected $httpDownloader;
    
    /** @var ProcessExecutor */
    protected $process;
    
    /** @var int */
    protected $exitCode = 0;
    protected function configure() : void {
        $this->setName('diagnose')
            ->setDescription('Diagnoses the system to identify common errors')
            ->setHelp(<<<EOT
The <info>diagnose</info> command checks common errors to help debugging problems.

The process exit code will be 1 in case of warnings and 2 for errors.

Read more at https://getcomposer.org/doc/03-cli.md#diagnose
EOT
);
    }
    protected function execute(InputInterface $input, OutputInterface $output) : int {
        $composer = $this->tryComposer();
        $io = $this->getIO();
        if ($composer) {
            $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
            $composer->getEventDispatcher()
                ->dispatch($commandEvent->getName(), $commandEvent);
            $io->write('Checking composer.json: ', false);
            $this->outputResult($this->checkComposerSchema());
            if ($composer->getLocker()
                ->isLocked()) {
                $io->write('Checking composer.lock: ', false);
                $this->outputResult($this->checkComposerLockSchema($composer->getLocker()));
            }
            $this->process = $composer->getLoop()
                ->getProcessExecutor() ?? new ProcessExecutor($io);
        }
        else {
            $this->process = new ProcessExecutor($io);
        }
        if ($composer) {
            $config = $composer->getConfig();
        }
        else {
            $config = Factory::createConfig();
        }
        $config->merge([
            'config' => [
                'secure-http' => false,
            ],
        ], Config::SOURCE_COMMAND);
        $config->prohibitUrlByConfig('http://repo.packagist.org', new NullIO());
        $this->httpDownloader = Factory::createHttpDownloader($io, $config);
        $io->write('Checking platform settings: ', false);
        $this->outputResult($this->checkPlatform());
        $io->write('Checking git settings: ', false);
        $this->outputResult($this->checkGit());
        $io->write('Checking http connectivity to packagist: ', false);
        $this->outputResult($this->checkHttp('http', $config));
        $io->write('Checking https connectivity to packagist: ', false);
        $this->outputResult($this->checkHttp('https', $config));
        foreach ($config->getRepositories() as $repo) {
            if (($repo['type'] ?? null) === 'composer' && isset($repo['url'])) {
                $composerRepo = new ComposerRepository($repo, $this->getIO(), $config, $this->httpDownloader);
                $reflMethod = new \ReflectionMethod($composerRepo, 'getPackagesJsonUrl');
                if (PHP_VERSION_ID < 80100) {
                    $reflMethod->setAccessible(true);
                }
                $url = $reflMethod->invoke($composerRepo);
                if (!str_starts_with($url, 'http')) {
                    continue;
                }
                if (str_starts_with($url, 'https://repo.packagist.org')) {
                    continue;
                }
                $io->write('Checking connectivity to ' . $repo['url'] . ': ', false);
                $this->outputResult($this->checkComposerRepo($url, $config));
            }
        }
        $proxyManager = ProxyManager::getInstance();
        $protos = $config->get('disable-tls') === true ? [
            'http',
        ] : [
            'http',
            'https',
        ];
        try {
            foreach ($protos as $proto) {
                $proxy = $proxyManager->getProxyForRequest($proto . '://repo.packagist.org');
                if ($proxy->getStatus() !== '') {
                    $type = $proxy->isSecure() ? 'HTTPS' : 'HTTP';
                    $io->write('Checking ' . $type . ' proxy with ' . $proto . ': ', false);
                    $this->outputResult($this->checkHttpProxy($proxy, $proto));
                }
            }
        } catch (TransportException $e) {
            $io->write('Checking HTTP proxy: ', false);
            $status = $this->checkConnectivityAndComposerNetworkHttpEnablement();
            $this->outputResult(is_string($status) ? $status : $e);
        }
        if (count($oauth = $config->get('github-oauth')) > 0) {
            foreach ($oauth as $domain => $token) {
                $io->write('Checking ' . $domain . ' oauth access: ', false);
                $this->outputResult($this->checkGithubOauth($domain, $token));
            }
        }
        else {
            $io->write('Checking github.com rate limit: ', false);
            try {
                $rate = $this->getGithubRateLimit('github.com');
                if (!is_array($rate)) {
                    $this->outputResult($rate);
                }
                elseif (10 > $rate['remaining']) {
                    $io->write('<warning>WARNING</warning>');
                    $io->write(sprintf('<comment>GitHub has a rate limit on their API. ' . 'You currently have <options=bold>%u</options=bold> ' . 'out of <options=bold>%u</options=bold> requests left.' . PHP_EOL . 'See https://developer.github.com/v3/#rate-limiting and also' . PHP_EOL . '    https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens</comment>', $rate['remaining'], $rate['limit']));
                }
                else {
                    $this->outputResult(true);
                }
            } catch (\Exception $e) {
                if ($e instanceof TransportException && $e->getCode() === 401) {
                    $this->outputResult('<comment>The oauth token for github.com seems invalid, run "composer config --global --unset github-oauth.github.com" to remove it</comment>');
                }
                else {
                    $this->outputResult($e);
                }
            }
        }
        $io->write('Checking disk free space: ', false);
        $this->outputResult($this->checkDiskSpace($config));
        if (strpos(__FILE__, 'phar:') === 0) {
            $io->write('Checking pubkeys: ', false);
            $this->outputResult($this->checkPubKeys($config));
            $io->write('Checking Composer version: ', false);
            $this->outputResult($this->checkVersion($config));
        }
        $io->write('Checking Composer and its dependencies for vulnerabilities: ', false);
        $this->outputResult($this->checkComposerAudit($config));
        $io->write(sprintf('Composer version: <comment>%s</comment>', Composer::getVersion()));
        $platformOverrides = $config->get('platform') ?: [];
        $platformRepo = new PlatformRepository([], $platformOverrides);
        $phpPkg = $platformRepo->findPackage('php', '*');
        $phpVersion = $phpPkg->getPrettyVersion();
        if ($phpPkg instanceof CompletePackageInterface && str_contains((string) $phpPkg->getDescription(), 'overridden')) {
            $phpVersion .= ' - ' . $phpPkg->getDescription();
        }
        $io->write(sprintf('PHP version: <comment>%s</comment>', $phpVersion));
        if (defined('PHP_BINARY')) {
            $io->write(sprintf('PHP binary path: <comment>%s</comment>', PHP_BINARY));
        }
        $io->write('OpenSSL version: ' . (defined('OPENSSL_VERSION_TEXT') ? '<comment>' . OPENSSL_VERSION_TEXT . '</comment>' : '<error>missing</error>'));
        $io->write('curl version: ' . $this->getCurlVersion());
        $finder = new ExecutableFinder();
        $hasSystemUnzip = (bool) $finder->find('unzip');
        $bin7zip = '';
        if ($hasSystem7zip = (bool) $finder->find('7z', null, [
            'C:\\Program Files\\7-Zip',
        ])) {
            $bin7zip = '7z';
        }
        if (!Platform::isWindows() && !$hasSystem7zip && ($hasSystem7zip = (bool) $finder->find('7zz'))) {
            $bin7zip = '7zz';
        }
        $io->write('zip: ' . (extension_loaded('zip') ? '<comment>extension present</comment>' : '<comment>extension not loaded</comment>') . ', ' . ($hasSystemUnzip ? '<comment>unzip present</comment>' : '<comment>unzip not available</comment>') . ', ' . ($hasSystem7zip ? '<comment>7-Zip present (' . $bin7zip . ')</comment>' : '<comment>7-Zip not available</comment>') . (($hasSystem7zip || $hasSystemUnzip) && !function_exists('proc_open') ? ', <warning>proc_open is disabled or not present, unzip/7-z will not be usable</warning>' : ''));
        return $this->exitCode;
    }
    
    /**
     * @return string|true
     */
    private function checkComposerSchema() {
        $validator = new ConfigValidator($this->getIO());
        [
            $errors,
            ,
            $warnings,
        ] = $validator->validate(Factory::getComposerFile());
        if ($errors || $warnings) {
            $messages = [
                'error' => $errors,
                'warning' => $warnings,
            ];
            $output = '';
            foreach ($messages as $style => $msgs) {
                foreach ($msgs as $msg) {
                    $output .= '<' . $style . '>' . $msg . '</' . $style . '>' . PHP_EOL;
                }
            }
            return rtrim($output);
        }
        return true;
    }
    
    /**
     * @return string|true
     */
    private function checkComposerLockSchema(Locker $locker) {
        $json = $locker->getJsonFile();
        try {
            $json->validateSchema(JsonFile::LOCK_SCHEMA);
        } catch (JsonValidationException $e) {
            $output = '';
            foreach ($e->getErrors() as $error) {
                $output .= '<error>' . $error . '</error>' . PHP_EOL;
            }
            return trim($output);
        }
        return true;
    }
    private function checkGit() : string {
        if (!function_exists('proc_open')) {
            return '<comment>proc_open is not available, git cannot be used</comment>';
        }
        $this->process
            ->execute([
            'git',
            'config',
            'color.ui',
        ], $output);
        if (strtolower(trim($output)) === 'always') {
            return '<comment>Your git color.ui setting is set to always, this is known to create issues. Use "git config --global color.ui true" to set it correctly.</comment>';
        }
        $gitVersion = Git::getVersion($this->process);
        if (null === $gitVersion) {
            return '<comment>No git process found</>';
        }
        if (version_compare('2.24.0', $gitVersion, '>')) {
            return '<warning>Your git version (' . $gitVersion . ') is too old and possibly will cause issues. Please upgrade to git 2.24 or above</>';
        }
        return '<info>OK</> <comment>git version ' . $gitVersion . '</>';
    }
    
    /**
     * @return string|string[]|true
     */
    private function checkHttp(string $proto, Config $config) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        $result = [];
        if ($proto === 'https' && $config->get('disable-tls') === true) {
            $tlsWarning = '<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>';
        }
        try {
            $this->httpDownloader
                ->get($proto . '://repo.packagist.org/packages.json');
        } catch (TransportException $e) {
            $hints = HttpDownloader::getExceptionHints($e);
            if (null !== $hints && count($hints) > 0) {
                foreach ($hints as $hint) {
                    $result[] = $hint;
                }
            }
            $result[] = '<error>[' . get_class($e) . '] ' . $e->getMessage() . '</error>';
        }
        if (isset($tlsWarning)) {
            $result[] = $tlsWarning;
        }
        if (count($result) > 0) {
            return $result;
        }
        return true;
    }
    
    /**
     * @return string|string[]|true
     */
    private function checkComposerRepo(string $url, Config $config) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        $result = [];
        if (str_starts_with($url, 'https://') && $config->get('disable-tls') === true) {
            $tlsWarning = '<warning>Composer is configured to disable SSL/TLS protection. This will leave remote HTTPS requests vulnerable to Man-In-The-Middle attacks.</warning>';
        }
        try {
            $this->httpDownloader
                ->get($url);
        } catch (TransportException $e) {
            $hints = HttpDownloader::getExceptionHints($e);
            if (null !== $hints && count($hints) > 0) {
                foreach ($hints as $hint) {
                    $result[] = $hint;
                }
            }
            $result[] = '<error>[' . get_class($e) . '] ' . $e->getMessage() . '</error>';
        }
        if (isset($tlsWarning)) {
            $result[] = $tlsWarning;
        }
        if (count($result) > 0) {
            return $result;
        }
        return true;
    }
    
    /**
     * @return string|\Exception
     */
    private function checkHttpProxy(RequestProxy $proxy, string $protocol) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        try {
            $proxyStatus = $proxy->getStatus();
            if ($proxy->isExcludedByNoProxy()) {
                return '<info>SKIP</> <comment>Because repo.packagist.org is ' . $proxyStatus . '</>';
            }
            $json = $this->httpDownloader
                ->get($protocol . '://repo.packagist.org/packages.json')
                ->decodeJson();
            if (isset($json['provider-includes'])) {
                $hash = reset($json['provider-includes']);
                $hash = $hash['sha256'];
                $path = str_replace('%hash%', $hash, key($json['provider-includes']));
                $provider = $this->httpDownloader
                    ->get($protocol . '://repo.packagist.org/' . $path)
                    ->getBody();
                if (hash('sha256', $provider) !== $hash) {
                    return '<warning>It seems that your proxy (' . $proxyStatus . ') is modifying ' . $protocol . ' traffic on the fly</>';
                }
            }
            return '<info>OK</> <comment>' . $proxyStatus . '</>';
        } catch (\Exception $e) {
            return $e;
        }
    }
    
    /**
     * @return string|\Exception
     */
    private function checkGithubOauth(string $domain, string $token) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        $this->getIO()
            ->setAuthentication($domain, $token, 'x-oauth-basic');
        try {
            $url = $domain === 'github.com' ? 'https://api.' . $domain . '/' : 'https://' . $domain . '/api/v3/';
            $response = $this->httpDownloader
                ->get($url, [
                'retry-auth-failure' => false,
            ]);
            $expiration = $response->getHeader('github-authentication-token-expiration');
            if ($expiration === null) {
                return '<info>OK</> <comment>does not expire</>';
            }
            return '<info>OK</> <comment>expires on ' . $expiration . '</>';
        } catch (\Exception $e) {
            if ($e instanceof TransportException && $e->getCode() === 401) {
                return '<comment>The oauth token for ' . $domain . ' seems invalid, run "composer config --global --unset github-oauth.' . $domain . '" to remove it</comment>';
            }
            return $e;
        }
    }
    
    /**
     * @param  string             $token
     * @throws TransportException
     * @return mixed|string
     */
    private function getGithubRateLimit(string $domain, ?string $token = null) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        if ($token) {
            $this->getIO()
                ->setAuthentication($domain, $token, 'x-oauth-basic');
        }
        $url = $domain === 'github.com' ? 'https://api.' . $domain . '/rate_limit' : 'https://' . $domain . '/api/rate_limit';
        $data = $this->httpDownloader
            ->get($url, [
            'retry-auth-failure' => false,
        ])
            ->decodeJson();
        return $data['resources']['core'];
    }
    
    /**
     * @return string|true
     */
    private function checkDiskSpace(Config $config) {
        if (!function_exists('disk_free_space')) {
            return true;
        }
        $minSpaceFree = 1024 * 1024;
        if (($df = @disk_free_space($dir = $config->get('home'))) !== false && $df < $minSpaceFree || ($df = @disk_free_space($dir = $config->get('vendor-dir'))) !== false && $df < $minSpaceFree) {
            return '<error>The disk hosting ' . $dir . ' is full</error>';
        }
        return true;
    }
    
    /**
     * @return string[]|true
     */
    private function checkPubKeys(Config $config) {
        $home = $config->get('home');
        $errors = [];
        $io = $this->getIO();
        if (file_exists($home . '/keys.tags.pub') && file_exists($home . '/keys.dev.pub')) {
            $io->write('');
        }
        if (file_exists($home . '/keys.tags.pub')) {
            $io->write('Tags Public Key Fingerprint: ' . Keys::fingerprint($home . '/keys.tags.pub'));
        }
        else {
            $errors[] = '<error>Missing pubkey for tags verification</error>';
        }
        if (file_exists($home . '/keys.dev.pub')) {
            $io->write('Dev Public Key Fingerprint: ' . Keys::fingerprint($home . '/keys.dev.pub'));
        }
        else {
            $errors[] = '<error>Missing pubkey for dev verification</error>';
        }
        if ($errors) {
            $errors[] = '<error>Run composer self-update --update-keys to set them up</error>';
        }
        return $errors ?: true;
    }
    
    /**
     * @return string|\Exception|true
     */
    private function checkVersion(Config $config) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        $versionsUtil = new Versions($config, $this->httpDownloader);
        try {
            $latest = $versionsUtil->getLatest();
        } catch (\Exception $e) {
            return $e;
        }
        if (Composer::VERSION !== $latest['version'] && Composer::VERSION !== '@package_version@') {
            return '<comment>You are not running the latest ' . $versionsUtil->getChannel() . ' version, run `composer self-update` to update (' . Composer::VERSION . ' => ' . $latest['version'] . ')</comment>';
        }
        return true;
    }
    
    /**
     * @return string|true
     */
    private function checkComposerAudit(Config $config) {
        $result = $this->checkConnectivityAndComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        $auditor = new Auditor();
        $repoSet = new RepositorySet();
        $installedJson = new JsonFile(__DIR__ . '/../../../vendor/composer/installed.json');
        if (!$installedJson->exists()) {
            return '<warning>Could not find Composer\'s installed.json, this must be a non-standard Composer installation.</>';
        }
        $localRepo = new FilesystemRepository($installedJson);
        $version = Composer::getVersion();
        $packages = $localRepo->getCanonicalPackages();
        if ($version !== '@package_version@') {
            $versionParser = new VersionParser();
            $normalizedVersion = $versionParser->normalize($version);
            $rootPkg = new RootPackage('composer/composer', $normalizedVersion, $version);
            $packages[] = $rootPkg;
        }
        $repoSet->addRepository(new ComposerRepository([
            'type' => 'composer',
            'url' => 'https://packagist.org',
        ], new NullIO(), $config, $this->httpDownloader));
        try {
            $io = new BufferIO();
            $result = $auditor->audit($io, $repoSet, $packages, Auditor::FORMAT_TABLE, true, [], Auditor::ABANDONED_IGNORE);
        } catch (\Throwable $e) {
            return '<warning>Failed performing audit: ' . $e->getMessage() . '</>';
        }
        if ($result > 0) {
            return '<error>Audit found some issues:</>' . PHP_EOL . $io->getOutput();
        }
        return true;
    }
    private function getCurlVersion() : string {
        if (extension_loaded('curl')) {
            if (!HttpDownloader::isCurlEnabled()) {
                return '<error>disabled via disable_functions, using php streams fallback, which reduces performance</error>';
            }
            $version = curl_version();
            return '<comment>' . $version['version'] . '</comment> ' . 'libz <comment>' . (!empty($version['libz_version']) ? $version['libz_version'] : 'missing') . '</comment> ' . 'ssl <comment>' . ($version['ssl_version'] ?? 'missing') . '</comment>';
        }
        return '<error>missing, using php streams fallback, which reduces performance</error>';
    }
    
    /**
     * @param bool|string|string[]|\Exception $result
     */
    private function outputResult($result) : void {
        $io = $this->getIO();
        if (true === $result) {
            $io->write('<info>OK</info>');
            return;
        }
        $hadError = false;
        $hadWarning = false;
        if ($result instanceof \Exception) {
            $result = '<error>[' . get_class($result) . '] ' . $result->getMessage() . '</error>';
        }
        if (!$result) {
            // falsey results should be considered as an error, even if there is nothing to output
            $hadError = true;
        }
        else {
            if (!is_array($result)) {
                $result = [
                    $result,
                ];
            }
            foreach ($result as $message) {
                if (false !== strpos($message, '<error>')) {
                    $hadError = true;
                }
                elseif (false !== strpos($message, '<warning>')) {
                    $hadWarning = true;
                }
            }
        }
        if ($hadError) {
            $io->write('<error>FAIL</error>');
            $this->exitCode = max($this->exitCode, 2);
        }
        elseif ($hadWarning) {
            $io->write('<warning>WARNING</warning>');
            $this->exitCode = max($this->exitCode, 1);
        }
        if ($result) {
            foreach ($result as $message) {
                $io->write(trim($message));
            }
        }
    }
    
    /**
     * @return string|true
     */
    private function checkPlatform() {
        $output = '';
        $out = static function ($msg, $style) use (&$output) : void {
            $output .= '<' . $style . '>' . $msg . '</' . $style . '>' . PHP_EOL;
        };
        // code below taken from getcomposer.org/installer, any changes should be made there and replicated here
        $errors = [];
        $warnings = [];
        $displayIniMessage = false;
        $iniMessage = PHP_EOL . PHP_EOL . IniHelper::getMessage();
        $iniMessage .= PHP_EOL . 'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
        if (!function_exists('json_decode')) {
            $errors['json'] = true;
        }
        if (!extension_loaded('Phar')) {
            $errors['phar'] = true;
        }
        if (!extension_loaded('filter')) {
            $errors['filter'] = true;
        }
        if (!extension_loaded('hash')) {
            $errors['hash'] = true;
        }
        if (!extension_loaded('iconv') && !extension_loaded('mbstring')) {
            $errors['iconv_mbstring'] = true;
        }
        if (!filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN)) {
            $errors['allow_url_fopen'] = true;
        }
        if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
            $errors['ioncube'] = ioncube_loader_version();
        }
        if (\PHP_VERSION_ID < 70205) {
            $errors['php'] = PHP_VERSION;
        }
        if (!extension_loaded('openssl')) {
            $errors['openssl'] = true;
        }
        if (extension_loaded('openssl') && OPENSSL_VERSION_NUMBER < 0x1000100f) {
            $warnings['openssl_version'] = true;
        }
        if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
            $warnings['apc_cli'] = true;
        }
        if (!extension_loaded('zlib')) {
            $warnings['zlib'] = true;
        }
        ob_start();
        phpinfo(INFO_GENERAL);
        $phpinfo = ob_get_clean();
        if (is_string($phpinfo) && Preg::isMatchStrictGroups('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) {
            $configure = $match[1];
            if (str_contains($configure, '--enable-sigchild')) {
                $warnings['sigchild'] = true;
            }
            if (str_contains($configure, '--with-curlwrappers')) {
                $warnings['curlwrappers'] = true;
            }
        }
        if (filter_var(ini_get('xdebug.profiler_enabled'), FILTER_VALIDATE_BOOLEAN)) {
            $warnings['xdebug_profile'] = true;
        }
        elseif (XdebugHandler::isXdebugActive()) {
            $warnings['xdebug_loaded'] = true;
        }
        if (defined('PHP_WINDOWS_VERSION_BUILD') && (version_compare(PHP_VERSION, '7.2.23', '<') || version_compare(PHP_VERSION, '7.3.0', '>=') && version_compare(PHP_VERSION, '7.3.10', '<'))) {
            $warnings['onedrive'] = PHP_VERSION;
        }
        if (extension_loaded('uopz') && !(filter_var(ini_get('uopz.disable'), FILTER_VALIDATE_BOOLEAN) || filter_var(ini_get('uopz.exit'), FILTER_VALIDATE_BOOLEAN))) {
            $warnings['uopz'] = true;
        }
        if (!empty($errors)) {
            foreach ($errors as $error => $current) {
                switch ($error) {
                    case 'json':
                        $text = PHP_EOL . "The json extension is missing." . PHP_EOL;
                        $text .= "Install it or recompile php without --disable-json";
                        break;
                    case 'phar':
                        $text = PHP_EOL . "The phar extension is missing." . PHP_EOL;
                        $text .= "Install it or recompile php without --disable-phar";
                        break;
                    case 'filter':
                        $text = PHP_EOL . "The filter extension is missing." . PHP_EOL;
                        $text .= "Install it or recompile php without --disable-filter";
                        break;
                    case 'hash':
                        $text = PHP_EOL . "The hash extension is missing." . PHP_EOL;
                        $text .= "Install it or recompile php without --disable-hash";
                        break;
                    case 'iconv_mbstring':
                        $text = PHP_EOL . "The iconv OR mbstring extension is required and both are missing." . PHP_EOL;
                        $text .= "Install either of them or recompile php without --disable-iconv";
                        break;
                    case 'php':
                        $text = PHP_EOL . "Your PHP ({$current}) is too old, you must upgrade to PHP 7.2.5 or higher.";
                        break;
                    case 'allow_url_fopen':
                        $text = PHP_EOL . "The allow_url_fopen setting is incorrect." . PHP_EOL;
                        $text .= "Add the following to the end of your `php.ini`:" . PHP_EOL;
                        $text .= "    allow_url_fopen = On";
                        $displayIniMessage = true;
                        break;
                    case 'ioncube':
                        $text = PHP_EOL . "Your ionCube Loader extension ({$current}) is incompatible with Phar files." . PHP_EOL;
                        $text .= "Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:" . PHP_EOL;
                        $text .= "    zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so";
                        $displayIniMessage = true;
                        break;
                    case 'openssl':
                        $text = PHP_EOL . "The openssl extension is missing, which means that secure HTTPS transfers are impossible." . PHP_EOL;
                        $text .= "If possible you should enable it or recompile php with --with-openssl";
                        break;
                    default:
                        throw new \InvalidArgumentException(sprintf("DiagnoseCommand: Unknown error type \"%s\". Please report at https://github.com/composer/composer/issues/new.", $error));
                }
                $out($text, 'error');
            }
            $output .= PHP_EOL;
        }
        if (!empty($warnings)) {
            foreach ($warnings as $warning => $current) {
                switch ($warning) {
                    case 'apc_cli':
                        $text = "The apc.enable_cli setting is incorrect." . PHP_EOL;
                        $text .= "Add the following to the end of your `php.ini`:" . PHP_EOL;
                        $text .= "  apc.enable_cli = Off";
                        $displayIniMessage = true;
                        break;
                    case 'zlib':
                        $text = 'The zlib extension is not loaded, this can slow down Composer a lot.' . PHP_EOL;
                        $text .= 'If possible, enable it or recompile php with --with-zlib' . PHP_EOL;
                        $displayIniMessage = true;
                        break;
                    case 'sigchild':
                        $text = "PHP was compiled with --enable-sigchild which can cause issues on some platforms." . PHP_EOL;
                        $text .= "Recompile it without this flag if possible, see also:" . PHP_EOL;
                        $text .= "  https://bugs.php.net/bug.php?id=22999";
                        break;
                    case 'curlwrappers':
                        $text = "PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub." . PHP_EOL;
                        $text .= " Recompile it without this flag if possible";
                        break;
                    case 'openssl_version':
                        // Attempt to parse version number out, fallback to whole string value.
                        $opensslVersion = strstr(trim(strstr(OPENSSL_VERSION_TEXT, ' ')), ' ', true);
                        $opensslVersion = $opensslVersion ?: OPENSSL_VERSION_TEXT;
                        $text = "The OpenSSL library ({$opensslVersion}) used by PHP does not support TLSv1.2 or TLSv1.1." . PHP_EOL;
                        $text .= "If possible you should upgrade OpenSSL to version 1.0.1 or above.";
                        break;
                    case 'xdebug_loaded':
                        $text = "The xdebug extension is loaded, this can slow down Composer a little." . PHP_EOL;
                        $text .= " Disabling it when using Composer is recommended.";
                        break;
                    case 'xdebug_profile':
                        $text = "The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot." . PHP_EOL;
                        $text .= "Add the following to the end of your `php.ini` to disable it:" . PHP_EOL;
                        $text .= "  xdebug.profiler_enabled = 0";
                        $displayIniMessage = true;
                        break;
                    case 'onedrive':
                        $text = "The Windows OneDrive folder is not supported on PHP versions below 7.2.23 and 7.3.10." . PHP_EOL;
                        $text .= "Upgrade your PHP ({$current}) to use this location with Composer." . PHP_EOL;
                        break;
                    case 'uopz':
                        $text = "The uopz extension ignores exit calls and may not work with all Composer commands." . PHP_EOL;
                        $text .= "Disabling it when using Composer is recommended.";
                        break;
                    default:
                        throw new \InvalidArgumentException(sprintf("DiagnoseCommand: Unknown warning type \"%s\". Please report at https://github.com/composer/composer/issues/new.", $warning));
                }
                $out($text, 'comment');
            }
        }
        if ($displayIniMessage) {
            $out($iniMessage, 'comment');
        }
        if (in_array(Platform::getEnv('COMPOSER_IPRESOLVE'), [
            '4',
            '6',
        ], true)) {
            $warnings['ipresolve'] = true;
            $out('The COMPOSER_IPRESOLVE env var is set to ' . Platform::getEnv('COMPOSER_IPRESOLVE') . ' which may result in network failures below.', 'comment');
        }
        return count($warnings) === 0 && count($errors) === 0 ? true : $output;
    }
    
    /**
     * Check if allow_url_fopen is ON
     *
     * @return string|true
     */
    private function checkConnectivity() {
        if (!ini_get('allow_url_fopen')) {
            return '<info>SKIP</> <comment>Because allow_url_fopen is missing.</>';
        }
        return true;
    }
    
    /**
     * @return string|true
     */
    private function checkConnectivityAndComposerNetworkHttpEnablement() {
        $result = $this->checkConnectivity();
        if ($result !== true) {
            return $result;
        }
        $result = $this->checkComposerNetworkHttpEnablement();
        if ($result !== true) {
            return $result;
        }
        return true;
    }
    
    /**
     * Check if Composer network is enabled for HTTP/S
     *
     * @return string|true
     */
    private function checkComposerNetworkHttpEnablement() {
        if ((bool) Platform::getEnv('COMPOSER_DISABLE_NETWORK')) {
            return '<info>SKIP</> <comment>Network is disabled by COMPOSER_DISABLE_NETWORK.</>';
        }
        return true;
    }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
BaseCommand::$composer private property
BaseCommand::$io private property
BaseCommand::complete public function @inheritdoc Overrides Command::complete 1
BaseCommand::createComposerInstance protected function Calls {
BaseCommand::formatRequirements protected function
BaseCommand::getApplication public function Gets the application instance for this command. Overrides Command::getApplication
BaseCommand::getAuditFormat protected function @internal
BaseCommand::getComposer Deprecated public function
BaseCommand::getIO public function
BaseCommand::getPlatformRequirementFilter protected function
BaseCommand::getPreferredInstallOptions protected function Returns preferSource and preferDist values based on the configuration.
BaseCommand::getTerminalWidth protected function
BaseCommand::initialize protected function @inheritDoc Overrides Command::initialize 1
BaseCommand::isProxyCommand public function Whether or not this command is meant to call another command. 2
BaseCommand::normalizeRequirements protected function
BaseCommand::renderTable protected function
BaseCommand::requireComposer public function Retrieves the default Composer\Composer instance or throws
BaseCommand::resetComposer public function Removes the cached composer instance
BaseCommand::setComposer public function
BaseCommand::setIO public function
BaseCommand::tryComposer public function Retrieves the default Composer\Composer instance or null
Command::$aliases private property 1
Command::$application private property
Command::$code private property
Command::$definition private property
Command::$description private property 1
Command::$fullDefinition private property
Command::$help private property
Command::$helperSet private property
Command::$hidden private property
Command::$ignoreValidationErrors private property 2
Command::$name private property
Command::$processTitle private property
Command::$synopsis private property
Command::$usages private property
Command::addArgument public function Adds an argument. 2
Command::addOption public function Adds an option. 2
Command::addUsage public function Add a command usage example, it&#039;ll be prefixed with the command name. 2
Command::FAILURE public constant
Command::getAliases public function Returns the aliases for the command.
Command::getDefaultDescription public static function
Command::getDefaultName public static function
Command::getDefinition public function Gets the InputDefinition attached to this Command. 2
Command::getDescription public function Returns the description for the command.
Command::getHelp public function Returns the help for the command. 2
Command::getHelper public function Gets a helper instance by name. 2
Command::getHelperSet public function Gets the helper set. 1
Command::getName public function Returns the command name.
Command::getNativeDefinition public function Gets the InputDefinition to be used to create representations of this Command. 2
Command::getProcessedHelp public function Returns the processed help for the command replacing the %command.name% and
%command.full_name% patterns with the real values dynamically.
2
Command::getSynopsis public function Returns the synopsis for the command. 2
Command::getUsages public function Returns alternative usages of the command. 2
Command::ignoreValidationErrors public function Ignores validation errors. 2
Command::interact protected function Interacts with the user. 5
Command::INVALID public constant
Command::isEnabled public function Checks whether the command is enabled or not in the current environment. 2
Command::isHidden public function
Command::mergeApplicationDefinition public function Merges the application definition with the command definition. 2
Command::run public function Runs the command. 4
Command::setAliases public function Sets the aliases for the command.
Command::setApplication public function 2
Command::setCode public function Sets the code to execute when running this command. 2
Command::setDefinition public function Sets an array of argument and option instances. 2
Command::setDescription public function Sets the description for the command.
Command::setHelp public function Sets the help for the command. 2
Command::setHelperSet public function 2
Command::setHidden public function
Command::setName public function Sets the name of the command.
Command::setProcessTitle public function Sets the process title of the command. 2
Command::SUCCESS public constant
Command::validateName private function Validates a command name.
Command::__construct public function 15
DiagnoseCommand::$exitCode protected property @var int
DiagnoseCommand::$httpDownloader protected property @var HttpDownloader
DiagnoseCommand::$process protected property @var ProcessExecutor
DiagnoseCommand::checkComposerAudit private function
DiagnoseCommand::checkComposerLockSchema private function
DiagnoseCommand::checkComposerNetworkHttpEnablement private function Check if Composer network is enabled for HTTP/S
DiagnoseCommand::checkComposerRepo private function
DiagnoseCommand::checkComposerSchema private function
DiagnoseCommand::checkConnectivity private function Check if allow_url_fopen is ON
DiagnoseCommand::checkConnectivityAndComposerNetworkHttpEnablement private function
DiagnoseCommand::checkDiskSpace private function
DiagnoseCommand::checkGit private function
DiagnoseCommand::checkGithubOauth private function
DiagnoseCommand::checkHttp private function
DiagnoseCommand::checkHttpProxy private function
DiagnoseCommand::checkPlatform private function
DiagnoseCommand::checkPubKeys private function
DiagnoseCommand::checkVersion private function
DiagnoseCommand::configure protected function Configures the current command. Overrides Command::configure
DiagnoseCommand::execute protected function Executes the current command. Overrides Command::execute
DiagnoseCommand::getCurlVersion private function
DiagnoseCommand::getGithubRateLimit private function
DiagnoseCommand::outputResult private function

API Navigation

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