2015-08-17 17:00:26 -07:00
< ? 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\Command ;
use Symfony\Component\Console\Descriptor\TextDescriptor ;
use Symfony\Component\Console\Descriptor\XmlDescriptor ;
use Symfony\Component\Console\Input\InputDefinition ;
use Symfony\Component\Console\Input\InputOption ;
use Symfony\Component\Console\Input\InputArgument ;
use Symfony\Component\Console\Input\InputInterface ;
use Symfony\Component\Console\Output\BufferedOutput ;
use Symfony\Component\Console\Output\OutputInterface ;
use Symfony\Component\Console\Application ;
use Symfony\Component\Console\Helper\HelperSet ;
/**
* Base class for all commands .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*
* @ api
*/
class Command
{
private $application ;
private $name ;
private $processTitle ;
private $aliases = array ();
private $definition ;
private $help ;
private $description ;
private $ignoreValidationErrors = false ;
private $applicationDefinitionMerged = false ;
private $applicationDefinitionMergedWithArgs = false ;
private $code ;
private $synopsis = array ();
private $usages = array ();
private $helperSet ;
/**
* Constructor .
*
* @ param string | null $name The name of the command ; passing null means it must be set in configure ()
*
* @ throws \LogicException When the command name is empty
*
* @ api
*/
public function __construct ( $name = null )
{
$this -> definition = new InputDefinition ();
if ( null !== $name ) {
$this -> setName ( $name );
}
$this -> configure ();
if ( ! $this -> name ) {
throw new \LogicException ( sprintf ( 'The command defined in "%s" cannot have an empty name.' , get_class ( $this )));
}
}
/**
* Ignores validation errors .
*
* This is mainly useful for the help command .
*/
public function ignoreValidationErrors ()
{
$this -> ignoreValidationErrors = true ;
}
/**
* Sets the application instance for this command .
*
* @ param Application $application An Application instance
*
* @ api
*/
public function setApplication ( Application $application = null )
{
$this -> application = $application ;
if ( $application ) {
$this -> setHelperSet ( $application -> getHelperSet ());
} else {
$this -> helperSet = null ;
}
}
/**
* Sets the helper set .
*
* @ param HelperSet $helperSet A HelperSet instance
*/
public function setHelperSet ( HelperSet $helperSet )
{
$this -> helperSet = $helperSet ;
}
/**
* Gets the helper set .
*
* @ return HelperSet A HelperSet instance
*/
public function getHelperSet ()
{
return $this -> helperSet ;
}
/**
* Gets the application instance for this command .
*
* @ return Application An Application instance
*
* @ api
*/
public function getApplication ()
{
return $this -> application ;
}
/**
* Checks whether the command is enabled or not in the current environment .
*
* Override this to check for x or y and return false if the command can not
* run properly under the current conditions .
*
* @ return bool
*/
public function isEnabled ()
{
return true ;
}
/**
* Configures the current command .
*/
protected function configure ()
{
}
/**
* Executes the current command .
*
* This method is not abstract because you can use this class
* as a concrete class . In this case , instead of defining the
* execute () method , you set the code to execute by passing
* a Closure to the setCode () method .
*
* @ param InputInterface $input An InputInterface instance
* @ param OutputInterface $output An OutputInterface instance
*
* @ return null | int null or 0 if everything went fine , or an error code
*
* @ throws \LogicException When this abstract method is not implemented
*
* @ see setCode ()
*/
protected function execute ( InputInterface $input , OutputInterface $output )
{
throw new \LogicException ( 'You must override the execute() method in the concrete command class.' );
}
/**
* Interacts with the user .
*
* This method is executed before the InputDefinition is validated .
* This means that this is the only place where the command can
* interactively ask for values of missing required arguments .
*
* @ param InputInterface $input An InputInterface instance
* @ param OutputInterface $output An OutputInterface instance
*/
protected function interact ( InputInterface $input , OutputInterface $output )
{
}
/**
* Initializes the command just after the input has been validated .
*
* This is mainly useful when a lot of commands extends one main command
* where some things need to be initialized based on the input arguments and options .
*
* @ param InputInterface $input An InputInterface instance
* @ param OutputInterface $output An OutputInterface instance
*/
protected function initialize ( InputInterface $input , OutputInterface $output )
{
}
/**
* Runs the command .
*
* The code to execute is either defined directly with the
* setCode () method or by overriding the execute () method
* in a sub - class .
*
* @ param InputInterface $input An InputInterface instance
* @ param OutputInterface $output An OutputInterface instance
*
* @ return int The command exit code
*
* @ throws \Exception
*
* @ see setCode ()
* @ see execute ()
*
* @ api
*/
public function run ( InputInterface $input , OutputInterface $output )
{
// force the creation of the synopsis before the merge with the app definition
$this -> getSynopsis ( true );
$this -> getSynopsis ( false );
// add the application arguments and options
$this -> mergeApplicationDefinition ();
// bind the input against the command specific arguments/options
try {
$input -> bind ( $this -> definition );
} catch ( \Exception $e ) {
if ( ! $this -> ignoreValidationErrors ) {
throw $e ;
}
}
$this -> initialize ( $input , $output );
if ( null !== $this -> processTitle ) {
if ( function_exists ( 'cli_set_process_title' )) {
cli_set_process_title ( $this -> processTitle );
} elseif ( function_exists ( 'setproctitle' )) {
setproctitle ( $this -> processTitle );
} elseif ( OutputInterface :: VERBOSITY_VERY_VERBOSE === $output -> getVerbosity ()) {
$output -> writeln ( '<comment>Install the proctitle PECL to be able to change the process title.</comment>' );
}
}
if ( $input -> isInteractive ()) {
$this -> interact ( $input , $output );
}
$input -> validate ();
if ( $this -> code ) {
$statusCode = call_user_func ( $this -> code , $input , $output );
} else {
$statusCode = $this -> execute ( $input , $output );
}
return is_numeric ( $statusCode ) ? ( int ) $statusCode : 0 ;
}
/**
* Sets the code to execute when running this command .
*
* If this method is used , it overrides the code defined
* in the execute () method .
*
* @ param callable $code A callable ( InputInterface $input , OutputInterface $output )
*
* @ return Command The current instance
*
* @ throws \InvalidArgumentException
*
* @ see execute ()
*
* @ api
*/
public function setCode ( $code )
{
if ( ! is_callable ( $code )) {
throw new \InvalidArgumentException ( 'Invalid callable provided to Command::setCode.' );
}
$this -> code = $code ;
return $this ;
}
/**
* Merges the application definition with the command definition .
*
* This method is not part of public API and should not be used directly .
*
* @ param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
*/
public function mergeApplicationDefinition ( $mergeArgs = true )
{
if ( null === $this -> application || ( true === $this -> applicationDefinitionMerged && ( $this -> applicationDefinitionMergedWithArgs || ! $mergeArgs ))) {
return ;
}
if ( $mergeArgs ) {
$currentArguments = $this -> definition -> getArguments ();
$this -> definition -> setArguments ( $this -> application -> getDefinition () -> getArguments ());
$this -> definition -> addArguments ( $currentArguments );
}
$this -> definition -> addOptions ( $this -> application -> getDefinition () -> getOptions ());
$this -> applicationDefinitionMerged = true ;
if ( $mergeArgs ) {
$this -> applicationDefinitionMergedWithArgs = true ;
}
}
/**
* Sets an array of argument and option instances .
*
* @ param array | InputDefinition $definition An array of argument and option instances or a definition instance
*
* @ return Command The current instance
*
* @ api
*/
public function setDefinition ( $definition )
{
if ( $definition instanceof InputDefinition ) {
$this -> definition = $definition ;
} else {
$this -> definition -> setDefinition ( $definition );
}
$this -> applicationDefinitionMerged = false ;
return $this ;
}
/**
* Gets the InputDefinition attached to this Command .
*
* @ return InputDefinition An InputDefinition instance
*
* @ api
*/
public function getDefinition ()
{
return $this -> definition ;
}
/**
* Gets the InputDefinition to be used to create XML and Text representations of this Command .
*
* Can be overridden to provide the original command representation when it would otherwise
* be changed by merging with the application InputDefinition .
*
* This method is not part of public API and should not be used directly .
*
* @ return InputDefinition An InputDefinition instance
*/
public function getNativeDefinition ()
{
return $this -> getDefinition ();
}
/**
* Adds an argument .
*
* @ param string $name The argument name
* @ param int $mode The argument mode : InputArgument :: REQUIRED or InputArgument :: OPTIONAL
* @ param string $description A description text
* @ param mixed $default The default value ( for InputArgument :: OPTIONAL mode only )
*
* @ return Command The current instance
*
* @ api
*/
public function addArgument ( $name , $mode = null , $description = '' , $default = null )
{
$this -> definition -> addArgument ( new InputArgument ( $name , $mode , $description , $default ));
return $this ;
}
/**
* Adds an option .
*
* @ param string $name The option name
* @ param string $shortcut The shortcut ( can be null )
* @ param int $mode The option mode : One of the InputOption :: VALUE_ * constants
* @ param string $description A description text
* @ param mixed $default The default value ( must be null for InputOption :: VALUE_REQUIRED or InputOption :: VALUE_NONE )
*
* @ return Command The current instance
*
* @ api
*/
public function addOption ( $name , $shortcut = null , $mode = null , $description = '' , $default = null )
{
$this -> definition -> addOption ( new InputOption ( $name , $shortcut , $mode , $description , $default ));
return $this ;
}
/**
* Sets the name of the command .
*
* This method can set both the namespace and the name if
* you separate them by a colon ( : )
*
* $command -> setName ( 'foo:bar' );
*
* @ param string $name The command name
*
* @ return Command The current instance
*
* @ throws \InvalidArgumentException When the name is invalid
*
* @ api
*/
public function setName ( $name )
{
$this -> validateName ( $name );
$this -> name = $name ;
return $this ;
}
/**
* Sets the process title of the command .
*
* This feature should be used only when creating a long process command ,
* like a daemon .
*
* PHP 5.5 + or the proctitle PECL library is required
*
* @ param string $title The process title
*
* @ return Command The current instance
*/
public function setProcessTitle ( $title )
{
$this -> processTitle = $title ;
return $this ;
}
/**
* Returns the command name .
*
* @ return string The command name
*
* @ api
*/
public function getName ()
{
return $this -> name ;
}
/**
* Sets the description for the command .
*
* @ param string $description The description for the command
*
* @ return Command The current instance
*
* @ api
*/
public function setDescription ( $description )
{
$this -> description = $description ;
return $this ;
}
/**
* Returns the description for the command .
*
* @ return string The description for the command
*
* @ api
*/
public function getDescription ()
{
return $this -> description ;
}
/**
* Sets the help for the command .
*
* @ param string $help The help for the command
*
* @ return Command The current instance
*
* @ api
*/
public function setHelp ( $help )
{
$this -> help = $help ;
return $this ;
}
/**
* Returns the help for the command .
*
* @ return string The help for the command
*
* @ api
*/
public function getHelp ()
{
2015-10-08 11:40:12 -07:00
return $this -> help ? : $this -> description ;
2015-08-17 17:00:26 -07:00
}
/**
* Returns the processed help for the command replacing the % command . name % and
* % command . full_name % patterns with the real values dynamically .
*
* @ return string The processed help for the command
*/
public function getProcessedHelp ()
{
$name = $this -> name ;
$placeholders = array (
'%command.name%' ,
'%command.full_name%' ,
);
$replacements = array (
$name ,
$_SERVER [ 'PHP_SELF' ] . ' ' . $name ,
);
return str_replace ( $placeholders , $replacements , $this -> getHelp ());
}
/**
* Sets the aliases for the command .
*
* @ param string [] $aliases An array of aliases for the command
*
* @ return Command The current instance
*
* @ throws \InvalidArgumentException When an alias is invalid
*
* @ api
*/
public function setAliases ( $aliases )
{
if ( ! is_array ( $aliases ) && ! $aliases instanceof \Traversable ) {
throw new \InvalidArgumentException ( '$aliases must be an array or an instance of \Traversable' );
}
foreach ( $aliases as $alias ) {
$this -> validateName ( $alias );
}
$this -> aliases = $aliases ;
return $this ;
}
/**
* Returns the aliases for the command .
*
* @ return array An array of aliases for the command
*
* @ api
*/
public function getAliases ()
{
return $this -> aliases ;
}
/**
* Returns the synopsis for the command .
*
* @ param bool $short Whether to show the short version of the synopsis ( with options folded ) or not
*
* @ return string The synopsis
*/
public function getSynopsis ( $short = false )
{
$key = $short ? 'short' : 'long' ;
if ( ! isset ( $this -> synopsis [ $key ])) {
$this -> synopsis [ $key ] = trim ( sprintf ( '%s %s' , $this -> name , $this -> definition -> getSynopsis ( $short )));
}
return $this -> synopsis [ $key ];
}
/**
* Add a command usage example .
*
* @ param string $usage The usage , it ' ll be prefixed with the command name
*/
public function addUsage ( $usage )
{
if ( 0 !== strpos ( $usage , $this -> name )) {
$usage = sprintf ( '%s %s' , $this -> name , $usage );
}
$this -> usages [] = $usage ;
return $this ;
}
/**
* Returns alternative usages of the command .
*
* @ return array
*/
public function getUsages ()
{
return $this -> usages ;
}
/**
* Gets a helper instance by name .
*
* @ param string $name The helper name
*
* @ return mixed The helper value
*
* @ throws \InvalidArgumentException if the helper is not defined
*
* @ api
*/
public function getHelper ( $name )
{
return $this -> helperSet -> get ( $name );
}
/**
* Returns a text representation of the command .
*
* @ return string A string representing the command
*
* @ deprecated since version 2.3 , to be removed in 3.0 .
*/
public function asText ()
{
2015-08-27 12:03:05 -07:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.3 and will be removed in 3.0.' , E_USER_DEPRECATED );
2015-08-17 17:00:26 -07:00
$descriptor = new TextDescriptor ();
$output = new BufferedOutput ( BufferedOutput :: VERBOSITY_NORMAL , true );
$descriptor -> describe ( $output , $this , array ( 'raw_output' => true ));
return $output -> fetch ();
}
/**
* Returns an XML representation of the command .
*
* @ param bool $asDom Whether to return a DOM or an XML string
*
* @ return string | \DOMDocument An XML string representing the command
*
* @ deprecated since version 2.3 , to be removed in 3.0 .
*/
public function asXml ( $asDom = false )
{
2015-08-27 12:03:05 -07:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.3 and will be removed in 3.0.' , E_USER_DEPRECATED );
2015-08-17 17:00:26 -07:00
$descriptor = new XmlDescriptor ();
if ( $asDom ) {
return $descriptor -> getCommandDocument ( $this );
}
$output = new BufferedOutput ();
$descriptor -> describe ( $output , $this );
return $output -> fetch ();
}
/**
* Validates a command name .
*
* It must be non - empty and parts can optionally be separated by " : " .
*
* @ param string $name
*
* @ throws \InvalidArgumentException When the name is invalid
*/
private function validateName ( $name )
{
if ( ! preg_match ( '/^[^\:]++(\:[^\:]++)*$/' , $name )) {
throw new \InvalidArgumentException ( sprintf ( 'Command name "%s" is invalid.' , $name ));
}
}
}