Deprecated : Constant E_STRICT is deprecated in /home/pastorz/old-espace-client/vendor/symfony/error-handler/ErrorHandler.php on line 58
Deprecated : Constant E_STRICT is deprecated in /home/pastorz/old-espace-client/vendor/symfony/error-handler/ErrorHandler.php on line 76
Symfony Profiler
<?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\Component\Console ;
use Symfony\Component\Console\Command\Command ;
use Symfony\Component\Console\Command\CompleteCommand ;
use Symfony\Component\Console\Command\DumpCompletionCommand ;
use Symfony\Component\Console\Command\HelpCommand ;
use Symfony\Component\Console\Command\LazyCommand ;
use Symfony\Component\Console\Command\ListCommand ;
use Symfony\Component\Console\Command\SignalableCommandInterface ;
use Symfony\Component\Console\CommandLoader\CommandLoaderInterface ;
use Symfony\Component\Console\Completion\CompletionInput ;
use Symfony\Component\Console\Completion\CompletionSuggestions ;
use Symfony\Component\Console\Event\ConsoleCommandEvent ;
use Symfony\Component\Console\Event\ConsoleErrorEvent ;
use Symfony\Component\Console\Event\ConsoleSignalEvent ;
use Symfony\Component\Console\Event\ConsoleTerminateEvent ;
use Symfony\Component\Console\Exception\CommandNotFoundException ;
use Symfony\Component\Console\Exception\ExceptionInterface ;
use Symfony\Component\Console\Exception\LogicException ;
use Symfony\Component\Console\Exception\NamespaceNotFoundException ;
use Symfony\Component\Console\Exception\RuntimeException ;
use Symfony\Component\Console\Formatter\OutputFormatter ;
use Symfony\Component\Console\Helper\DebugFormatterHelper ;
use Symfony\Component\Console\Helper\FormatterHelper ;
use Symfony\Component\Console\Helper\Helper ;
use Symfony\Component\Console\Helper\HelperSet ;
use Symfony\Component\Console\Helper\ProcessHelper ;
use Symfony\Component\Console\Helper\QuestionHelper ;
use Symfony\Component\Console\Input\ArgvInput ;
use Symfony\Component\Console\Input\ArrayInput ;
use Symfony\Component\Console\Input\InputArgument ;
use Symfony\Component\Console\Input\InputAwareInterface ;
use Symfony\Component\Console\Input\InputDefinition ;
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Input\InputOption ;
use Symfony\Component\Console\Output\ConsoleOutput ;
use Symfony\Component\Console\Output\ConsoleOutputInterface ;
use Symfony\Component\Console\Output\OutputInterface ;
use Symfony\Component\Console\SignalRegistry\SignalRegistry ;
use Symfony\Component\Console\Style\SymfonyStyle ;
use Symfony\Component\ErrorHandler\ErrorHandler ;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface ;
use Symfony\Contracts\Service\ResetInterface ;
/**
* An Application is the container for a collection of commands.
*
* It is the main entry point of a Console application.
*
* This class is optimized for a standard CLI environment.
*
* Usage:
*
* $app = new Application('myapp', '1.0 (stable)');
* $app->add(new SimpleCommand());
* $app->run();
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Application implements ResetInterface
{
private $commands = [];
private $wantHelps = false ;
private $runningCommand ;
private $name ;
private $version ;
private $commandLoader ;
private $catchExceptions = true ;
private $autoExit = true ;
private $definition ;
private $helperSet ;
private $dispatcher ;
private $terminal ;
private $defaultCommand ;
private $singleCommand = false ;
private $initialized ;
private $signalRegistry ;
private $signalsToDispatchEvent = [];
public function __construct ( string $name = 'UNKNOWN' , string $version = 'UNKNOWN' )
{
$this -> name = $name ;
$this -> version = $version ;
$this -> terminal = new Terminal ();
$this -> defaultCommand = 'list' ;
if (\defined ( 'SIGINT' ) && SignalRegistry :: isSupported ()) {
$this -> signalRegistry = new SignalRegistry ();
$this -> signalsToDispatchEvent = [ \SIGINT , \SIGTERM , \SIGUSR1 , \SIGUSR2 ];
}
}
/**
* @final
*/
public function setDispatcher ( EventDispatcherInterface $dispatcher )
{
$this -> dispatcher = $dispatcher ;
}
public function setCommandLoader ( CommandLoaderInterface $commandLoader )
{
$this -> commandLoader = $commandLoader ;
}
public function getSignalRegistry (): SignalRegistry
{
if (!$this -> signalRegistry ) {
throw new RuntimeException ( 'Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.' );
}
return $this -> signalRegistry ;
}
public function setSignalsToDispatchEvent ( int ... $signalsToDispatchEvent )
{
$this -> signalsToDispatchEvent = $signalsToDispatchEvent ;
}
/**
* Runs the current application.
*
* @return int 0 if everything went fine, or an error code
*
* @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
*/
public function run ( InputInterface $input = null , OutputInterface $output = null )
{
if (\function_exists ( 'putenv' )) {
@putenv ( 'LINES=' . $this -> terminal -> getHeight ());
@putenv ( 'COLUMNS=' . $this -> terminal -> getWidth ());
}
if (null === $input ) {
$input = new ArgvInput ();
}
if (null === $output ) {
$output = new ConsoleOutput ();
}
$renderException = function ( \Throwable $e ) use ( $output ) {
if ($output instanceof ConsoleOutputInterface ) {
$this -> renderThrowable ( $e , $output -> getErrorOutput ());
} else {
$this -> renderThrowable ( $e , $output );
}
};
if ($phpHandler = set_exception_handler ( $renderException )) {
restore_exception_handler ();
if (!\is_array ( $phpHandler ) || ! $phpHandler [ 0 ] instanceof ErrorHandler ) {
$errorHandler = true ;
} elseif ($errorHandler = $phpHandler [ 0 ]-> setExceptionHandler ( $renderException )) {
$phpHandler [ 0 ]-> setExceptionHandler ( $errorHandler );
}
}
$this -> configureIO ( $input , $output );
try {
$exitCode = $this -> doRun ( $input , $output );
} catch (\Exception $e ) {
if (!$this -> catchExceptions ) {
throw $e ;
}
$renderException ( $e );
$exitCode = $e -> getCode ();
if (is_numeric ( $exitCode )) {
$exitCode = (int) $exitCode ;
if ($exitCode <= 0 ) {
$exitCode = 1 ;
}
} else {
$exitCode = 1 ;
}
} finally {
// if the exception handler changed, keep it
// otherwise, unregister $renderException
if (! $phpHandler ) {
if (set_exception_handler ( $renderException ) === $renderException ) {
restore_exception_handler ();
}
restore_exception_handler ();
} elseif (!$errorHandler ) {
$finalHandler = $phpHandler [ 0 ]-> setExceptionHandler ( null );
if ($finalHandler !== $renderException ) {
$phpHandler [ 0 ]-> setExceptionHandler ( $finalHandler );
}
}
}
if ($this -> autoExit ) {
if ($exitCode > 255 ) {
$exitCode = 255 ;
}
exit($exitCode );
}
return $exitCode ;
}
/**
* Runs the current application.
*
* @return int 0 if everything went fine, or an error code
*/
public function doRun ( InputInterface $input , OutputInterface $output )
{
if (true === $input -> hasParameterOption ([ '--version' , '-V' ], true )) {
$output -> writeln ( $this -> getLongVersion ());
return 0 ;
}
try {
// Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
$input -> bind ( $this -> getDefinition ());
} catch (ExceptionInterface $e ) {
// Errors must be ignored, full binding/validation happens later when the command is known.
}
$name = $this -> getCommandName ( $input );
if (true === $input -> hasParameterOption ([ '--help' , '-h' ], true )) {
if (!$name ) {
$name = 'help' ;
$input = new ArrayInput ([ 'command_name' => $this -> defaultCommand ]);
} else {
$this -> wantHelps = true ;
}
}
if (!$name ) {
$name = $this -> defaultCommand ;
$definition = $this -> getDefinition ();
$definition -> setArguments ( array_merge (
$definition -> getArguments (),
[
'command' => new InputArgument ( 'command' , InputArgument :: OPTIONAL , $definition -> getArgument ( 'command' )-> getDescription (), $name ),
]
));
}
try {
$this -> runningCommand = null ;
// the command name MUST be the first element of the input
$command = $this -> find ( $name );
} catch (\Throwable $e ) {
if (!($e instanceof CommandNotFoundException && ! $e instanceof NamespaceNotFoundException ) || 1 !== \count ( $alternatives = $e -> getAlternatives ()) || ! $input -> isInteractive ()) {
if (null !== $this -> dispatcher ) {
$event = new ConsoleErrorEvent ( $input , $output , $e );
$this -> dispatcher -> dispatch ( $event , ConsoleEvents :: ERROR );
if (0 === $event -> getExitCode ()) {
return 0 ;
}
$e = $event -> getError ();
}
throw $e ;
}
$alternative = $alternatives [ 0 ];
$style = new SymfonyStyle ( $input , $output );
$output -> writeln ( '' );
$formattedBlock = (new FormatterHelper ())-> formatBlock ( sprintf ( 'Command "%s" is not defined.' , $name ), 'error' , true );
$output -> writeln ( $formattedBlock );
if (!$style -> confirm ( sprintf ( 'Do you want to run "%s" instead? ' , $alternative ), false )) {
if (null !== $this -> dispatcher ) {
$event = new ConsoleErrorEvent ( $input , $output , $e );
$this -> dispatcher -> dispatch ( $event , ConsoleEvents :: ERROR );
return $event -> getExitCode ();
}
return 1 ;
}
$command = $this -> find ( $alternative );
}
if ($command instanceof LazyCommand ) {
$command = $command -> getCommand ();
}
$this -> runningCommand = $command ;
$exitCode = $this -> doRunCommand ( $command , $input , $output );
$this -> runningCommand = null ;
return $exitCode ;
}
/**
* {@inheritdoc}
*/
public function reset ()
{
}
public function setHelperSet ( HelperSet $helperSet )
{
$this -> helperSet = $helperSet ;
}
/**
* Get the helper set associated with the command.
*
* @return HelperSet
*/
public function getHelperSet ()
{
if (!$this -> helperSet ) {
$this -> helperSet = $this -> getDefaultHelperSet ();
}
return $this -> helperSet ;
}
public function setDefinition ( InputDefinition $definition )
{
$this -> definition = $definition ;
}
/**
* Gets the InputDefinition related to this Application.
*
* @return InputDefinition
*/
public function getDefinition ()
{
if (!$this -> definition ) {
$this -> definition = $this -> getDefaultInputDefinition ();
}
if ($this -> singleCommand ) {
$inputDefinition = $this -> definition ;
$inputDefinition -> setArguments ();
return $inputDefinition ;
}
return $this -> definition ;
}
/**
* Adds suggestions to $suggestions for the current completion input (e.g. option or argument).
*/
public function complete ( CompletionInput $input , CompletionSuggestions $suggestions ): void
{
if (
CompletionInput :: TYPE_ARGUMENT_VALUE === $input -> getCompletionType ()
&& 'command' === $input -> getCompletionName ()
) {
$commandNames = [];
foreach ($this -> all () as $name => $command ) {
// skip hidden commands and aliased commands as they already get added below
if ( $command -> isHidden () || $command -> getName () !== $name ) {
continue;
}
$commandNames [] = $command -> getName ();
foreach ($command -> getAliases () as $name ) {
$commandNames [] = $name ;
}
}
$suggestions -> suggestValues ( array_filter ( $commandNames ));
return;
}
if (CompletionInput :: TYPE_OPTION_NAME === $input -> getCompletionType ()) {
$suggestions -> suggestOptions ( $this -> getDefinition ()-> getOptions ());
return;
}
}
/**
* Gets the help message.
*
* @return string
*/
public function getHelp ()
{
return $this -> getLongVersion ();
}
/**
* Gets whether to catch exceptions or not during commands execution.
*
* @return bool
*/
public function areExceptionsCaught ()
{
return $this -> catchExceptions ;
}
/**
* Sets whether to catch exceptions or not during commands execution.
*/
public function setCatchExceptions ( bool $boolean )
{
$this -> catchExceptions = $boolean ;
}
/**
* Gets whether to automatically exit after a command execution or not.
*
* @return bool
*/
public function isAutoExitEnabled ()
{
return $this -> autoExit ;
}
/**
* Sets whether to automatically exit after a command execution or not.
*/
public function setAutoExit ( bool $boolean )
{
$this -> autoExit = $boolean ;
}
/**
* Gets the name of the application.
*
* @return string
*/
public function getName ()
{
return $this -> name ;
}
/**
* Sets the application name.
**/
public function setName ( string $name )
{
$this -> name = $name ;
}
/**
* Gets the application version.
*
* @return string
*/
public function getVersion ()
{
return $this -> version ;
}
/**
* Sets the application version.
*/
public function setVersion ( string $version )
{
$this -> version = $version ;
}
/**
* Returns the long version of the application.
*
* @return string
*/
public function getLongVersion ()
{
if ('UNKNOWN' !== $this -> getName ()) {
if ('UNKNOWN' !== $this -> getVersion ()) {
return sprintf ( '%s <info>%s</info>' , $this -> getName (), $this -> getVersion ());
}
return $this -> getName ();
}
return 'Console Tool' ;
}
/**
* Registers a new command.
*
* @return Command
*/
public function register ( string $name )
{
return $this -> add (new Command ( $name ));
}
/**
* Adds an array of command objects.
*
* If a Command is not enabled it will not be added.
*
* @param Command[] $commands An array of commands
*/
public function addCommands (array $commands )
{
foreach ($commands as $command ) {
$this -> add ( $command );
}
}
/**
* Adds a command object.
*
* If a command with the same name already exists, it will be overridden.
* If the command is not enabled it will not be added.
*
* @return Command|null
*/
public function add ( Command $command )
{
$this -> init ();
$command -> setApplication ( $this );
if (!$command -> isEnabled ()) {
$command -> setApplication ( null );
return null ;
}
if (!$command instanceof LazyCommand ) {
// Will throw if the command is not correctly initialized.
$command -> getDefinition ();
}
if (!$command -> getName ()) {
throw new LogicException ( sprintf ( 'The command defined in "%s" cannot have an empty name.' , get_debug_type ( $command )));
}
$this -> commands [ $command -> getName ()] = $command ;
foreach ($command -> getAliases () as $alias ) {
$this -> commands [ $alias ] = $command ;
}
return $command ;
}
/**
* Returns a registered command by name or alias.
*
* @return Command
*
* @throws CommandNotFoundException When given command name does not exist
*/
public function get ( string $name )
{
$this -> init ();
if (!$this -> has ( $name )) {
throw new CommandNotFoundException ( sprintf ( 'The command "%s" does not exist.' , $name ));
}
// When the command has a different name than the one used at the command loader level
if (!isset( $this -> commands [ $name ])) {
throw new CommandNotFoundException ( sprintf ( 'The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".' , $name ));
}
$command = $this -> commands [ $name ];
if ($this -> wantHelps ) {
$this -> wantHelps = false ;
$helpCommand = $this -> get ( 'help' );
$helpCommand -> setCommand ( $command );
return $helpCommand ;
}
return $command ;
}
/**
* Returns true if the command exists, false otherwise.
*
* @return bool
*/
public function has ( string $name )
{
$this -> init ();
return isset($this -> commands [ $name ]) || ( $this -> commandLoader && $this -> commandLoader -> has ( $name ) && $this -> add ( $this -> commandLoader -> get ( $name )));
}
/**
* Returns an array of all unique namespaces used by currently registered commands.
*
* It does not return the global namespace which always exists.
*
* @return string[]
*/
public function getNamespaces ()
{
$namespaces = [];
foreach ($this -> all () as $command ) {
if ($command -> isHidden ()) {
continue;
}
$namespaces [] = $this -> extractAllNamespaces ( $command -> getName ());
foreach ($command -> getAliases () as $alias ) {
$namespaces [] = $this -> extractAllNamespaces ( $alias );
}
}
return array_values ( array_unique ( array_filter ( array_merge ([], ... $namespaces ))));
}
/**
* Finds a registered namespace by a name or an abbreviation.
*
* @return string
*
* @throws NamespaceNotFoundException When namespace is incorrect or ambiguous
*/
public function findNamespace ( string $namespace )
{
$allNamespaces = $this -> getNamespaces ();
$expr = implode ( '[^:]*:' , array_map ( 'preg_quote' , explode ( ':' , $namespace ))). '[^:]*' ;
$namespaces = preg_grep ( '{^' . $expr . '}' , $allNamespaces );
if (empty($namespaces )) {
$message = sprintf ( 'There are no commands defined in the "%s" namespace.' , $namespace );
if ($alternatives = $this -> findAlternatives ( $namespace , $allNamespaces )) {
if (1 == \count ( $alternatives )) {
$message .= "\n\nDid you mean this?\n " ;
} else {
$message .= "\n\nDid you mean one of these?\n " ;
}
$message .= implode ( "\n " , $alternatives );
}
throw new NamespaceNotFoundException ( $message , $alternatives );
}
$exact = \in_array ( $namespace , $namespaces , true );
if (\count ( $namespaces ) > 1 && ! $exact ) {
throw new NamespaceNotFoundException ( sprintf ( "The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s." , $namespace , $this -> getAbbreviationSuggestions ( array_values ( $namespaces ))), array_values ( $namespaces ));
}
return $exact ? $namespace : reset ( $namespaces );
}
/**
* Finds a command by name or alias.
*
* Contrary to get, this command tries to find the best
* match if you give it an abbreviation of a name or alias.
*
* @return Command
*
* @throws CommandNotFoundException When command name is incorrect or ambiguous
*/
public function find ( string $name )
{
$this -> init ();
$aliases = [];
foreach ($this -> commands as $command ) {
foreach ($command -> getAliases () as $alias ) {
if (!$this -> has ( $alias )) {
$this -> commands [ $alias ] = $command ;
}
}
}
if ($this -> has ( $name )) {
return $this -> get ( $name );
}
$allCommands = $this -> commandLoader ? array_merge ( $this -> commandLoader -> getNames (), array_keys ( $this -> commands )) : array_keys ( $this -> commands );
$expr = implode ( '[^:]*:' , array_map ( 'preg_quote' , explode ( ':' , $name ))). '[^:]*' ;
$commands = preg_grep ( '{^' . $expr . '}' , $allCommands );
if (empty($commands )) {
$commands = preg_grep ( '{^' . $expr . '}i' , $allCommands );
}
// if no commands matched or we just matched namespaces
if (empty( $commands ) || \count ( preg_grep ( '{^' . $expr . '$}i' , $commands )) < 1 ) {
if (false !== $pos = strrpos ( $name , ':' )) {
// check if a namespace exists and contains commands
$this -> findNamespace ( substr ( $name , 0 , $pos ));
}
$message = sprintf ( 'Command "%s" is not defined.' , $name );
if ($alternatives = $this -> findAlternatives ( $name , $allCommands )) {
// remove hidden commands
$alternatives = array_filter ( $alternatives , function ( $name ) {
return !$this -> get ( $name )-> isHidden ();
});
if (1 == \count ( $alternatives )) {
$message .= "\n\nDid you mean this?\n " ;
} else {
$message .= "\n\nDid you mean one of these?\n " ;
}
$message .= implode ( "\n " , $alternatives );
}
throw new CommandNotFoundException ( $message , array_values ( $alternatives ));
}
// filter out aliases for commands which are already on the list
if ( \count ( $commands ) > 1 ) {
$commandList = $this -> commandLoader ? array_merge ( array_flip ( $this -> commandLoader -> getNames ()), $this -> commands ) : $this -> commands ;
$commands = array_unique ( array_filter ( $commands , function ( $nameOrAlias ) use (& $commandList , $commands , & $aliases ) {
if (!$commandList [ $nameOrAlias ] instanceof Command ) {
$commandList [ $nameOrAlias ] = $this -> commandLoader -> get ( $nameOrAlias );
}
$commandName = $commandList [ $nameOrAlias ]-> getName ();
$aliases [ $nameOrAlias ] = $commandName ;
return $commandName === $nameOrAlias || ! \in_array ( $commandName , $commands );
}));
}
if (\count ( $commands ) > 1 ) {
$usableWidth = $this -> terminal -> getWidth () - 10 ;
$abbrevs = array_values ( $commands );
$maxLen = 0 ;
foreach ($abbrevs as $abbrev ) {
$maxLen = max ( Helper :: width ( $abbrev ), $maxLen );
}
$abbrevs = array_map (function ( $cmd ) use ( $commandList , $usableWidth , $maxLen , & $commands ) {
if ($commandList [ $cmd ]-> isHidden ()) {
unset($commands [ array_search ( $cmd , $commands )]);
return false ;
}
$abbrev = str_pad ( $cmd , $maxLen , ' ' ). ' ' . $commandList [ $cmd ]-> getDescription ();
return Helper :: width ( $abbrev ) > $usableWidth ? Helper :: substr ( $abbrev , 0 , $usableWidth - 3 ). '...' : $abbrev ;
}, array_values ( $commands ));
if (\count ( $commands ) > 1 ) {
$suggestions = $this -> getAbbreviationSuggestions ( array_filter ( $abbrevs ));
throw new CommandNotFoundException ( sprintf ( "Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s." , $name , $suggestions ), array_values ( $commands ));
}
}
$command = $this -> get ( reset ( $commands ));
if ($command -> isHidden ()) {
throw new CommandNotFoundException ( sprintf ( 'The command "%s" does not exist.' , $name ));
}
return $command ;
}
/**
* Gets the commands (registered in the given namespace if provided).
*
* The array keys are the full names and the values the command instances.
*
* @return Command[]
*/
public function all ( string $namespace = null )
{
$this -> init ();
if (null === $namespace ) {
if (!$this -> commandLoader ) {
return $this -> commands ;
}
$commands = $this -> commands ;
foreach ($this -> commandLoader -> getNames () as $name ) {
if (!isset($commands [ $name ]) && $this -> has ( $name )) {
$commands [ $name ] = $this -> get ( $name );
}
}
return $commands ;
}
$commands = [];
foreach ($this -> commands as $name => $command ) {
if ($namespace === $this -> extractNamespace ( $name , substr_count ( $namespace , ':' ) + 1 )) {
$commands [ $name ] = $command ;
}
}
if ($this -> commandLoader ) {
foreach ($this -> commandLoader -> getNames () as $name ) {
if (!isset($commands [ $name ]) && $namespace === $this -> extractNamespace ( $name , substr_count ( $namespace , ':' ) + 1 ) && $this -> has ( $name )) {
$commands [ $name ] = $this -> get ( $name );
}
}
}
return $commands ;
}
/**
* Returns an array of possible abbreviations given a set of names.
*
* @return string[][]
*/
public static function getAbbreviations (array $names )
{
$abbrevs = [];
foreach ($names as $name ) {
for ($len = \strlen ( $name ); $len > 0 ; -- $len ) {
$abbrev = substr ( $name , 0 , $len );
$abbrevs [ $abbrev ][] = $name ;
}
}
return $abbrevs ;
}
public function renderThrowable ( \Throwable $e , OutputInterface $output ): void
{
$output -> writeln ( '' , OutputInterface :: VERBOSITY_QUIET );
$this -> doRenderThrowable ( $e , $output );
if (null !== $this -> runningCommand ) {
$output -> writeln ( sprintf ( '<info>%s</info>' , OutputFormatter :: escape ( sprintf ( $this -> runningCommand -> getSynopsis (), $this -> getName ()))), OutputInterface :: VERBOSITY_QUIET );
$output -> writeln ( '' , OutputInterface :: VERBOSITY_QUIET );
}
}
protected function doRenderThrowable ( \Throwable $e , OutputInterface $output ): void
{
do {
$message = trim ( $e -> getMessage ());
if ('' === $message || OutputInterface :: VERBOSITY_VERBOSE <= $output -> getVerbosity ()) {
$class = get_debug_type ( $e );
$title = sprintf ( ' [%s%s] ' , $class , 0 !== ( $code = $e -> getCode ()) ? ' (' . $code . ')' : '' );
$len = Helper :: width ( $title );
} else {
$len = 0 ;
}
if (str_contains ( $message , "@anonymous\0" )) {
$message = preg_replace_callback ( '/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/' , function ( $m ) {
return class_exists ( $m [ 0 ], false ) ? ( get_parent_class ( $m [ 0 ]) ?: key ( class_implements ( $m [ 0 ])) ?: 'class' ). '@anonymous' : $m [ 0 ];
}, $message );
}
$width = $this -> terminal -> getWidth () ? $this -> terminal -> getWidth () - 1 : \PHP_INT_MAX ;
$lines = [];
foreach ('' !== $message ? preg_split ( '/\r?\n/' , $message ) : [] as $line ) {
foreach ($this -> splitStringByWidth ( $line , $width - 4 ) as $line ) {
// pre-format lines to get the right string length
$lineLength = Helper :: width ( $line ) + 4 ;
$lines [] = [ $line , $lineLength ];
$len = max ( $lineLength , $len );
}
}
$messages = [];
if (!$e instanceof ExceptionInterface || OutputInterface :: VERBOSITY_VERBOSE <= $output -> getVerbosity ()) {
$messages [] = sprintf ( '<comment>%s</comment>' , OutputFormatter :: escape ( sprintf ( 'In %s line %s:' , basename ( $e -> getFile ()) ?: 'n/a' , $e -> getLine () ?: 'n/a' )));
}
$messages [] = $emptyLine = sprintf ( '<error>%s</error>' , str_repeat ( ' ' , $len ));
if ('' === $message || OutputInterface :: VERBOSITY_VERBOSE <= $output -> getVerbosity ()) {
$messages [] = sprintf ( '<error>%s%s</error>' , $title , str_repeat ( ' ' , max ( 0 , $len - Helper :: width ( $title ))));
}
foreach ($lines as $line ) {
$messages [] = sprintf ( '<error> %s %s</error>' , OutputFormatter :: escape ( $line [ 0 ]), str_repeat ( ' ' , $len - $line [ 1 ]));
}
$messages [] = $emptyLine ;
$messages [] = '' ;
$output -> writeln ( $messages , OutputInterface :: VERBOSITY_QUIET );
if (OutputInterface :: VERBOSITY_VERBOSE <= $output -> getVerbosity ()) {
$output -> writeln ( '<comment>Exception trace:</comment>' , OutputInterface :: VERBOSITY_QUIET );
// exception related properties
$trace = $e -> getTrace ();
array_unshift ( $trace , [
'function' => '' ,
'file' => $e -> getFile () ?: 'n/a' ,
'line' => $e -> getLine () ?: 'n/a' ,
'args' => [],
]);
for ($i = 0 , $count = \count ( $trace ); $i < $count ; ++ $i ) {
$class = $trace [ $i ][ 'class' ] ?? '' ;
$type = $trace [ $i ][ 'type' ] ?? '' ;
$function = $trace [ $i ][ 'function' ] ?? '' ;
$file = $trace [ $i ][ 'file' ] ?? 'n/a' ;
$line = $trace [ $i ][ 'line' ] ?? 'n/a' ;
$output -> writeln ( sprintf ( ' %s%s at <info>%s:%s</info>' , $class , $function ? $type . $function . '()' : '' , $file , $line ), OutputInterface :: VERBOSITY_QUIET );
}
$output -> writeln ( '' , OutputInterface :: VERBOSITY_QUIET );
}
} while ($e = $e -> getPrevious ());
}
/**
* Configures the input and output instances based on the user arguments and options.
*/
protected function configureIO ( InputInterface $input , OutputInterface $output )
{
if (true === $input -> hasParameterOption ([ '--ansi' ], true )) {
$output -> setDecorated ( true );
} elseif (true === $input -> hasParameterOption ([ '--no-ansi' ], true )) {
$output -> setDecorated ( false );
}
if (true === $input -> hasParameterOption ([ '--no-interaction' , '-n' ], true )) {
$input -> setInteractive ( false );
}
switch ($shellVerbosity = (int) getenv ( 'SHELL_VERBOSITY' )) {
case -1 :
$output -> setVerbosity ( OutputInterface :: VERBOSITY_QUIET );
break;
case 1 :
$output -> setVerbosity ( OutputInterface :: VERBOSITY_VERBOSE );
break;
case 2 :
$output -> setVerbosity ( OutputInterface :: VERBOSITY_VERY_VERBOSE );
break;
case 3 :
$output -> setVerbosity ( OutputInterface :: VERBOSITY_DEBUG );
break;
default:
$shellVerbosity = 0 ;
break;
}
if (true === $input -> hasParameterOption ([ '--quiet' , '-q' ], true )) {
$output -> setVerbosity ( OutputInterface :: VERBOSITY_QUIET );
$shellVerbosity = - 1 ;
} else {
if ($input -> hasParameterOption ( '-vvv' , true ) || $input -> hasParameterOption ( '--verbose=3' , true ) || 3 === $input -> getParameterOption ( '--verbose' , false , true )) {
$output -> setVerbosity ( OutputInterface :: VERBOSITY_DEBUG );
$shellVerbosity = 3 ;
} elseif ($input -> hasParameterOption ( '-vv' , true ) || $input -> hasParameterOption ( '--verbose=2' , true ) || 2 === $input -> getParameterOption ( '--verbose' , false , true )) {
$output -> setVerbosity ( OutputInterface :: VERBOSITY_VERY_VERBOSE );
$shellVerbosity = 2 ;
} elseif ($input -> hasParameterOption ( '-v' , true ) || $input -> hasParameterOption ( '--verbose=1' , true ) || $input -> hasParameterOption ( '--verbose' , true ) || $input -> getParameterOption ( '--verbose' , false , true )) {
$output -> setVerbosity ( OutputInterface :: VERBOSITY_VERBOSE );
$shellVerbosity = 1 ;
}
}
if (-1 === $shellVerbosity ) {
$input -> setInteractive ( false );
}
if (\function_exists ( 'putenv' )) {
@putenv ( 'SHELL_VERBOSITY=' . $shellVerbosity );
}
$_ENV [ 'SHELL_VERBOSITY' ] = $shellVerbosity ;
$_SERVER [ 'SHELL_VERBOSITY' ] = $shellVerbosity ;
}
/**
* Runs the current command.
*
* If an event dispatcher has been attached to the application,
* events are also dispatched during the life-cycle of the command.
*
* @return int 0 if everything went fine, or an error code
*/
protected function doRunCommand ( Command $command , InputInterface $input , OutputInterface $output )
{
foreach ($command -> getHelperSet () as $helper ) {
if ($helper instanceof InputAwareInterface ) {
$helper -> setInput ( $input );
}
}
if ($this -> signalsToDispatchEvent ) {
$commandSignals = $command instanceof SignalableCommandInterface ? $command -> getSubscribedSignals () : [];
if ($commandSignals || null !== $this -> dispatcher ) {
if (!$this -> signalRegistry ) {
throw new RuntimeException ( 'Unable to subscribe to signal events. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.' );
}
if (Terminal :: hasSttyAvailable ()) {
$sttyMode = shell_exec ( 'stty -g' );
foreach ([\SIGINT , \SIGTERM ] as $signal ) {
$this -> signalRegistry -> register ( $signal , static function () use ( $sttyMode ) {
shell_exec ( 'stty ' . $sttyMode );
});
}
}
}
if (null !== $this -> dispatcher ) {
foreach ($this -> signalsToDispatchEvent as $signal ) {
$event = new ConsoleSignalEvent ( $command , $input , $output , $signal );
$this -> signalRegistry -> register ( $signal , function ( $signal , $hasNext ) use ( $event ) {
$this -> dispatcher -> dispatch ( $event , ConsoleEvents :: SIGNAL );
// No more handlers, we try to simulate PHP default behavior
if (! $hasNext ) {
if (!\in_array ( $signal , [ \SIGUSR1 , \SIGUSR2 ], true )) {
exit(0 );
}
}
});
}
}
foreach ($commandSignals as $signal ) {
$this -> signalRegistry -> register ( $signal , [ $command , 'handleSignal' ]);
}
}
if (null === $this -> dispatcher ) {
return $command -> run ( $input , $output );
}
// bind before the console.command event, so the listeners have access to input options/arguments
try {
$command -> mergeApplicationDefinition ();
$input -> bind ( $command -> getDefinition ());
} catch (ExceptionInterface $e ) {
// ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
}
$event = new ConsoleCommandEvent ( $command , $input , $output );
$e = null ;
try {
$this -> dispatcher -> dispatch ( $event , ConsoleEvents :: COMMAND );
if ($event -> commandShouldRun ()) {
$exitCode = $command -> run ( $input , $output );
} else {
$exitCode = ConsoleCommandEvent :: RETURN_CODE_DISABLED ;
}
} catch (\Throwable $e ) {
$event = new ConsoleErrorEvent ( $input , $output , $e , $command );
$this -> dispatcher -> dispatch ( $event , ConsoleEvents :: ERROR );
$e = $event -> getError ();
if (0 === $exitCode = $event -> getExitCode ()) {
$e = null ;
}
}
$event = new ConsoleTerminateEvent ( $command , $input , $output , $exitCode );
$this -> dispatcher -> dispatch ( $event , ConsoleEvents :: TERMINATE );
if (null !== $e ) {
throw $e ;
}
return $event -> getExitCode ();
}
/**
* Gets the name of the command based on input.
*
* @return string|null
*/
protected function getCommandName ( InputInterface $input )
{
return $this -> singleCommand ? $this -> defaultCommand : $input -> getFirstArgument ();
}
/**
* Gets the default input definition.
*
* @return InputDefinition
*/
protected function getDefaultInputDefinition ()
{
return new InputDefinition ([
new InputArgument ( 'command' , InputArgument :: REQUIRED , 'The command to execute' ),
new InputOption ( '--help' , '-h' , InputOption :: VALUE_NONE , 'Display help for the given command. When no command is given display help for the <info>' . $this -> defaultCommand . '</info> command' ),
new InputOption ( '--quiet' , '-q' , InputOption :: VALUE_NONE , 'Do not output any message' ),
new InputOption ( '--verbose' , '-v|vv|vvv' , InputOption :: VALUE_NONE , 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug' ),
new InputOption ( '--version' , '-V' , InputOption :: VALUE_NONE , 'Display this application version' ),
new InputOption ( '--ansi' , '' , InputOption :: VALUE_NEGATABLE , 'Force (or disable --no-ansi) ANSI output' , null ),
new InputOption ( '--no-interaction' , '-n' , InputOption :: VALUE_NONE , 'Do not ask any interactive question' ),
]);
}
/**
* Gets the default commands that should always be available.
*
* @return Command[]
*/
protected function getDefaultCommands ()
{
return [new HelpCommand (), new ListCommand (), new CompleteCommand (), new DumpCompletionCommand ()];
}
/**
* Gets the default helper set with the helpers that should always be available.
*
* @return HelperSet
*/
protected function getDefaultHelperSet ()
{
return new HelperSet ([
new FormatterHelper (),
new DebugFormatterHelper (),
new ProcessHelper (),
new QuestionHelper (),
]);
}
/**
* Returns abbreviated suggestions in string format.
*/
private function getAbbreviationSuggestions (array $abbrevs ): string
{
return ' ' . implode ( "\n " , $abbrevs );
}
/**
* Returns the namespace part of the command name.
*
* This method is not part of public API and should not be used directly.
*
* @return string
*/
public function extractNamespace ( string $name , int $limit = null )
{
$parts = explode ( ':' , $name , - 1 );
return implode ( ':' , null === $limit ? $parts : \array_slice ( $parts , 0 , $limit ));
}
/**
* Finds alternative of $name among $collection,
* if nothing is found in $collection, try in $abbrevs.
*
* @return string[]
*/
private function findAlternatives ( string $name , iterable $collection ): array
{
$threshold = 1e3 ;
$alternatives = [];
$collectionParts = [];
foreach ($collection as $item ) {
$collectionParts [ $item ] = explode ( ':' , $item );
}
foreach (explode ( ':' , $name ) as $i => $subname ) {
foreach ($collectionParts as $collectionName => $parts ) {
$exists = isset( $alternatives [ $collectionName ]);
if (!isset($parts [ $i ]) && $exists ) {
$alternatives [ $collectionName ] += $threshold ;
continue;
} elseif (!isset($parts [ $i ])) {
continue;
}
$lev = levenshtein ( $subname , $parts [ $i ]);
if ($lev <= \strlen ( $subname ) / 3 || '' !== $subname && str_contains ( $parts [ $i ], $subname )) {
$alternatives [ $collectionName ] = $exists ? $alternatives [ $collectionName ] + $lev : $lev ;
} elseif ($exists ) {
$alternatives [ $collectionName ] += $threshold ;
}
}
}
foreach ($collection as $item ) {
$lev = levenshtein ( $name , $item );
if ($lev <= \strlen ( $name ) / 3 || str_contains ( $item , $name )) {
$alternatives [ $item ] = isset( $alternatives [ $item ]) ? $alternatives [ $item ] - $lev : $lev ;
}
}
$alternatives = array_filter ( $alternatives , function ( $lev ) use ( $threshold ) { return $lev < 2 * $threshold ; });
ksort ( $alternatives , \SORT_NATURAL | \SORT_FLAG_CASE );
return array_keys ( $alternatives );
}
/**
* Sets the default Command name.
*
* @return $this
*/
public function setDefaultCommand ( string $commandName , bool $isSingleCommand = false )
{
$this -> defaultCommand = explode ( '|' , ltrim ( $commandName , '|' ))[ 0 ];
if ($isSingleCommand ) {
// Ensure the command exist
$this -> find ( $commandName );
$this -> singleCommand = true ;
}
return $this ;
}
/**
* @internal
*/
public function isSingleCommand (): bool
{
return $this -> singleCommand ;
}
private function splitStringByWidth ( string $string , int $width ): array
{
// str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
// additionally, array_slice() is not enough as some character has doubled width.
// we need a function to split string not by character count but by string width
if ( false === $encoding = mb_detect_encoding ( $string , null , true )) {
return str_split ( $string , $width );
}
$utf8String = mb_convert_encoding ( $string , 'utf8' , $encoding );
$lines = [];
$line = '' ;
$offset = 0 ;
while (preg_match ( '/.{1,10000}/u' , $utf8String , $m , 0 , $offset )) {
$offset += \strlen ( $m [ 0 ]);
foreach (preg_split ( '//u' , $m [ 0 ]) as $char ) {
// test if $char could be appended to current line
if ( mb_strwidth ( $line . $char , 'utf8' ) <= $width ) {
$line .= $char ;
continue;
}
// if not, push current line to array and make new line
$lines [] = str_pad ( $line , $width );
$line = $char ;
}
}
$lines [] = \count ( $lines ) ? str_pad ( $line , $width ) : $line ;
mb_convert_variables ( $encoding , 'utf8' , $lines );
return $lines ;
}
/**
* Returns all namespaces of the command name.
*
* @return string[]
*/
private function extractAllNamespaces ( string $name ): array
{
// -1 as third argument is needed to skip the command short name when exploding
$parts = explode ( ':' , $name , - 1 );
$namespaces = [];
foreach ($parts as $part ) {
if (\count ( $namespaces )) {
$namespaces [] = end ( $namespaces ). ':' . $part ;
} else {
$namespaces [] = $part ;
}
}
return $namespaces ;
}
private function init ()
{
if ($this -> initialized ) {
return;
}
$this -> initialized = true ;
foreach ($this -> getDefaultCommands () as $command ) {
$this -> add ( $command );
}
}
}