* Dariusz Rumiński * * 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\Application; 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 * @author Stephane PY * @author Grégoire Pineau * @author Dariusz Rumiński * * @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; } 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 %command.name% command replace your php-cs-fixer.phar by the latest version released on: https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases $ php php-cs-fixer.phar %command.name% EOT ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { if ($output instanceof ConsoleOutputInterface) { $stdErr = $output->getErrorOutput(); $stdErr->writeln(Application::getAboutWithRuntime(true)); } if (!$this->toolInfo->isInstalledAsPhar()) { $output->writeln('Self-update is available only for PHAR version.'); return 1; } $currentVersion = $this->getApplication()->getVersion(); Preg::match('/^v?(?\d+)\./', $currentVersion, $matches); $currentMajor = (int) $matches['major']; try { $latestVersion = $this->versionChecker->getLatestVersion(); $latestVersionOfCurrentMajor = $this->versionChecker->getLatestVersionOfMajor($currentMajor); } catch (\Exception $exception) { $output->writeln(sprintf( 'Unable to determine newest version: %s', $exception->getMessage() )); return 1; } if (1 !== $this->versionChecker->compareVersions($latestVersion, $currentVersion)) { $output->writeln('PHP CS Fixer is already up-to-date.'); return 0; } $remoteTag = $latestVersion; if ( 0 !== $this->versionChecker->compareVersions($latestVersionOfCurrentMajor, $latestVersion) && true !== $input->getOption('force') ) { $output->writeln(sprintf('A new major version of PHP CS Fixer is available (%s)', $latestVersion)); $output->writeln(sprintf('Before upgrading please read https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/%s/UPGRADE-v%s.md', $latestVersion, $currentMajor + 1)); $output->writeln('If you are ready to upgrade run this command with -f'); $output->writeln('Checking for new minor/patch version...'); if (1 !== $this->versionChecker->compareVersions($latestVersionOfCurrentMajor, $currentVersion)) { $output->writeln('No minor update for PHP CS Fixer.'); return 0; } $remoteTag = $latestVersionOfCurrentMajor; } $localFilename = $_SERVER['argv'][0]; $realPath = realpath($localFilename); if (false !== $realPath) { $localFilename = $realPath; } if (!is_writable($localFilename)) { $output->writeln(sprintf('No permission to update "%s" file.', $localFilename)); return 1; } $tempFilename = \dirname($localFilename).'/'.basename($localFilename, '.phar').'-tmp.phar'; $remoteFilename = $this->toolInfo->getPharDownloadUri($remoteTag); if (false === @copy($remoteFilename, $tempFilename)) { $output->writeln(sprintf('Unable to download new version %s from the server.', $remoteTag)); return 1; } chmod($tempFilename, 0777 & ~umask()); $pharInvalidityReason = $this->pharChecker->checkFileValidity($tempFilename); if (null !== $pharInvalidityReason) { unlink($tempFilename); $output->writeln(sprintf('The download of %s is corrupt (%s).', $remoteTag, $pharInvalidityReason)); $output->writeln('Please re-run the "self-update" command to try again.'); return 1; } rename($tempFilename, $localFilename); $output->writeln(sprintf('PHP CS Fixer updated (%s -> %s)', $currentVersion, $remoteTag)); return 0; } }