136 lines
4.6 KiB
PHP
136 lines
4.6 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Symfony package.
|
|
*
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Symfony\Flex;
|
|
|
|
use Composer\Composer;
|
|
use Composer\EventDispatcher\ScriptExecutionException;
|
|
use Composer\IO\IOInterface;
|
|
use Composer\Semver\Constraint\EmptyConstraint;
|
|
use Composer\Semver\Constraint\MatchAllConstraint;
|
|
use Composer\Util\ProcessExecutor;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Output\StreamOutput;
|
|
use Symfony\Component\Process\PhpExecutableFinder;
|
|
|
|
/**
|
|
* @author Fabien Potencier <fabien@symfony.com>
|
|
*/
|
|
class ScriptExecutor
|
|
{
|
|
private $composer;
|
|
private $io;
|
|
private $options;
|
|
private $executor;
|
|
|
|
public function __construct(Composer $composer, IOInterface $io, Options $options, ProcessExecutor $executor = null)
|
|
{
|
|
$this->composer = $composer;
|
|
$this->io = $io;
|
|
$this->options = $options;
|
|
$this->executor = $executor ?: new ProcessExecutor();
|
|
}
|
|
|
|
/**
|
|
* @throws ScriptExecutionException if the executed command returns a non-0 exit code
|
|
*/
|
|
public function execute(string $type, string $cmd)
|
|
{
|
|
$parsedCmd = $this->options->expandTargetDir($cmd);
|
|
if (null === $expandedCmd = $this->expandCmd($type, $parsedCmd)) {
|
|
return;
|
|
}
|
|
|
|
$cmdOutput = new StreamOutput(fopen('php://temp', 'rw'), OutputInterface::VERBOSITY_VERBOSE, $this->io->isDecorated());
|
|
$outputHandler = function ($type, $buffer) use ($cmdOutput) {
|
|
$cmdOutput->write($buffer, false, OutputInterface::OUTPUT_RAW);
|
|
};
|
|
|
|
$this->io->writeError(sprintf('Executing script %s', $parsedCmd), $this->io->isVerbose());
|
|
$exitCode = $this->executor->execute($expandedCmd, $outputHandler);
|
|
|
|
$code = 0 === $exitCode ? ' <info>[OK]</>' : ' <error>[KO]</>';
|
|
|
|
if ($this->io->isVerbose()) {
|
|
$this->io->writeError(sprintf('Executed script %s %s', $cmd, $code));
|
|
} else {
|
|
$this->io->writeError($code);
|
|
}
|
|
|
|
if (0 !== $exitCode) {
|
|
$this->io->writeError(' <error>[KO]</>');
|
|
$this->io->writeError(sprintf('<error>Script %s returned with error code %s</>', $cmd, $exitCode));
|
|
fseek($cmdOutput->getStream(), 0);
|
|
foreach (explode("\n", stream_get_contents($cmdOutput->getStream())) as $line) {
|
|
$this->io->writeError('!! '.$line);
|
|
}
|
|
|
|
throw new ScriptExecutionException($cmd, $exitCode);
|
|
}
|
|
}
|
|
|
|
private function expandCmd(string $type, string $cmd)
|
|
{
|
|
switch ($type) {
|
|
case 'symfony-cmd':
|
|
return $this->expandSymfonyCmd($cmd);
|
|
case 'php-script':
|
|
return $this->expandPhpScript($cmd);
|
|
case 'script':
|
|
return $cmd;
|
|
default:
|
|
throw new \InvalidArgumentException(sprintf('Invalid symfony/flex auto-script in composer.json: "%s" is not a valid type of command.', $type));
|
|
}
|
|
}
|
|
|
|
private function expandSymfonyCmd(string $cmd)
|
|
{
|
|
$repo = $this->composer->getRepositoryManager()->getLocalRepository();
|
|
if (!$repo->findPackage('symfony/console', class_exists(MatchAllConstraint::class) ? new MatchAllConstraint() : new EmptyConstraint())) {
|
|
$this->io->writeError(sprintf('<warning>Skipping "%s" (needs symfony/console to run).</>', $cmd));
|
|
|
|
return null;
|
|
}
|
|
|
|
$console = ProcessExecutor::escape($this->options->get('root-dir').'/'.$this->options->get('bin-dir').'/console');
|
|
if ($this->io->isDecorated()) {
|
|
$console .= ' --ansi';
|
|
}
|
|
|
|
return $this->expandPhpScript($console.' '.$cmd);
|
|
}
|
|
|
|
private function expandPhpScript(string $cmd): string
|
|
{
|
|
$phpFinder = new PhpExecutableFinder();
|
|
if (!$php = $phpFinder->find(false)) {
|
|
throw new \RuntimeException('The PHP executable could not be found, add it to your PATH and try again.');
|
|
}
|
|
|
|
$arguments = $phpFinder->findArguments();
|
|
|
|
if ($env = (string) (getenv('COMPOSER_ORIGINAL_INIS'))) {
|
|
$paths = explode(\PATH_SEPARATOR, $env);
|
|
$ini = array_shift($paths);
|
|
} else {
|
|
$ini = php_ini_loaded_file();
|
|
}
|
|
|
|
if ($ini) {
|
|
$arguments[] = '--php-ini='.$ini;
|
|
}
|
|
|
|
$phpArgs = implode(' ', array_map([ProcessExecutor::class, 'escape'], $arguments));
|
|
|
|
return ProcessExecutor::escape($php).($phpArgs ? ' '.$phpArgs : '').' '.$cmd;
|
|
}
|
|
}
|