Spaces:
No application file
No application file
declare(strict_types=1); | |
namespace Mautic\CoreBundle\Helper; | |
use Composer\Console\Application; | |
use Mautic\MarketplaceBundle\DTO\ConsoleOutput; | |
use Psr\Log\LoggerInterface; | |
use Symfony\Component\Console\Input\ArrayInput; | |
use Symfony\Component\Console\Output\BufferedOutput; | |
use Symfony\Component\HttpKernel\KernelInterface; | |
/** | |
* Provides several helper functions to interact with Composer (composer require, remove, etc.). | |
*/ | |
class ComposerHelper | |
{ | |
public function __construct( | |
private KernelInterface $kernel, | |
private LoggerInterface $logger | |
) { | |
} | |
/** | |
* Installs a package using its Packagist name. | |
* | |
* @param string $packageName The package name, e.g. mautic/example-plugin | |
* @param bool $dryRun Whether to dry-run the installation. Comes in handy during automated tests | |
* and to test whether an installation would succeed or not. | |
*/ | |
public function install(string $packageName, bool $dryRun = false): ConsoleOutput | |
{ | |
$input = [ | |
'command' => 'require', | |
'packages' => [$packageName], | |
]; | |
if (true === $dryRun) { | |
$input['--dry-run'] = null; | |
} | |
return $this->runCommand($input); | |
} | |
/** | |
* Removes a package using its Packagist name. | |
* | |
* @param string $packageName The package name, e.g. mautic/example-plugin | |
* @param bool $dryRun Whether to dry-run the removal. Comes in handy during automated tests | |
* and to test whether an removal would succeed or not. | |
*/ | |
public function remove(string $packageName, bool $dryRun = false): ConsoleOutput | |
{ | |
/** | |
* "composer remove package-name" also triggers an update of all other Mautic dependencies. | |
* By using the --no-update option first, we can work around that issue and only delete | |
* this specific package from the composer.json file. | |
*/ | |
$input = [ | |
'command' => 'remove', | |
'packages' => [$packageName], | |
'--no-update' => null, | |
]; | |
if (true === $dryRun) { | |
$input['--dry-run'] = null; | |
} | |
$firstOutput = $this->runCommand($input); | |
if (0 === $firstOutput->exitCode) { | |
/** | |
* Triggering an update of the package we just removed from composer.json | |
* will remove it from composer.lock and actually delete the plugin folder | |
* as well. | |
*/ | |
$input = [ | |
'command' => 'update', | |
'packages' => [$packageName], | |
]; | |
if (true === $dryRun) { | |
$input['--dry-run'] = null; | |
} | |
$secondOutput = $this->runCommand($input); | |
// Let's merge the output so that we return all the output we have. | |
return new ConsoleOutput( | |
$secondOutput->exitCode, | |
$firstOutput->output."\n".$secondOutput->output | |
); | |
} | |
return $firstOutput; | |
} | |
/** | |
* Checks if the given Composer package is installed. | |
* | |
* @param string $packageName The package name, e.g. mautic/exmple-plugin | |
*/ | |
public function isInstalled(string $packageName): bool | |
{ | |
return \Composer\InstalledVersions::isInstalled($packageName); | |
} | |
/** | |
* Returns a list of installed Composer packages that are of type mautic-plugin. | |
* | |
* @return string[] | |
*/ | |
public function getMauticPluginPackages(): array | |
{ | |
return \Composer\InstalledVersions::getInstalledPackagesByType('mautic-plugin'); | |
} | |
/** | |
* Updates one or multiple Composer packages. | |
*/ | |
public function update(?string $packageName = null, bool $dryRun = false): ConsoleOutput | |
{ | |
$input = [ | |
'command' => 'update', | |
]; | |
if (!empty($packageName)) { | |
$input['packages'] = [$packageName]; | |
} | |
if (true === $dryRun) { | |
$input['--dry-run'] = null; | |
} | |
return $this->runCommand($input); | |
} | |
/** | |
* @param array<string,mixed> $input | |
*/ | |
private function runCommand(array $input): ConsoleOutput | |
{ | |
$arrayInput = new ArrayInput(array_merge( | |
$input, [ | |
'--no-interaction', | |
'--working-dir' => $this->kernel->getProjectDir(), | |
])); | |
$application = new Application(); | |
// We don't want our script to stop after running a Composer command | |
$application->setAutoExit(false); | |
$this->logger->info('Running Composer command: '.$arrayInput->__toString()); | |
$output = new BufferedOutput(); | |
$exitCode = 1; | |
try { | |
$exitCode = $application->run($arrayInput, $output); | |
} catch (\Exception $e) { | |
$output->writeln('Exception while running Composer command: '.$e->getMessage()); | |
$this->logger->error('Exception while running Composer command: '.$e->getMessage()); | |
} | |
$this->logger->info('Composer command output: '.$output->fetch()); | |
return new ConsoleOutput($exitCode, $output->fetch()); | |
} | |
} | |