Missing dependancies
This commit is contained in:
+428
@@ -0,0 +1,428 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\Differ\DiffConsoleFormatter;
|
||||
use PhpCsFixer\Differ\FullDiffer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
|
||||
use PhpCsFixer\Fixer\FixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\AliasedFixerOption;
|
||||
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
|
||||
use PhpCsFixer\FixerConfiguration\DeprecatedFixerOption;
|
||||
use PhpCsFixer\FixerDefinition\CodeSampleInterface;
|
||||
use PhpCsFixer\FixerDefinition\FileSpecificCodeSampleInterface;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSampleInterface;
|
||||
use PhpCsFixer\FixerFactory;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\RuleSet\RuleSets;
|
||||
use PhpCsFixer\StdinFileInfo;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Utils;
|
||||
use PhpCsFixer\WordMatcher;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'describe')]
|
||||
final class DescribeCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'describe';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $setNames;
|
||||
|
||||
private FixerFactory $fixerFactory;
|
||||
|
||||
/**
|
||||
* @var array<string, FixerInterface>
|
||||
*/
|
||||
private $fixers;
|
||||
|
||||
public function __construct(?FixerFactory $fixerFactory = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (null === $fixerFactory) {
|
||||
$fixerFactory = new FixerFactory();
|
||||
$fixerFactory->registerBuiltInFixers();
|
||||
}
|
||||
|
||||
$this->fixerFactory = $fixerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition(
|
||||
[
|
||||
new InputArgument('name', InputArgument::REQUIRED, 'Name of rule / set.'),
|
||||
]
|
||||
)
|
||||
->setDescription('Describe rule / ruleset.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity() && $output instanceof ConsoleOutputInterface) {
|
||||
$stdErr = $output->getErrorOutput();
|
||||
$stdErr->writeln($this->getApplication()->getLongVersion());
|
||||
}
|
||||
|
||||
$name = $input->getArgument('name');
|
||||
|
||||
try {
|
||||
if (str_starts_with($name, '@')) {
|
||||
$this->describeSet($output, $name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->describeRule($output, $name);
|
||||
} catch (DescribeNameNotFoundException $e) {
|
||||
$matcher = new WordMatcher(
|
||||
'set' === $e->getType() ? $this->getSetNames() : array_keys($this->getFixers())
|
||||
);
|
||||
|
||||
$alternative = $matcher->match($name);
|
||||
|
||||
$this->describeList($output, $e->getType());
|
||||
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'%s "%s" not found.%s',
|
||||
ucfirst($e->getType()),
|
||||
$name,
|
||||
null === $alternative ? '' : ' Did you mean "'.$alternative.'"?'
|
||||
));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function describeRule(OutputInterface $output, string $name): void
|
||||
{
|
||||
$fixers = $this->getFixers();
|
||||
|
||||
if (!isset($fixers[$name])) {
|
||||
throw new DescribeNameNotFoundException($name, 'rule');
|
||||
}
|
||||
|
||||
/** @var FixerInterface $fixer */
|
||||
$fixer = $fixers[$name];
|
||||
|
||||
$definition = $fixer->getDefinition();
|
||||
|
||||
$summary = $definition->getSummary();
|
||||
|
||||
if ($fixer instanceof DeprecatedFixerInterface) {
|
||||
$successors = $fixer->getSuccessorsNames();
|
||||
$message = [] === $successors
|
||||
? 'will be removed on next major version'
|
||||
: sprintf('use %s instead', Utils::naturalLanguageJoinWithBackticks($successors));
|
||||
$message = Preg::replace('/(`.+?`)/', '<info>$1</info>', $message);
|
||||
$summary .= sprintf(' <error>DEPRECATED</error>: %s.', $message);
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('<info>Description of</info> %s <info>rule</info>.', $name));
|
||||
|
||||
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$output->writeln(sprintf('Fixer class: <comment>%s</comment>.', \get_class($fixer)));
|
||||
}
|
||||
|
||||
$output->writeln($summary);
|
||||
|
||||
$description = $definition->getDescription();
|
||||
|
||||
if (null !== $description) {
|
||||
$output->writeln($description);
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
|
||||
if ($fixer->isRisky()) {
|
||||
$output->writeln('<error>Fixer applying this rule is risky.</error>');
|
||||
|
||||
$riskyDescription = $definition->getRiskyDescription();
|
||||
|
||||
if (null !== $riskyDescription) {
|
||||
$output->writeln($riskyDescription);
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
}
|
||||
|
||||
if ($fixer instanceof ConfigurableFixerInterface) {
|
||||
$configurationDefinition = $fixer->getConfigurationDefinition();
|
||||
$options = $configurationDefinition->getOptions();
|
||||
|
||||
$output->writeln(sprintf('Fixer is configurable using following option%s:', 1 === \count($options) ? '' : 's'));
|
||||
|
||||
foreach ($options as $option) {
|
||||
$line = '* <info>'.OutputFormatter::escape($option->getName()).'</info>';
|
||||
$allowed = HelpCommand::getDisplayableAllowedValues($option);
|
||||
|
||||
if (null === $allowed) {
|
||||
$allowed = array_map(
|
||||
static fn (string $type): string => '<comment>'.$type.'</comment>',
|
||||
$option->getAllowedTypes(),
|
||||
);
|
||||
} else {
|
||||
$allowed = array_map(static function ($value): string {
|
||||
return $value instanceof AllowedValueSubset
|
||||
? 'a subset of <comment>'.HelpCommand::toString($value->getAllowedValues()).'</comment>'
|
||||
: '<comment>'.HelpCommand::toString($value).'</comment>';
|
||||
}, $allowed);
|
||||
}
|
||||
|
||||
$line .= ' ('.implode(', ', $allowed).')';
|
||||
|
||||
$description = Preg::replace('/(`.+?`)/', '<info>$1</info>', OutputFormatter::escape($option->getDescription()));
|
||||
$line .= ': '.lcfirst(Preg::replace('/\.$/', '', $description)).'; ';
|
||||
|
||||
if ($option->hasDefault()) {
|
||||
$line .= sprintf(
|
||||
'defaults to <comment>%s</comment>',
|
||||
HelpCommand::toString($option->getDefault())
|
||||
);
|
||||
} else {
|
||||
$line .= '<comment>required</comment>';
|
||||
}
|
||||
|
||||
if ($option instanceof DeprecatedFixerOption) {
|
||||
$line .= '. <error>DEPRECATED</error>: '.Preg::replace(
|
||||
'/(`.+?`)/',
|
||||
'<info>$1</info>',
|
||||
OutputFormatter::escape(lcfirst($option->getDeprecationMessage()))
|
||||
);
|
||||
}
|
||||
|
||||
if ($option instanceof AliasedFixerOption) {
|
||||
$line .= '; <error>DEPRECATED</error> alias: <comment>'.$option->getAlias().'</comment>';
|
||||
}
|
||||
|
||||
$output->writeln($line);
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
}
|
||||
|
||||
/** @var CodeSampleInterface[] $codeSamples */
|
||||
$codeSamples = array_filter($definition->getCodeSamples(), static function (CodeSampleInterface $codeSample): bool {
|
||||
if ($codeSample instanceof VersionSpecificCodeSampleInterface) {
|
||||
return $codeSample->isSuitableFor(\PHP_VERSION_ID);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (0 === \count($codeSamples)) {
|
||||
$output->writeln([
|
||||
'Fixing examples cannot be demonstrated on the current PHP version.',
|
||||
'',
|
||||
]);
|
||||
} else {
|
||||
$output->writeln('Fixing examples:');
|
||||
|
||||
$differ = new FullDiffer();
|
||||
$diffFormatter = new DiffConsoleFormatter(
|
||||
$output->isDecorated(),
|
||||
sprintf(
|
||||
'<comment> ---------- begin diff ----------</comment>%s%%s%s<comment> ----------- end diff -----------</comment>',
|
||||
PHP_EOL,
|
||||
PHP_EOL
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($codeSamples as $index => $codeSample) {
|
||||
$old = $codeSample->getCode();
|
||||
$tokens = Tokens::fromCode($old);
|
||||
|
||||
$configuration = $codeSample->getConfiguration();
|
||||
|
||||
if ($fixer instanceof ConfigurableFixerInterface) {
|
||||
$fixer->configure($configuration ?? []);
|
||||
}
|
||||
|
||||
$file = $codeSample instanceof FileSpecificCodeSampleInterface
|
||||
? $codeSample->getSplFileInfo()
|
||||
: new StdinFileInfo();
|
||||
|
||||
$fixer->fix($file, $tokens);
|
||||
|
||||
$diff = $differ->diff($old, $tokens->generateCode());
|
||||
|
||||
if ($fixer instanceof ConfigurableFixerInterface) {
|
||||
if (null === $configuration) {
|
||||
$output->writeln(sprintf(' * Example #%d. Fixing with the <comment>default</comment> configuration.', $index + 1));
|
||||
} else {
|
||||
$output->writeln(sprintf(' * Example #%d. Fixing with configuration: <comment>%s</comment>.', $index + 1, HelpCommand::toString($codeSample->getConfiguration())));
|
||||
}
|
||||
} else {
|
||||
$output->writeln(sprintf(' * Example #%d.', $index + 1));
|
||||
}
|
||||
|
||||
$output->writeln([$diffFormatter->format($diff, ' %s'), '']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function describeSet(OutputInterface $output, string $name): void
|
||||
{
|
||||
if (!\in_array($name, $this->getSetNames(), true)) {
|
||||
throw new DescribeNameNotFoundException($name, 'set');
|
||||
}
|
||||
|
||||
$ruleSetDefinitions = RuleSets::getSetDefinitions();
|
||||
$fixers = $this->getFixers();
|
||||
|
||||
$output->writeln(sprintf('<info>Description of the</info> %s <info>set.</info>', $ruleSetDefinitions[$name]->getName()));
|
||||
$output->writeln($this->replaceRstLinks($ruleSetDefinitions[$name]->getDescription()));
|
||||
|
||||
if ($ruleSetDefinitions[$name]->isRisky()) {
|
||||
$output->writeln('This set contains <error>risky</error> rules.');
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
|
||||
$help = '';
|
||||
|
||||
foreach ($ruleSetDefinitions[$name]->getRules() as $rule => $config) {
|
||||
if (str_starts_with($rule, '@')) {
|
||||
$set = $ruleSetDefinitions[$rule];
|
||||
$help .= sprintf(
|
||||
" * <info>%s</info>%s\n | %s\n\n",
|
||||
$rule,
|
||||
$set->isRisky() ? ' <error>risky</error>' : '',
|
||||
$this->replaceRstLinks($set->getDescription())
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var FixerInterface $fixer */
|
||||
$fixer = $fixers[$rule];
|
||||
|
||||
$definition = $fixer->getDefinition();
|
||||
$help .= sprintf(
|
||||
" * <info>%s</info>%s\n | %s\n%s\n",
|
||||
$rule,
|
||||
$fixer->isRisky() ? ' <error>risky</error>' : '',
|
||||
$definition->getSummary(),
|
||||
true !== $config ? sprintf(" <comment>| Configuration: %s</comment>\n", HelpCommand::toString($config)) : ''
|
||||
);
|
||||
}
|
||||
|
||||
$output->write($help);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, FixerInterface>
|
||||
*/
|
||||
private function getFixers(): array
|
||||
{
|
||||
if (null !== $this->fixers) {
|
||||
return $this->fixers;
|
||||
}
|
||||
|
||||
$fixers = [];
|
||||
|
||||
foreach ($this->fixerFactory->getFixers() as $fixer) {
|
||||
$fixers[$fixer->getName()] = $fixer;
|
||||
}
|
||||
|
||||
$this->fixers = $fixers;
|
||||
ksort($this->fixers);
|
||||
|
||||
return $this->fixers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getSetNames(): array
|
||||
{
|
||||
if (null !== $this->setNames) {
|
||||
return $this->setNames;
|
||||
}
|
||||
|
||||
$this->setNames = RuleSets::getSetDefinitionNames();
|
||||
|
||||
return $this->setNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type 'rule'|'set'
|
||||
*/
|
||||
private function describeList(OutputInterface $output, string $type): void
|
||||
{
|
||||
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) {
|
||||
$describe = [
|
||||
'sets' => $this->getSetNames(),
|
||||
'rules' => $this->getFixers(),
|
||||
];
|
||||
} elseif ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
|
||||
$describe = 'set' === $type ? ['sets' => $this->getSetNames()] : ['rules' => $this->getFixers()];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var string[] $items */
|
||||
foreach ($describe as $list => $items) {
|
||||
$output->writeln(sprintf('<comment>Defined %s:</comment>', $list));
|
||||
|
||||
foreach ($items as $name => $item) {
|
||||
$output->writeln(sprintf('* <info>%s</info>', \is_string($name) ? $name : $item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function replaceRstLinks(string $content): string
|
||||
{
|
||||
return Preg::replaceCallback(
|
||||
'/(`[^<]+<[^>]+>`_)/',
|
||||
static function (array $matches) {
|
||||
return Preg::replaceCallback(
|
||||
'/`(.*)<(.*)>`_/',
|
||||
static function (array $matches): string {
|
||||
return $matches[1].'('.$matches[2].')';
|
||||
},
|
||||
$matches[1]
|
||||
);
|
||||
},
|
||||
$content
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DescribeNameNotFoundException extends \InvalidArgumentException
|
||||
{
|
||||
private string $name;
|
||||
|
||||
/**
|
||||
* 'rule'|'set'.
|
||||
*/
|
||||
private string $type;
|
||||
|
||||
public function __construct(string $name, string $type)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\Documentation\DocumentationLocator;
|
||||
use PhpCsFixer\Documentation\FixerDocumentGenerator;
|
||||
use PhpCsFixer\Documentation\ListDocumentGenerator;
|
||||
use PhpCsFixer\Documentation\RuleSetDocumentationGenerator;
|
||||
use PhpCsFixer\FixerFactory;
|
||||
use PhpCsFixer\RuleSet\RuleSets;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'documentation')]
|
||||
final class DocumentationCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'documentation';
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setAliases(['doc'])
|
||||
->setDescription('Dumps the documentation of the project into its "/doc" directory.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$locator = new DocumentationLocator();
|
||||
|
||||
$fixerFactory = new FixerFactory();
|
||||
$fixerFactory->registerBuiltInFixers();
|
||||
$fixers = $fixerFactory->getFixers();
|
||||
|
||||
$setDefinitions = RuleSets::getSetDefinitions();
|
||||
|
||||
$fixerDocumentGenerator = new FixerDocumentGenerator($locator);
|
||||
$ruleSetDocumentationGenerator = new RuleSetDocumentationGenerator($locator);
|
||||
$listDocumentGenerator = new ListDocumentGenerator($locator);
|
||||
|
||||
// Array of existing fixer docs.
|
||||
// We first override existing files, and then we will delete files that are no longer needed.
|
||||
// We cannot remove all files first, as generation of docs is re-using existing docs to extract code-samples for
|
||||
// VersionSpecificCodeSample under incompatible PHP version.
|
||||
$docForFixerRelativePaths = [];
|
||||
|
||||
foreach ($fixers as $fixer) {
|
||||
$docForFixerRelativePaths[] = $locator->getFixerDocumentationFileRelativePath($fixer);
|
||||
$filesystem->dumpFile(
|
||||
$locator->getFixerDocumentationFilePath($fixer),
|
||||
$fixerDocumentGenerator->generateFixerDocumentation($fixer)
|
||||
);
|
||||
}
|
||||
|
||||
/** @var SplFileInfo $file */
|
||||
foreach (
|
||||
(new Finder())->files()
|
||||
->in($locator->getFixersDocumentationDirectoryPath())
|
||||
->notPath($docForFixerRelativePaths) as $file
|
||||
) {
|
||||
$filesystem->remove($file->getPathname());
|
||||
}
|
||||
|
||||
// Fixer doc. index
|
||||
|
||||
$filesystem->dumpFile(
|
||||
$locator->getFixersDocumentationIndexFilePath(),
|
||||
$fixerDocumentGenerator->generateFixersDocumentationIndex($fixers)
|
||||
);
|
||||
|
||||
// RuleSet docs.
|
||||
|
||||
/** @var SplFileInfo $file */
|
||||
foreach ((new Finder())->files()->in($locator->getRuleSetsDocumentationDirectoryPath()) as $file) {
|
||||
$filesystem->remove($file->getPathname());
|
||||
}
|
||||
|
||||
$paths = [];
|
||||
|
||||
foreach ($setDefinitions as $name => $definition) {
|
||||
$path = $locator->getRuleSetsDocumentationFilePath($name);
|
||||
$paths[$name] = $path;
|
||||
$filesystem->dumpFile($path, $ruleSetDocumentationGenerator->generateRuleSetsDocumentation($definition, $fixers));
|
||||
}
|
||||
|
||||
// RuleSet doc. index
|
||||
|
||||
$filesystem->dumpFile(
|
||||
$locator->getRuleSetsDocumentationIndexFilePath(),
|
||||
$ruleSetDocumentationGenerator->generateRuleSetsDocumentationIndex($paths)
|
||||
);
|
||||
|
||||
// List file / Appendix
|
||||
|
||||
$filesystem->dumpFile(
|
||||
$locator->getListingFilePath(),
|
||||
$listDocumentGenerator->generateListingDocumentation($fixers)
|
||||
);
|
||||
|
||||
$output->writeln('Docs updated.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\Config;
|
||||
use PhpCsFixer\ConfigInterface;
|
||||
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
|
||||
use PhpCsFixer\Console\ConfigurationResolver;
|
||||
use PhpCsFixer\Console\Output\ErrorOutput;
|
||||
use PhpCsFixer\Console\Output\NullOutput;
|
||||
use PhpCsFixer\Console\Output\ProcessOutput;
|
||||
use PhpCsFixer\Console\Report\FixReport\ReportSummary;
|
||||
use PhpCsFixer\Error\ErrorsManager;
|
||||
use PhpCsFixer\Runner\Runner;
|
||||
use PhpCsFixer\ToolInfoInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Terminal;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'fix')]
|
||||
final class FixCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'fix';
|
||||
|
||||
private EventDispatcherInterface $eventDispatcher;
|
||||
|
||||
private ErrorsManager $errorsManager;
|
||||
|
||||
private Stopwatch $stopwatch;
|
||||
|
||||
private ConfigInterface $defaultConfig;
|
||||
|
||||
private ToolInfoInterface $toolInfo;
|
||||
|
||||
public function __construct(ToolInfoInterface $toolInfo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->eventDispatcher = new EventDispatcher();
|
||||
$this->errorsManager = new ErrorsManager();
|
||||
$this->stopwatch = new Stopwatch();
|
||||
$this->defaultConfig = new Config();
|
||||
$this->toolInfo = $toolInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Override here to only generate the help copy when used.
|
||||
*/
|
||||
public function getHelp(): string
|
||||
{
|
||||
return <<<'EOF'
|
||||
The <info>%command.name%</info> command tries to fix as much coding standards
|
||||
problems as possible on a given file or files in a given directory and its subdirectories:
|
||||
|
||||
<info>$ php %command.full_name% /path/to/dir</info>
|
||||
<info>$ php %command.full_name% /path/to/file</info>
|
||||
|
||||
By default <comment>--path-mode</comment> is set to `override`, which means, that if you specify the path to a file or a directory via
|
||||
command arguments, then the paths provided to a `Finder` in config file will be ignored. You can use <comment>--path-mode=intersection</comment>
|
||||
to merge paths from the config file and from the argument:
|
||||
|
||||
<info>$ php %command.full_name% --path-mode=intersection /path/to/dir</info>
|
||||
|
||||
The <comment>--format</comment> option for the output format. Supported formats are `txt` (default one), `json`, `xml`, `checkstyle`, `junit` and `gitlab`.
|
||||
|
||||
NOTE: the output for the following formats are generated in accordance with schemas
|
||||
|
||||
* `checkstyle` follows the common `"checkstyle" XML schema </doc/schemas/fix/checkstyle.xsd>`_
|
||||
* `json` follows the `own JSON schema </doc/schemas/fix/schema.json>`_
|
||||
* `junit` follows the `JUnit XML schema from Jenkins </doc/schemas/fix/junit-10.xsd>`_
|
||||
* `xml` follows the `own XML schema </doc/schemas/fix/xml.xsd>`_
|
||||
|
||||
The <comment>--quiet</comment> Do not output any message.
|
||||
|
||||
The <comment>--verbose</comment> option will show the applied rules. When using the `txt` format it will also display progress notifications.
|
||||
|
||||
NOTE: if there is an error like "errors reported during linting after fixing", you can use this to be even more verbose for debugging purpose
|
||||
|
||||
* `-v`: verbose
|
||||
* `-vv`: very verbose
|
||||
* `-vvv`: debug
|
||||
|
||||
The <comment>--rules</comment> option limits the rules to apply to the
|
||||
project:
|
||||
|
||||
EOF. /* @TODO: 4.0 - change to @PER */ <<<'EOF'
|
||||
|
||||
<info>$ php %command.full_name% /path/to/project --rules=@PSR12</info>
|
||||
|
||||
By default the PSR-12 rules are used.
|
||||
|
||||
The <comment>--rules</comment> option lets you choose the exact rules to
|
||||
apply (the rule names must be separated by a comma):
|
||||
|
||||
<info>$ php %command.full_name% /path/to/dir --rules=line_ending,full_opening_tag,indentation_type</info>
|
||||
|
||||
You can also exclude the rules you don't want by placing a dash in front of the rule name, if this is more convenient,
|
||||
using <comment>-name_of_fixer</comment>:
|
||||
|
||||
<info>$ php %command.full_name% /path/to/dir --rules=-full_opening_tag,-indentation_type</info>
|
||||
|
||||
When using combinations of exact and exclude rules, applying exact rules along with above excluded results:
|
||||
|
||||
<info>$ php %command.full_name% /path/to/project --rules=@Symfony,-@PSR1,-blank_line_before_statement,strict_comparison</info>
|
||||
|
||||
Complete configuration for rules can be supplied using a `json` formatted string.
|
||||
|
||||
<info>$ php %command.full_name% /path/to/project --rules='{"concat_space": {"spacing": "none"}}'</info>
|
||||
|
||||
The <comment>--dry-run</comment> flag will run the fixer without making changes to your files.
|
||||
|
||||
The <comment>--diff</comment> flag can be used to let the fixer output all the changes it makes.
|
||||
|
||||
The <comment>--allow-risky</comment> option (pass `yes` or `no`) allows you to set whether risky rules may run. Default value is taken from config file.
|
||||
A rule is considered risky if it could change code behaviour. By default no risky rules are run.
|
||||
|
||||
The <comment>--stop-on-violation</comment> flag stops the execution upon first file that needs to be fixed.
|
||||
|
||||
The <comment>--show-progress</comment> option allows you to choose the way process progress is rendered:
|
||||
|
||||
* <comment>none</comment>: disables progress output;
|
||||
* <comment>dots</comment>: multiline progress output with number of files and percentage on each line.
|
||||
|
||||
If the option is not provided, it defaults to <comment>dots</comment> unless a config file that disables output is used, in which case it defaults to <comment>none</comment>. This option has no effect if the verbosity of the command is less than <comment>verbose</comment>.
|
||||
|
||||
<info>$ php %command.full_name% --verbose --show-progress=dots</info>
|
||||
|
||||
By using <command>--using-cache</command> option with `yes` or `no` you can set if the caching
|
||||
mechanism should be used.
|
||||
|
||||
The command can also read from standard input, in which case it won't
|
||||
automatically fix anything:
|
||||
|
||||
<info>$ cat foo.php | php %command.full_name% --diff -</info>
|
||||
|
||||
Finally, if you don't need BC kept on CLI level, you might use `PHP_CS_FIXER_FUTURE_MODE` to start using options that
|
||||
would be default in next MAJOR release and to forbid using deprecated configuration:
|
||||
|
||||
<info>$ PHP_CS_FIXER_FUTURE_MODE=1 php %command.full_name% -v --diff</info>
|
||||
|
||||
Exit code
|
||||
---------
|
||||
|
||||
Exit code of the fix command is built using following bit flags:
|
||||
|
||||
* 0 - OK.
|
||||
* 1 - General error (or PHP minimal requirement not matched).
|
||||
* 4 - Some files have invalid syntax (only in dry-run mode).
|
||||
* 8 - Some files need fixing (only in dry-run mode).
|
||||
* 16 - Configuration error of the application.
|
||||
* 32 - Configuration error of a Fixer.
|
||||
* 64 - Exception raised within the application.
|
||||
|
||||
EOF
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition(
|
||||
[
|
||||
new InputArgument('path', InputArgument::IS_ARRAY, 'The path.'),
|
||||
new InputOption('path-mode', '', InputOption::VALUE_REQUIRED, 'Specify path mode (can be override or intersection).', ConfigurationResolver::PATH_MODE_OVERRIDE),
|
||||
new InputOption('allow-risky', '', InputOption::VALUE_REQUIRED, 'Are risky fixers allowed (can be yes or no).'),
|
||||
new InputOption('config', '', InputOption::VALUE_REQUIRED, 'The path to a .php-cs-fixer.php file.'),
|
||||
new InputOption('dry-run', '', InputOption::VALUE_NONE, 'Only shows which files would have been modified.'),
|
||||
new InputOption('rules', '', InputOption::VALUE_REQUIRED, 'The rules.'),
|
||||
new InputOption('using-cache', '', InputOption::VALUE_REQUIRED, 'Does cache should be used (can be yes or no).'),
|
||||
new InputOption('cache-file', '', InputOption::VALUE_REQUIRED, 'The path to the cache file.'),
|
||||
new InputOption('diff', '', InputOption::VALUE_NONE, 'Also produce diff for each file.'),
|
||||
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'To output results in other formats.'),
|
||||
new InputOption('stop-on-violation', '', InputOption::VALUE_NONE, 'Stop execution on first violation.'),
|
||||
new InputOption('show-progress', '', InputOption::VALUE_REQUIRED, 'Type of progress indicator (none, dots).'),
|
||||
]
|
||||
)
|
||||
->setDescription('Fixes a directory or a file.')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$verbosity = $output->getVerbosity();
|
||||
|
||||
$passedConfig = $input->getOption('config');
|
||||
$passedRules = $input->getOption('rules');
|
||||
|
||||
if (null !== $passedConfig && null !== $passedRules) {
|
||||
throw new InvalidConfigurationException('Passing both `--config` and `--rules` options is not allowed.');
|
||||
}
|
||||
|
||||
$resolver = new ConfigurationResolver(
|
||||
$this->defaultConfig,
|
||||
[
|
||||
'allow-risky' => $input->getOption('allow-risky'),
|
||||
'config' => $passedConfig,
|
||||
'dry-run' => $input->getOption('dry-run'),
|
||||
'rules' => $passedRules,
|
||||
'path' => $input->getArgument('path'),
|
||||
'path-mode' => $input->getOption('path-mode'),
|
||||
'using-cache' => $input->getOption('using-cache'),
|
||||
'cache-file' => $input->getOption('cache-file'),
|
||||
'format' => $input->getOption('format'),
|
||||
'diff' => $input->getOption('diff'),
|
||||
'stop-on-violation' => $input->getOption('stop-on-violation'),
|
||||
'verbosity' => $verbosity,
|
||||
'show-progress' => $input->getOption('show-progress'),
|
||||
],
|
||||
getcwd(),
|
||||
$this->toolInfo
|
||||
);
|
||||
|
||||
$reporter = $resolver->getReporter();
|
||||
|
||||
$stdErr = $output instanceof ConsoleOutputInterface
|
||||
? $output->getErrorOutput()
|
||||
: ('txt' === $reporter->getFormat() ? $output : null)
|
||||
;
|
||||
|
||||
if (null !== $stdErr) {
|
||||
if (OutputInterface::VERBOSITY_VERBOSE <= $verbosity) {
|
||||
$stdErr->writeln($this->getApplication()->getLongVersion());
|
||||
}
|
||||
|
||||
$configFile = $resolver->getConfigFile();
|
||||
$stdErr->writeln(sprintf('Loaded config <comment>%s</comment>%s.', $resolver->getConfig()->getName(), null === $configFile ? '' : ' from "'.$configFile.'"'));
|
||||
|
||||
if ($resolver->getUsingCache()) {
|
||||
$cacheFile = $resolver->getCacheFile();
|
||||
|
||||
if (is_file($cacheFile)) {
|
||||
$stdErr->writeln(sprintf('Using cache file "%s".', $cacheFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$progressType = $resolver->getProgress();
|
||||
$finder = $resolver->getFinder();
|
||||
|
||||
if (null !== $stdErr && $resolver->configFinderIsOverridden()) {
|
||||
$stdErr->writeln(
|
||||
sprintf($stdErr->isDecorated() ? '<bg=yellow;fg=black;>%s</>' : '%s', 'Paths from configuration file have been overridden by paths provided as command arguments.')
|
||||
);
|
||||
}
|
||||
|
||||
if ('none' === $progressType || null === $stdErr) {
|
||||
$progressOutput = new NullOutput();
|
||||
} else {
|
||||
$finder = new \ArrayIterator(iterator_to_array($finder));
|
||||
$progressOutput = new ProcessOutput(
|
||||
$stdErr,
|
||||
$this->eventDispatcher,
|
||||
(new Terminal())->getWidth(),
|
||||
\count($finder)
|
||||
);
|
||||
}
|
||||
|
||||
$runner = new Runner(
|
||||
$finder,
|
||||
$resolver->getFixers(),
|
||||
$resolver->getDiffer(),
|
||||
'none' !== $progressType ? $this->eventDispatcher : null,
|
||||
$this->errorsManager,
|
||||
$resolver->getLinter(),
|
||||
$resolver->isDryRun(),
|
||||
$resolver->getCacheManager(),
|
||||
$resolver->getDirectory(),
|
||||
$resolver->shouldStopOnViolation()
|
||||
);
|
||||
|
||||
$this->stopwatch->start('fixFiles');
|
||||
$changed = $runner->fix();
|
||||
$this->stopwatch->stop('fixFiles');
|
||||
|
||||
$progressOutput->printLegend();
|
||||
|
||||
$fixEvent = $this->stopwatch->getEvent('fixFiles');
|
||||
|
||||
$reportSummary = new ReportSummary(
|
||||
$changed,
|
||||
$fixEvent->getDuration(),
|
||||
$fixEvent->getMemory(),
|
||||
OutputInterface::VERBOSITY_VERBOSE <= $verbosity,
|
||||
$resolver->isDryRun(),
|
||||
$output->isDecorated()
|
||||
);
|
||||
|
||||
$output->isDecorated()
|
||||
? $output->write($reporter->generate($reportSummary))
|
||||
: $output->write($reporter->generate($reportSummary), false, OutputInterface::OUTPUT_RAW)
|
||||
;
|
||||
|
||||
$invalidErrors = $this->errorsManager->getInvalidErrors();
|
||||
$exceptionErrors = $this->errorsManager->getExceptionErrors();
|
||||
$lintErrors = $this->errorsManager->getLintErrors();
|
||||
|
||||
if (null !== $stdErr) {
|
||||
$errorOutput = new ErrorOutput($stdErr);
|
||||
|
||||
if (\count($invalidErrors) > 0) {
|
||||
$errorOutput->listErrors('linting before fixing', $invalidErrors);
|
||||
}
|
||||
|
||||
if (\count($exceptionErrors) > 0) {
|
||||
$errorOutput->listErrors('fixing', $exceptionErrors);
|
||||
}
|
||||
|
||||
if (\count($lintErrors) > 0) {
|
||||
$errorOutput->listErrors('linting after fixing', $lintErrors);
|
||||
}
|
||||
}
|
||||
|
||||
$exitStatusCalculator = new FixCommandExitStatusCalculator();
|
||||
|
||||
return $exitStatusCalculator->calculate(
|
||||
$resolver->isDryRun(),
|
||||
\count($changed) > 0,
|
||||
\count($invalidErrors) > 0,
|
||||
\count($exceptionErrors) > 0,
|
||||
\count($lintErrors) > 0
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class FixCommandExitStatusCalculator
|
||||
{
|
||||
// Exit status 1 is reserved for environment constraints not matched.
|
||||
public const EXIT_STATUS_FLAG_HAS_INVALID_FILES = 4;
|
||||
public const EXIT_STATUS_FLAG_HAS_CHANGED_FILES = 8;
|
||||
public const EXIT_STATUS_FLAG_HAS_INVALID_CONFIG = 16;
|
||||
public const EXIT_STATUS_FLAG_HAS_INVALID_FIXER_CONFIG = 32;
|
||||
public const EXIT_STATUS_FLAG_EXCEPTION_IN_APP = 64;
|
||||
|
||||
public function calculate(bool $isDryRun, bool $hasChangedFiles, bool $hasInvalidErrors, bool $hasExceptionErrors, bool $hasLintErrorsAfterFixing): int
|
||||
{
|
||||
$exitStatus = 0;
|
||||
|
||||
if ($isDryRun) {
|
||||
if ($hasChangedFiles) {
|
||||
$exitStatus |= self::EXIT_STATUS_FLAG_HAS_CHANGED_FILES;
|
||||
}
|
||||
|
||||
if ($hasInvalidErrors) {
|
||||
$exitStatus |= self::EXIT_STATUS_FLAG_HAS_INVALID_FILES;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasExceptionErrors || $hasLintErrorsAfterFixing) {
|
||||
$exitStatus |= self::EXIT_STATUS_FLAG_EXCEPTION_IN_APP;
|
||||
}
|
||||
|
||||
return $exitStatus;
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\HelpCommand as BaseHelpCommand;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'help')]
|
||||
final class HelpCommand extends BaseHelpCommand
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'help';
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function toString($value): string
|
||||
{
|
||||
return \is_array($value)
|
||||
? static::arrayToString($value)
|
||||
: static::scalarToString($value)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the allowed values of the given option that can be converted to a string.
|
||||
*
|
||||
* @return null|list<AllowedValueSubset|mixed>
|
||||
*/
|
||||
public static function getDisplayableAllowedValues(FixerOptionInterface $option): ?array
|
||||
{
|
||||
$allowed = $option->getAllowedValues();
|
||||
|
||||
if (null !== $allowed) {
|
||||
$allowed = array_filter($allowed, static function ($value): bool {
|
||||
return !$value instanceof \Closure;
|
||||
});
|
||||
|
||||
usort($allowed, static function ($valueA, $valueB): int {
|
||||
if ($valueA instanceof AllowedValueSubset) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($valueB instanceof AllowedValueSubset) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return strcasecmp(
|
||||
self::toString($valueA),
|
||||
self::toString($valueB)
|
||||
);
|
||||
});
|
||||
|
||||
if (0 === \count($allowed)) {
|
||||
$allowed = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initialize(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$output->getFormatter()->setStyle('url', new OutputFormatterStyle('blue'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private static function scalarToString($value): string
|
||||
{
|
||||
$str = var_export($value, true);
|
||||
|
||||
return Preg::replace('/\bNULL\b/', 'null', $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $value
|
||||
*/
|
||||
private static function arrayToString(array $value): string
|
||||
{
|
||||
if (0 === \count($value)) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
$isHash = !array_is_list($value);
|
||||
$str = '[';
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if ($isHash) {
|
||||
$str .= static::scalarToString($k).' => ';
|
||||
}
|
||||
|
||||
$str .= \is_array($v)
|
||||
? static::arrayToString($v).', '
|
||||
: static::scalarToString($v).', '
|
||||
;
|
||||
}
|
||||
|
||||
return substr($str, 0, -2).']';
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\Config;
|
||||
use PhpCsFixer\ConfigInterface;
|
||||
use PhpCsFixer\Console\ConfigurationResolver;
|
||||
use PhpCsFixer\ToolInfoInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Markus Staab <markus.staab@redaxo.org>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'list-files')]
|
||||
final class ListFilesCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'list-files';
|
||||
|
||||
private ConfigInterface $defaultConfig;
|
||||
|
||||
private ToolInfoInterface $toolInfo;
|
||||
|
||||
public function __construct(ToolInfoInterface $toolInfo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->defaultConfig = new Config();
|
||||
$this->toolInfo = $toolInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition(
|
||||
[
|
||||
new InputOption('config', '', InputOption::VALUE_REQUIRED, 'The path to a .php-cs-fixer.php file.'),
|
||||
]
|
||||
)
|
||||
->setDescription('List all files being fixed by the given config.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$passedConfig = $input->getOption('config');
|
||||
$cwd = getcwd();
|
||||
|
||||
$resolver = new ConfigurationResolver(
|
||||
$this->defaultConfig,
|
||||
[
|
||||
'config' => $passedConfig,
|
||||
],
|
||||
$cwd,
|
||||
$this->toolInfo
|
||||
);
|
||||
|
||||
$finder = $resolver->getFinder();
|
||||
|
||||
/** @var \SplFileInfo $file */
|
||||
foreach ($finder as $file) {
|
||||
if ($file->isFile()) {
|
||||
$relativePath = str_replace($cwd, '.', $file->getRealPath());
|
||||
// unify directory separators across operating system
|
||||
$relativePath = str_replace('/', \DIRECTORY_SEPARATOR, $relativePath);
|
||||
|
||||
$output->writeln(escapeshellarg($relativePath));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\ConfigurationException\InvalidConfigurationException;
|
||||
use PhpCsFixer\Console\Report\ListSetsReport\ReporterFactory;
|
||||
use PhpCsFixer\Console\Report\ListSetsReport\ReporterInterface;
|
||||
use PhpCsFixer\Console\Report\ListSetsReport\ReportSummary;
|
||||
use PhpCsFixer\Console\Report\ListSetsReport\TextReporter;
|
||||
use PhpCsFixer\RuleSet\RuleSets;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatter;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'list-sets')]
|
||||
final class ListSetsCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'list-sets';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition(
|
||||
[
|
||||
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'To output results in other formats.', (new TextReporter())->getFormat()),
|
||||
]
|
||||
)
|
||||
->setDescription('List all available RuleSets.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$reporter = $this->resolveReporterWithFactory(
|
||||
$input->getOption('format'),
|
||||
new ReporterFactory()
|
||||
);
|
||||
|
||||
$reportSummary = new ReportSummary(
|
||||
array_values(RuleSets::getSetDefinitions())
|
||||
);
|
||||
|
||||
$report = $reporter->generate($reportSummary);
|
||||
|
||||
$output->isDecorated()
|
||||
? $output->write(OutputFormatter::escape($report))
|
||||
: $output->write($report, false, OutputInterface::OUTPUT_RAW)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function resolveReporterWithFactory(string $format, ReporterFactory $factory): ReporterInterface
|
||||
{
|
||||
try {
|
||||
$factory->registerBuiltInReporters();
|
||||
$reporter = $factory->getReporter($format);
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
$formats = $factory->getFormats();
|
||||
sort($formats);
|
||||
|
||||
throw new InvalidConfigurationException(sprintf('The format "%s" is not defined, supported are "%s".', $format, implode('", "', $formats)));
|
||||
}
|
||||
|
||||
return $reporter;
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of PHP CS Fixer.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace PhpCsFixer\Console\Command;
|
||||
|
||||
use PhpCsFixer\Console\SelfUpdate\NewVersionCheckerInterface;
|
||||
use PhpCsFixer\PharCheckerInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\ToolInfoInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @author Igor Wiedler <igor@wiedler.ch>
|
||||
* @author Stephane PY <py.stephane1@gmail.com>
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'self-update')]
|
||||
final class SelfUpdateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $defaultName = 'self-update';
|
||||
|
||||
private NewVersionCheckerInterface $versionChecker;
|
||||
|
||||
private ToolInfoInterface $toolInfo;
|
||||
|
||||
private PharCheckerInterface $pharChecker;
|
||||
|
||||
public function __construct(
|
||||
NewVersionCheckerInterface $versionChecker,
|
||||
ToolInfoInterface $toolInfo,
|
||||
PharCheckerInterface $pharChecker
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
$this->versionChecker = $versionChecker;
|
||||
$this->toolInfo = $toolInfo;
|
||||
$this->pharChecker = $pharChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setAliases(['selfupdate'])
|
||||
->setDefinition(
|
||||
[
|
||||
new InputOption('--force', '-f', InputOption::VALUE_NONE, 'Force update to next major version if available.'),
|
||||
]
|
||||
)
|
||||
->setDescription('Update php-cs-fixer.phar to the latest stable version.')
|
||||
->setHelp(
|
||||
<<<'EOT'
|
||||
The <info>%command.name%</info> command replace your php-cs-fixer.phar by the
|
||||
latest version released on:
|
||||
<comment>https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases</comment>
|
||||
|
||||
<info>$ php php-cs-fixer.phar %command.name%</info>
|
||||
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity() && $output instanceof ConsoleOutputInterface) {
|
||||
$stdErr = $output->getErrorOutput();
|
||||
$stdErr->writeln($this->getApplication()->getLongVersion());
|
||||
}
|
||||
|
||||
if (!$this->toolInfo->isInstalledAsPhar()) {
|
||||
$output->writeln('<error>Self-update is available only for PHAR version.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$currentVersion = $this->getApplication()->getVersion();
|
||||
Preg::match('/^v?(?<major>\d+)\./', $currentVersion, $matches);
|
||||
$currentMajor = (int) $matches['major'];
|
||||
|
||||
try {
|
||||
$latestVersion = $this->versionChecker->getLatestVersion();
|
||||
$latestVersionOfCurrentMajor = $this->versionChecker->getLatestVersionOfMajor($currentMajor);
|
||||
} catch (\Exception $exception) {
|
||||
$output->writeln(sprintf(
|
||||
'<error>Unable to determine newest version: %s</error>',
|
||||
$exception->getMessage()
|
||||
));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (1 !== $this->versionChecker->compareVersions($latestVersion, $currentVersion)) {
|
||||
$output->writeln('<info>PHP CS Fixer is already up-to-date.</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$remoteTag = $latestVersion;
|
||||
|
||||
if (
|
||||
0 !== $this->versionChecker->compareVersions($latestVersionOfCurrentMajor, $latestVersion)
|
||||
&& true !== $input->getOption('force')
|
||||
) {
|
||||
$output->writeln(sprintf('<info>A new major version of PHP CS Fixer is available</info> (<comment>%s</comment>)', $latestVersion));
|
||||
$output->writeln(sprintf('<info>Before upgrading please read</info> https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/%s/UPGRADE-v%s.md', $latestVersion, $currentMajor + 1));
|
||||
$output->writeln('<info>If you are ready to upgrade run this command with</info> <comment>-f</comment>');
|
||||
$output->writeln('<info>Checking for new minor/patch version...</info>');
|
||||
|
||||
if (1 !== $this->versionChecker->compareVersions($latestVersionOfCurrentMajor, $currentVersion)) {
|
||||
$output->writeln('<info>No minor update for PHP CS Fixer.</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$remoteTag = $latestVersionOfCurrentMajor;
|
||||
}
|
||||
|
||||
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
|
||||
|
||||
if (!is_writable($localFilename)) {
|
||||
$output->writeln(sprintf('<error>No permission to update</error> "%s" <error>file.</error>', $localFilename));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$tempFilename = \dirname($localFilename).'/'.basename($localFilename, '.phar').'-tmp.phar';
|
||||
$remoteFilename = $this->toolInfo->getPharDownloadUri($remoteTag);
|
||||
|
||||
if (false === @copy($remoteFilename, $tempFilename)) {
|
||||
$output->writeln(sprintf('<error>Unable to download new version</error> %s <error>from the server.</error>', $remoteTag));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
chmod($tempFilename, 0777 & ~umask());
|
||||
|
||||
$pharInvalidityReason = $this->pharChecker->checkFileValidity($tempFilename);
|
||||
if (null !== $pharInvalidityReason) {
|
||||
unlink($tempFilename);
|
||||
$output->writeln(sprintf('<error>The download of</error> %s <error>is corrupt (%s).</error>', $remoteTag, $pharInvalidityReason));
|
||||
$output->writeln('<error>Please re-run the "self-update" command to try again.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
rename($tempFilename, $localFilename);
|
||||
|
||||
$output->writeln(sprintf('<info>PHP CS Fixer updated</info> (<comment>%s</comment> -> <comment>%s</comment>)', $currentVersion, $remoteTag));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user