vendor/symfony/src/Symfony/Component/Console/Command/Command.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Component\Console\Command;
       
    13 
       
    14 use Symfony\Component\Console\Input\InputDefinition;
       
    15 use Symfony\Component\Console\Input\InputOption;
       
    16 use Symfony\Component\Console\Input\InputArgument;
       
    17 use Symfony\Component\Console\Input\InputInterface;
       
    18 use Symfony\Component\Console\Output\OutputInterface;
       
    19 use Symfony\Component\Console\Application;
       
    20 use Symfony\Component\Console\Helper\HelperSet;
       
    21 
       
    22 /**
       
    23  * Base class for all commands.
       
    24  *
       
    25  * @author Fabien Potencier <fabien@symfony.com>
       
    26  *
       
    27  * @api
       
    28  */
       
    29 class Command
       
    30 {
       
    31     private $application;
       
    32     private $name;
       
    33     private $aliases;
       
    34     private $definition;
       
    35     private $help;
       
    36     private $description;
       
    37     private $ignoreValidationErrors;
       
    38     private $applicationDefinitionMerged;
       
    39     private $code;
       
    40     private $synopsis;
       
    41     private $helperSet;
       
    42 
       
    43     /**
       
    44      * Constructor.
       
    45      *
       
    46      * @param string $name The name of the command
       
    47      *
       
    48      * @throws \LogicException When the command name is empty
       
    49      *
       
    50      * @api
       
    51      */
       
    52     public function __construct($name = null)
       
    53     {
       
    54         $this->definition = new InputDefinition();
       
    55         $this->ignoreValidationErrors = false;
       
    56         $this->applicationDefinitionMerged = false;
       
    57         $this->aliases = array();
       
    58 
       
    59         if (null !== $name) {
       
    60             $this->setName($name);
       
    61         }
       
    62 
       
    63         $this->configure();
       
    64 
       
    65         if (!$this->name) {
       
    66             throw new \LogicException('The command name cannot be empty.');
       
    67         }
       
    68     }
       
    69 
       
    70     /**
       
    71      * Sets the application instance for this command.
       
    72      *
       
    73      * @param Application $application An Application instance
       
    74      *
       
    75      * @api
       
    76      */
       
    77     public function setApplication(Application $application = null)
       
    78     {
       
    79         $this->application = $application;
       
    80         if ($application) {
       
    81             $this->setHelperSet($application->getHelperSet());
       
    82         } else {
       
    83             $this->helperSet = null;
       
    84         }
       
    85     }
       
    86 
       
    87     /**
       
    88      * Sets the helper set.
       
    89      *
       
    90      * @param HelperSet $helperSet A HelperSet instance
       
    91      */
       
    92     public function setHelperSet(HelperSet $helperSet)
       
    93     {
       
    94         $this->helperSet = $helperSet;
       
    95     }
       
    96 
       
    97     /**
       
    98      * Gets the helper set.
       
    99      *
       
   100      * @return HelperSet A HelperSet instance
       
   101      */
       
   102     public function getHelperSet()
       
   103     {
       
   104         return $this->helperSet;
       
   105     }
       
   106 
       
   107     /**
       
   108      * Gets the application instance for this command.
       
   109      *
       
   110      * @return Application An Application instance
       
   111      *
       
   112      * @api
       
   113      */
       
   114     public function getApplication()
       
   115     {
       
   116         return $this->application;
       
   117     }
       
   118 
       
   119     /**
       
   120      * Configures the current command.
       
   121      */
       
   122     protected function configure()
       
   123     {
       
   124     }
       
   125 
       
   126     /**
       
   127      * Executes the current command.
       
   128      *
       
   129      * This method is not abstract because you can use this class
       
   130      * as a concrete class. In this case, instead of defining the
       
   131      * execute() method, you set the code to execute by passing
       
   132      * a Closure to the setCode() method.
       
   133      *
       
   134      * @param InputInterface  $input  An InputInterface instance
       
   135      * @param OutputInterface $output An OutputInterface instance
       
   136      *
       
   137      * @return integer 0 if everything went fine, or an error code
       
   138      *
       
   139      * @throws \LogicException When this abstract method is not implemented
       
   140      * @see    setCode()
       
   141      */
       
   142     protected function execute(InputInterface $input, OutputInterface $output)
       
   143     {
       
   144         throw new \LogicException('You must override the execute() method in the concrete command class.');
       
   145     }
       
   146 
       
   147     /**
       
   148      * Interacts with the user.
       
   149      *
       
   150      * @param InputInterface  $input  An InputInterface instance
       
   151      * @param OutputInterface $output An OutputInterface instance
       
   152      */
       
   153     protected function interact(InputInterface $input, OutputInterface $output)
       
   154     {
       
   155     }
       
   156 
       
   157     /**
       
   158      * Initializes the command just after the input has been validated.
       
   159      *
       
   160      * This is mainly useful when a lot of commands extends one main command
       
   161      * where some things need to be initialized based on the input arguments and options.
       
   162      *
       
   163      * @param InputInterface  $input  An InputInterface instance
       
   164      * @param OutputInterface $output An OutputInterface instance
       
   165      */
       
   166     protected function initialize(InputInterface $input, OutputInterface $output)
       
   167     {
       
   168     }
       
   169 
       
   170     /**
       
   171      * Runs the command.
       
   172      *
       
   173      * The code to execute is either defined directly with the
       
   174      * setCode() method or by overriding the execute() method
       
   175      * in a sub-class.
       
   176      *
       
   177      * @param InputInterface  $input  An InputInterface instance
       
   178      * @param OutputInterface $output An OutputInterface instance
       
   179      *
       
   180      * @see setCode()
       
   181      * @see execute()
       
   182      *
       
   183      * @api
       
   184      */
       
   185     public function run(InputInterface $input, OutputInterface $output)
       
   186     {
       
   187         // force the creation of the synopsis before the merge with the app definition
       
   188         $this->getSynopsis();
       
   189 
       
   190         // add the application arguments and options
       
   191         $this->mergeApplicationDefinition();
       
   192 
       
   193         // bind the input against the command specific arguments/options
       
   194         try {
       
   195             $input->bind($this->definition);
       
   196         } catch (\Exception $e) {
       
   197             if (!$this->ignoreValidationErrors) {
       
   198                 throw $e;
       
   199             }
       
   200         }
       
   201 
       
   202         $this->initialize($input, $output);
       
   203 
       
   204         if ($input->isInteractive()) {
       
   205             $this->interact($input, $output);
       
   206         }
       
   207 
       
   208         $input->validate();
       
   209 
       
   210         if ($this->code) {
       
   211             return call_user_func($this->code, $input, $output);
       
   212         }
       
   213 
       
   214         return $this->execute($input, $output);
       
   215     }
       
   216 
       
   217     /**
       
   218      * Sets the code to execute when running this command.
       
   219      *
       
   220      * If this method is used, it overrides the code defined
       
   221      * in the execute() method.
       
   222      *
       
   223      * @param \Closure $code A \Closure
       
   224      *
       
   225      * @return Command The current instance
       
   226      *
       
   227      * @see execute()
       
   228      *
       
   229      * @api
       
   230      */
       
   231     public function setCode(\Closure $code)
       
   232     {
       
   233         $this->code = $code;
       
   234 
       
   235         return $this;
       
   236     }
       
   237 
       
   238     /**
       
   239      * Merges the application definition with the command definition.
       
   240      */
       
   241     private function mergeApplicationDefinition()
       
   242     {
       
   243         if (null === $this->application || true === $this->applicationDefinitionMerged) {
       
   244             return;
       
   245         }
       
   246 
       
   247         $this->definition->setArguments(array_merge(
       
   248             $this->application->getDefinition()->getArguments(),
       
   249             $this->definition->getArguments()
       
   250         ));
       
   251 
       
   252         $this->definition->addOptions($this->application->getDefinition()->getOptions());
       
   253 
       
   254         $this->applicationDefinitionMerged = true;
       
   255     }
       
   256 
       
   257     /**
       
   258      * Sets an array of argument and option instances.
       
   259      *
       
   260      * @param array|Definition $definition An array of argument and option instances or a definition instance
       
   261      *
       
   262      * @return Command The current instance
       
   263      *
       
   264      * @api
       
   265      */
       
   266     public function setDefinition($definition)
       
   267     {
       
   268         if ($definition instanceof InputDefinition) {
       
   269             $this->definition = $definition;
       
   270         } else {
       
   271             $this->definition->setDefinition($definition);
       
   272         }
       
   273 
       
   274         $this->applicationDefinitionMerged = false;
       
   275 
       
   276         return $this;
       
   277     }
       
   278 
       
   279     /**
       
   280      * Gets the InputDefinition attached to this Command.
       
   281      *
       
   282      * @return InputDefinition An InputDefinition instance
       
   283      *
       
   284      * @api
       
   285      */
       
   286     public function getDefinition()
       
   287     {
       
   288         return $this->definition;
       
   289     }
       
   290 
       
   291     /**
       
   292      * Adds an argument.
       
   293      *
       
   294      * @param string  $name        The argument name
       
   295      * @param integer $mode        The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
       
   296      * @param string  $description A description text
       
   297      * @param mixed   $default     The default value (for InputArgument::OPTIONAL mode only)
       
   298      *
       
   299      * @return Command The current instance
       
   300      *
       
   301      * @api
       
   302      */
       
   303     public function addArgument($name, $mode = null, $description = '', $default = null)
       
   304     {
       
   305         $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
       
   306 
       
   307         return $this;
       
   308     }
       
   309 
       
   310     /**
       
   311      * Adds an option.
       
   312      *
       
   313      * @param string  $name        The option name
       
   314      * @param string  $shortcut    The shortcut (can be null)
       
   315      * @param integer $mode        The option mode: One of the InputOption::VALUE_* constants
       
   316      * @param string  $description A description text
       
   317      * @param mixed   $default     The default value (must be null for InputOption::VALUE_REQUIRED or self::VALUE_NONE)
       
   318      *
       
   319      * @return Command The current instance
       
   320      *
       
   321      * @api
       
   322      */
       
   323     public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
       
   324     {
       
   325         $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
       
   326 
       
   327         return $this;
       
   328     }
       
   329 
       
   330     /**
       
   331      * Sets the name of the command.
       
   332      *
       
   333      * This method can set both the namespace and the name if
       
   334      * you separate them by a colon (:)
       
   335      *
       
   336      *     $command->setName('foo:bar');
       
   337      *
       
   338      * @param string $name The command name
       
   339      *
       
   340      * @return Command The current instance
       
   341      *
       
   342      * @throws \InvalidArgumentException When command name given is empty
       
   343      *
       
   344      * @api
       
   345      */
       
   346     public function setName($name)
       
   347     {
       
   348         $this->validateName($name);
       
   349 
       
   350         $this->name = $name;
       
   351 
       
   352         return $this;
       
   353     }
       
   354 
       
   355     /**
       
   356      * Returns the command name.
       
   357      *
       
   358      * @return string The command name
       
   359      *
       
   360      * @api
       
   361      */
       
   362     public function getName()
       
   363     {
       
   364         return $this->name;
       
   365     }
       
   366 
       
   367     /**
       
   368      * Sets the description for the command.
       
   369      *
       
   370      * @param string $description The description for the command
       
   371      *
       
   372      * @return Command The current instance
       
   373      *
       
   374      * @api
       
   375      */
       
   376     public function setDescription($description)
       
   377     {
       
   378         $this->description = $description;
       
   379 
       
   380         return $this;
       
   381     }
       
   382 
       
   383     /**
       
   384      * Returns the description for the command.
       
   385      *
       
   386      * @return string The description for the command
       
   387      *
       
   388      * @api
       
   389      */
       
   390     public function getDescription()
       
   391     {
       
   392         return $this->description;
       
   393     }
       
   394 
       
   395     /**
       
   396      * Sets the help for the command.
       
   397      *
       
   398      * @param string $help The help for the command
       
   399      *
       
   400      * @return Command The current instance
       
   401      *
       
   402      * @api
       
   403      */
       
   404     public function setHelp($help)
       
   405     {
       
   406         $this->help = $help;
       
   407 
       
   408         return $this;
       
   409     }
       
   410 
       
   411     /**
       
   412      * Returns the help for the command.
       
   413      *
       
   414      * @return string The help for the command
       
   415      *
       
   416      * @api
       
   417      */
       
   418     public function getHelp()
       
   419     {
       
   420         return $this->help;
       
   421     }
       
   422 
       
   423     /**
       
   424      * Returns the processed help for the command replacing the %command.name% and
       
   425      * %command.full_name% patterns with the real values dynamically.
       
   426      *
       
   427      * @return string  The processed help for the command
       
   428      */
       
   429     public function getProcessedHelp()
       
   430     {
       
   431         $name = $this->name;
       
   432 
       
   433         $placeholders = array(
       
   434             '%command.name%',
       
   435             '%command.full_name%'
       
   436         );
       
   437         $replacements = array(
       
   438             $name,
       
   439             $_SERVER['PHP_SELF'].' '.$name
       
   440         );
       
   441 
       
   442         return str_replace($placeholders, $replacements, $this->getHelp());
       
   443     }
       
   444 
       
   445     /**
       
   446      * Sets the aliases for the command.
       
   447      *
       
   448      * @param array $aliases An array of aliases for the command
       
   449      *
       
   450      * @return Command The current instance
       
   451      *
       
   452      * @api
       
   453      */
       
   454     public function setAliases($aliases)
       
   455     {
       
   456         foreach ($aliases as $alias) {
       
   457             $this->validateName($alias);
       
   458         }
       
   459 
       
   460         $this->aliases = $aliases;
       
   461 
       
   462         return $this;
       
   463     }
       
   464 
       
   465     /**
       
   466      * Returns the aliases for the command.
       
   467      *
       
   468      * @return array An array of aliases for the command
       
   469      *
       
   470      * @api
       
   471      */
       
   472     public function getAliases()
       
   473     {
       
   474         return $this->aliases;
       
   475     }
       
   476 
       
   477     /**
       
   478      * Returns the synopsis for the command.
       
   479      *
       
   480      * @return string The synopsis
       
   481      */
       
   482     public function getSynopsis()
       
   483     {
       
   484         if (null === $this->synopsis) {
       
   485             $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis()));
       
   486         }
       
   487 
       
   488         return $this->synopsis;
       
   489     }
       
   490 
       
   491     /**
       
   492      * Gets a helper instance by name.
       
   493      *
       
   494      * @param string $name The helper name
       
   495      *
       
   496      * @return mixed The helper value
       
   497      *
       
   498      * @throws \InvalidArgumentException if the helper is not defined
       
   499      *
       
   500      * @api
       
   501      */
       
   502     public function getHelper($name)
       
   503     {
       
   504         return $this->helperSet->get($name);
       
   505     }
       
   506 
       
   507     /**
       
   508      * Returns a text representation of the command.
       
   509      *
       
   510      * @return string A string representing the command
       
   511      */
       
   512     public function asText()
       
   513     {
       
   514         $messages = array(
       
   515             '<comment>Usage:</comment>',
       
   516             ' '.$this->getSynopsis(),
       
   517             '',
       
   518         );
       
   519 
       
   520         if ($this->getAliases()) {
       
   521             $messages[] = '<comment>Aliases:</comment> <info>'.implode(', ', $this->getAliases()).'</info>';
       
   522         }
       
   523 
       
   524         $messages[] = $this->definition->asText();
       
   525 
       
   526         if ($help = $this->getProcessedHelp()) {
       
   527             $messages[] = '<comment>Help:</comment>';
       
   528             $messages[] = ' '.implode("\n ", explode("\n", $help))."\n";
       
   529         }
       
   530 
       
   531         return implode("\n", $messages);
       
   532     }
       
   533 
       
   534     /**
       
   535      * Returns an XML representation of the command.
       
   536      *
       
   537      * @param Boolean $asDom Whether to return a DOM or an XML string
       
   538      *
       
   539      * @return string|DOMDocument An XML string representing the command
       
   540      */
       
   541     public function asXml($asDom = false)
       
   542     {
       
   543         $dom = new \DOMDocument('1.0', 'UTF-8');
       
   544         $dom->formatOutput = true;
       
   545         $dom->appendChild($commandXML = $dom->createElement('command'));
       
   546         $commandXML->setAttribute('id', $this->name);
       
   547         $commandXML->setAttribute('name', $this->name);
       
   548 
       
   549         $commandXML->appendChild($usageXML = $dom->createElement('usage'));
       
   550         $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), '')));
       
   551 
       
   552         $commandXML->appendChild($descriptionXML = $dom->createElement('description'));
       
   553         $descriptionXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $this->getDescription()))));
       
   554 
       
   555         $commandXML->appendChild($helpXML = $dom->createElement('help'));
       
   556         $help = $this->help;
       
   557         $helpXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $help))));
       
   558 
       
   559         $commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
       
   560         foreach ($this->getAliases() as $alias) {
       
   561             $aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
       
   562             $aliasXML->appendChild($dom->createTextNode($alias));
       
   563         }
       
   564 
       
   565         $definition = $this->definition->asXml(true);
       
   566         $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true));
       
   567         $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true));
       
   568 
       
   569         return $asDom ? $dom : $dom->saveXml();
       
   570     }
       
   571 
       
   572     private function validateName($name)
       
   573     {
       
   574         if (!preg_match('/^[^\:]+(\:[^\:]+)*$/', $name)) {
       
   575             throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
       
   576         }
       
   577     }
       
   578 }