Merge branch 'diag'

main
Jordi Boggiano 11 years ago
commit bb9b6ddb4b

@ -348,6 +348,14 @@ performance.
autoloader. This is recommended especially for production, but can take
a bit of time to run so it is currently not done by default.
## diagnose
If you think you found a bug, or something is behaving strangely, you might
want to run the `diagnose` command to perform automated checks for many common
problems.
$ php composer.phar diagnose
## help
To get more information about a certain command, just use `help`.

@ -7,13 +7,16 @@ This is a list of common pitfalls on using Composer, and how to avoid them.
## General
1. When facing any kind of problems using Composer, be sure to **work with the
1. Before asking anyone, run [`composer diag`](../03-cli.md#diag) to check
for common problems. If it all checks out, proceed to the next steps.
2. When facing any kind of problems using Composer, be sure to **work with the
latest version**. See [self-update](../03-cli.md#self-update) for details.
2. Make sure you have no problems with your setup by running the installer's
3. Make sure you have no problems with your setup by running the installer's
checks via `curl -sS https://getcomposer.org/installer | php -- --check`.
3. Ensure you're **installing vendors straight from your `composer.json`** via
4. Ensure you're **installing vendors straight from your `composer.json`** via
`rm -rf vendor && composer update -v` when troubleshooting, excluding any
possible interferences with existing vendor installations or `composer.lock`
entries.

@ -0,0 +1,303 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Command;
use Composer\Composer;
use Composer\Factory;
use Composer\Downloader\TransportException;
use Composer\Util\ConfigValidator;
use Composer\Util\RemoteFilesystem;
use Composer\Util\StreamContextFactory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class DiagnoseCommand extends Command
{
protected $rfs;
protected $failures = 0;
protected function configure()
{
$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.
EOT
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->rfs = new RemoteFilesystem($this->getIO());
$output->write('Checking platform settings: ');
$this->outputResult($output, $this->checkPlatform());
$output->write('Checking http connectivity: ');
$this->outputResult($output, $this->checkHttp());
$opts = stream_context_get_options(StreamContextFactory::getContext());
if (!empty($opts['http']['proxy'])) {
$output->write('Checking HTTP proxy: ');
$this->outputResult($output, $this->checkHttpProxy());
}
$composer = $this->getComposer(false);
if ($composer) {
$output->write('Checking composer.json: ');
$this->outputResult($output, $this->checkComposerSchema());
}
if ($composer) {
$config = $composer->getConfig();
} else {
$config = Factory::createConfig();
}
if ($oauth = $config->get('github-oauth')) {
foreach ($oauth as $domain => $token) {
$output->write('Checking '.$domain.' oauth access: ');
$this->outputResult($output, $this->checkGithubOauth($domain, $token));
}
}
$output->write('Checking composer version: ');
$this->outputResult($output, $this->checkVersion());
return $this->failures;
}
private function checkComposerSchema()
{
$validator = new ConfigValidator($this->getIO());
list($errors, $publishErrors, $warnings) = $validator->validate(Factory::getComposerFile());
if ($errors || $publishErrors || $warnings) {
$messages = array(
'error' => array_merge($errors, $publishErrors),
'warning' => $warnings,
);
$output = '';
foreach ($messages as $style => $msgs) {
foreach ($msgs as $msg) {
$output .= '<' . $style . '>' . $msg . '</' . $style . '>';
}
}
return $output;
}
return true;
}
private function checkHttp()
{
$protocol = extension_loaded('openssl') ? 'https' : 'http';
try {
$json = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false);
} catch (\Exception $e) {
return $e;
}
return true;
}
private function checkHttpProxy()
{
$protocol = extension_loaded('openssl') ? 'https' : 'http';
try {
$json = json_decode($this->rfs->getContents('packagist.org', $protocol . '://packagist.org/packages.json', false), true);
$hash = reset($json['provider-includes']);
$hash = $hash['sha256'];
$path = str_replace('%hash%', $hash, key($json['provider-includes']));
$provider = $this->rfs->getContents('packagist.org', $protocol . '://packagist.org/'.$path, false);
if (hash('sha256', $provider) !== $hash) {
return 'It seems that your proxy is modifying http traffic on the fly';
}
} catch (\Exception $e) {
return $e;
}
return true;
}
private function checkGithubOauth($domain, $token)
{
$this->getIO()->setAuthentication($domain, $token, 'x-oauth-basic');
try {
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/user/repos' : 'https://'.$domain.'/api/v3/user/repos';
return $this->rfs->getContents($domain, $url, false) ? true : 'Unexpected error';
} catch (\Exception $e) {
if ($e instanceof TransportException && $e->getCode() === 401) {
return '<warning>The oauth token for '.$domain.' seems invalid, run "composer config --global --unset github-oauth.'.$domain.'" to remove it</warning>';
}
return $e;
}
}
private function checkVersion()
{
$protocol = extension_loaded('openssl') ? 'https' : 'http';
$latest = trim($this->rfs->getContents('getcomposer.org', $protocol . '://getcomposer.org/version', false));
if (Composer::VERSION !== $latest && Composer::VERSION !== '@package_version@') {
return '<warning>Your are not running the latest version</warning>';
}
return true;
}
private function outputResult(OutputInterface $output, $result)
{
if (true === $result) {
$output->writeln('<info>OK</info>');
} else {
$this->failures++;
$output->writeln('<error>FAIL</error>');
if ($result instanceof \Exception) {
$output->writeln('['.get_class($result).'] '.$result->getMessage());
} elseif ($result) {
$output->writeln($result);
}
}
}
private function checkPlatform()
{
$output = '';
$out = function ($msg, $style) use (&$output) {
$output .= '<'.$style.'>'.$msg.'</'.$style.'>';
};
// code below taken from getcomposer.org/installer, any changes should be made there and replicated here
$errors = array();
$warnings = array();
$iniPath = php_ini_loaded_file();
$displayIniMessage = false;
if ($iniPath) {
$iniMessage = PHP_EOL.PHP_EOL.'The php.ini used by your command-line PHP is: ' . $iniPath;
} else {
$iniMessage = PHP_EOL.PHP_EOL.'A php.ini file does not exist. You will have to create one.';
}
$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 (!ini_get('allow_url_fopen')) {
$errors['allow_url_fopen'] = true;
}
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
$errors['php'] = PHP_VERSION;
}
if (version_compare(PHP_VERSION, '5.3.4', '<')) {
$warnings['php'] = PHP_VERSION;
}
if (!extension_loaded('openssl')) {
$warnings['openssl'] = true;
}
if (ini_get('apc.enable_cli')) {
$warnings['apc_cli'] = true;
}
ob_start();
phpinfo(INFO_GENERAL);
$phpinfo = ob_get_clean();
if (preg_match('{Configure Command(?: *</td><td class="v">| *=> *)(.*?)(?:</td>|$)}m', $phpinfo, $match)) {
$configure = $match[1];
if (false !== strpos($configure, '--enable-sigchild')) {
$warnings['sigchild'] = true;
}
if (false !== strpos($configure, '--with-curlwrappers')) {
$warnings['curlwrappers'] = true;
}
}
if (!empty($errors)) {
foreach ($errors as $error => $current) {
switch ($error) {
case 'php':
$text = PHP_EOL."Your PHP ({$current}) is too old, you must upgrade to PHP 5.3.2 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;
}
if ($displayIniMessage) {
$text .= $iniMessage;
}
$out($text, 'error');
}
$out('');
}
if (!empty($warnings)) {
foreach ($warnings as $warning => $current) {
switch ($warning) {
case 'apc_cli':
$text = PHP_EOL."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 'sigchild':
$text = PHP_EOL."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_EOL."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':
$text = PHP_EOL."The openssl extension is missing, which will reduce the security and stability of Composer.".PHP_EOL;
$text .= "If possible you should enable it or recompile php with --with-openssl";
break;
case 'php':
$text = PHP_EOL."Your PHP ({$current}) is quite old, upgrading to PHP 5.3.4 or higher is recommended.".PHP_EOL;
$text .= "Composer works with 5.3.2+ for most people, but there might be edge case issues.";
break;
}
if ($displayIniMessage) {
$text .= $iniMessage;
}
$out($text, 'warning');
}
}
return !$warnings && !$errors ? true : $output;
}
}

@ -199,6 +199,7 @@ class Application extends BaseApplication
$commands[] = new Command\DumpAutoloadCommand();
$commands[] = new Command\StatusCommand();
$commands[] = new Command\ArchiveCommand();
$commands[] = new Command\DiagnoseCommand();
if ('phar:' === substr(__FILE__, 0, 5)) {
$commands[] = new Command\SelfUpdateCommand();

Loading…
Cancel
Save