Refactoring
This commit is contained in:
346
lib/composer/vendor/consolidation/annotated-command/src/AnnotatedCommand.php
vendored
Normal file
346
lib/composer/vendor/consolidation/annotated-command/src/AnnotatedCommand.php
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
use Consolidation\AnnotatedCommand\Hooks\HookManager;
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
use Consolidation\OutputFormatters\FormatterManager;
|
||||
use Consolidation\OutputFormatters\Options\FormatterOptions;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* AnnotatedCommands are created automatically by the
|
||||
* AnnotatedCommandFactory. Each command method in a
|
||||
* command file will produce one AnnotatedCommand. These
|
||||
* are then added to your Symfony Console Application object;
|
||||
* nothing else is needed.
|
||||
*
|
||||
* Optionally, though, you may extend AnnotatedCommand directly
|
||||
* to make a single command. The usage pattern is the same
|
||||
* as for any other Symfony Console command, except that you may
|
||||
* omit the 'Confiure' method, and instead place your annotations
|
||||
* on the execute() method.
|
||||
*
|
||||
* @package Consolidation\AnnotatedCommand
|
||||
*/
|
||||
class AnnotatedCommand extends Command
|
||||
{
|
||||
protected $commandCallback;
|
||||
protected $commandProcessor;
|
||||
protected $annotationData;
|
||||
protected $usesInputInterface;
|
||||
protected $usesOutputInterface;
|
||||
protected $returnType;
|
||||
|
||||
public function __construct($name = null)
|
||||
{
|
||||
$commandInfo = false;
|
||||
|
||||
// If this is a subclass of AnnotatedCommand, check to see
|
||||
// if the 'execute' method is annotated. We could do this
|
||||
// unconditionally; it is a performance optimization to skip
|
||||
// checking the annotations if $this is an instance of
|
||||
// AnnotatedCommand. Alternately, we break out a new subclass.
|
||||
// The command factory instantiates the subclass.
|
||||
if (get_class($this) != 'Consolidation\AnnotatedCommand\AnnotatedCommand') {
|
||||
$commandInfo = new CommandInfo($this, 'execute');
|
||||
if (!isset($name)) {
|
||||
$name = $commandInfo->getName();
|
||||
}
|
||||
}
|
||||
parent::__construct($name);
|
||||
if ($commandInfo && $commandInfo->hasAnnotation('command')) {
|
||||
$this->setCommandInfo($commandInfo);
|
||||
$this->setCommandOptions($commandInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public function setCommandCallback($commandCallback)
|
||||
{
|
||||
$this->commandCallback = $commandCallback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCommandProcessor($commandProcessor)
|
||||
{
|
||||
$this->commandProcessor = $commandProcessor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function commandProcessor()
|
||||
{
|
||||
// If someone is using an AnnotatedCommand, and is NOT getting
|
||||
// it from an AnnotatedCommandFactory OR not correctly injecting
|
||||
// a command processor via setCommandProcessor() (ideally via the
|
||||
// DI container), then we'll just give each annotated command its
|
||||
// own command processor. This is not ideal; preferably, there would
|
||||
// only be one instance of the command processor in the application.
|
||||
if (!isset($this->commandProcessor)) {
|
||||
$this->commandProcessor = new CommandProcessor(new HookManager());
|
||||
}
|
||||
return $this->commandProcessor;
|
||||
}
|
||||
|
||||
public function getReturnType()
|
||||
{
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function setReturnType($returnType)
|
||||
{
|
||||
$this->returnType = $returnType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAnnotationData()
|
||||
{
|
||||
return $this->annotationData;
|
||||
}
|
||||
|
||||
public function setAnnotationData($annotationData)
|
||||
{
|
||||
$this->annotationData = $annotationData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCommandInfo($commandInfo)
|
||||
{
|
||||
$this->setDescription($commandInfo->getDescription());
|
||||
$this->setHelp($commandInfo->getHelp());
|
||||
$this->setAliases($commandInfo->getAliases());
|
||||
$this->setAnnotationData($commandInfo->getAnnotations());
|
||||
foreach ($commandInfo->getExampleUsages() as $usage => $description) {
|
||||
// Symfony Console does not support attaching a description to a usage
|
||||
$this->addUsage($usage);
|
||||
}
|
||||
$this->setCommandArguments($commandInfo);
|
||||
$this->setReturnType($commandInfo->getReturnType());
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setCommandArguments($commandInfo)
|
||||
{
|
||||
$this->setUsesInputInterface($commandInfo);
|
||||
$this->setUsesOutputInterface($commandInfo);
|
||||
$this->setCommandArgumentsFromParameters($commandInfo);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the first parameter is an InputInterface.
|
||||
*/
|
||||
protected function checkUsesInputInterface($params)
|
||||
{
|
||||
$firstParam = reset($params);
|
||||
return $firstParam instanceof InputInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this command wants to get its inputs
|
||||
* via an InputInterface or via its command parameters
|
||||
*/
|
||||
protected function setUsesInputInterface($commandInfo)
|
||||
{
|
||||
$params = $commandInfo->getParameters();
|
||||
$this->usesInputInterface = $this->checkUsesInputInterface($params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this command wants to send its output directly
|
||||
* to the provided OutputInterface, or whether it will returned
|
||||
* structured output to be processed by the command processor.
|
||||
*/
|
||||
protected function setUsesOutputInterface($commandInfo)
|
||||
{
|
||||
$params = $commandInfo->getParameters();
|
||||
$index = $this->checkUsesInputInterface($params) ? 1 : 0;
|
||||
$this->usesOutputInterface =
|
||||
(count($params) > $index) &&
|
||||
($params[$index] instanceof OutputInterface);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setCommandArgumentsFromParameters($commandInfo)
|
||||
{
|
||||
$args = $commandInfo->arguments()->getValues();
|
||||
foreach ($args as $name => $defaultValue) {
|
||||
$description = $commandInfo->arguments()->getDescription($name);
|
||||
$hasDefault = $commandInfo->arguments()->hasDefault($name);
|
||||
$parameterMode = $this->getCommandArgumentMode($hasDefault, $defaultValue);
|
||||
$this->addArgument($name, $parameterMode, $description, $defaultValue);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getCommandArgumentMode($hasDefault, $defaultValue)
|
||||
{
|
||||
if (!$hasDefault) {
|
||||
return InputArgument::REQUIRED;
|
||||
}
|
||||
if (is_array($defaultValue)) {
|
||||
return InputArgument::IS_ARRAY;
|
||||
}
|
||||
return InputArgument::OPTIONAL;
|
||||
}
|
||||
|
||||
public function setCommandOptions($commandInfo, $automaticOptions = [])
|
||||
{
|
||||
$inputOptions = $commandInfo->inputOptions();
|
||||
|
||||
$this->addOptions($inputOptions + $automaticOptions, $automaticOptions);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addOptions($inputOptions, $automaticOptions = [])
|
||||
{
|
||||
foreach ($inputOptions as $name => $inputOption) {
|
||||
$description = $inputOption->getDescription();
|
||||
|
||||
if (empty($description) && isset($automaticOptions[$name])) {
|
||||
$description = $automaticOptions[$name]->getDescription();
|
||||
$inputOption = static::inputOptionSetDescription($inputOption, $description);
|
||||
}
|
||||
$this->getDefinition()->addOption($inputOption);
|
||||
}
|
||||
}
|
||||
|
||||
protected static function inputOptionSetDescription($inputOption, $description)
|
||||
{
|
||||
// Recover the 'mode' value, because Symfony is stubborn
|
||||
$mode = 0;
|
||||
if ($inputOption->isValueRequired()) {
|
||||
$mode |= InputOption::VALUE_REQUIRED;
|
||||
}
|
||||
if ($inputOption->isValueOptional()) {
|
||||
$mode |= InputOption::VALUE_OPTIONAL;
|
||||
}
|
||||
if ($inputOption->isArray()) {
|
||||
$mode |= InputOption::VALUE_IS_ARRAY;
|
||||
}
|
||||
if (!$mode) {
|
||||
$mode = InputOption::VALUE_NONE;
|
||||
}
|
||||
|
||||
$inputOption = new InputOption(
|
||||
$inputOption->getName(),
|
||||
$inputOption->getShortcut(),
|
||||
$mode,
|
||||
$description,
|
||||
$inputOption->getDefault()
|
||||
);
|
||||
return $inputOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the hook names that may be called for this command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNames()
|
||||
{
|
||||
return HookManager::getNames($this, $this->commandCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any options to this command that are defined by hook implementations
|
||||
*/
|
||||
public function optionsHook()
|
||||
{
|
||||
$this->commandProcessor()->optionsHook(
|
||||
$this,
|
||||
$this->getNames(),
|
||||
$this->annotationData
|
||||
);
|
||||
}
|
||||
|
||||
public function optionsHookForHookAnnotations($commandInfoList)
|
||||
{
|
||||
foreach ($commandInfoList as $commandInfo) {
|
||||
$inputOptions = $commandInfo->inputOptions();
|
||||
$this->addOptions($inputOptions);
|
||||
foreach ($commandInfo->getExampleUsages() as $usage => $description) {
|
||||
if (!in_array($usage, $this->getUsages())) {
|
||||
$this->addUsage($usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function interact(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->commandProcessor()->interact(
|
||||
$input,
|
||||
$output,
|
||||
$this->getNames(),
|
||||
$this->annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// Allow the hook manager a chance to provide configuration values,
|
||||
// if there are any registered hooks to do that.
|
||||
$this->commandProcessor()->initializeHook($input, $this->getNames(), $this->annotationData);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// Validate, run, process, alter, handle results.
|
||||
return $this->commandProcessor()->process(
|
||||
$output,
|
||||
$this->getNames(),
|
||||
$this->commandCallback,
|
||||
$this->createCommandData($input, $output)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is available for use by a class that may
|
||||
* wish to extend this class rather than use annotations to
|
||||
* define commands. Using this technique does allow for the
|
||||
* use of annotations to define hooks.
|
||||
*/
|
||||
public function processResults(InputInterface $input, OutputInterface $output, $results)
|
||||
{
|
||||
$commandData = $this->createCommandData($input, $output);
|
||||
$commandProcessor = $this->commandProcessor();
|
||||
$names = $this->getNames();
|
||||
$results = $commandProcessor->processResults(
|
||||
$names,
|
||||
$results,
|
||||
$commandData
|
||||
);
|
||||
return $commandProcessor->handleResults(
|
||||
$output,
|
||||
$names,
|
||||
$results,
|
||||
$commandData
|
||||
);
|
||||
}
|
||||
|
||||
protected function createCommandData(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$commandData = new CommandData(
|
||||
$this->annotationData,
|
||||
$input,
|
||||
$output
|
||||
);
|
||||
|
||||
$commandData->setUseIOInterfaces(
|
||||
$this->usesOutputInterface,
|
||||
$this->usesInputInterface
|
||||
);
|
||||
|
||||
return $commandData;
|
||||
}
|
||||
}
|
||||
327
lib/composer/vendor/consolidation/annotated-command/src/AnnotatedCommandFactory.php
vendored
Normal file
327
lib/composer/vendor/consolidation/annotated-command/src/AnnotatedCommandFactory.php
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
use Consolidation\AnnotatedCommand\Hooks\HookManager;
|
||||
use Consolidation\AnnotatedCommand\Options\AutomaticOptionsProviderInterface;
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
use Consolidation\OutputFormatters\Options\FormatterOptions;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* The AnnotatedCommandFactory creates commands for your application.
|
||||
* Use with a Dependency Injection Container and the CommandFactory.
|
||||
* Alternately, use the CommandFileDiscovery to find commandfiles, and
|
||||
* then use AnnotatedCommandFactory::createCommandsFromClass() to create
|
||||
* commands. See the README for more information.
|
||||
*
|
||||
* @package Consolidation\AnnotatedCommand
|
||||
*/
|
||||
class AnnotatedCommandFactory implements AutomaticOptionsProviderInterface
|
||||
{
|
||||
/** var CommandProcessor */
|
||||
protected $commandProcessor;
|
||||
|
||||
/** var CommandCreationListenerInterface[] */
|
||||
protected $listeners = [];
|
||||
|
||||
/** var AutomaticOptionsProvider[] */
|
||||
|
||||
protected $automaticOptionsProviderList = [];
|
||||
|
||||
/** var boolean */
|
||||
protected $includeAllPublicMethods = true;
|
||||
|
||||
/** var CommandInfoAltererInterface */
|
||||
protected $commandInfoAlterers = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->commandProcessor = new CommandProcessor(new HookManager());
|
||||
$this->addAutomaticOptionProvider($this);
|
||||
}
|
||||
|
||||
public function setCommandProcessor(CommandProcessor $commandProcessor)
|
||||
{
|
||||
$this->commandProcessor = $commandProcessor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommandProcessor
|
||||
*/
|
||||
public function commandProcessor()
|
||||
{
|
||||
return $this->commandProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the 'include all public methods flag'. If true (the default), then
|
||||
* every public method of each commandFile will be used to create commands.
|
||||
* If it is false, then only those public methods annotated with @command
|
||||
* or @name (deprecated) will be used to create commands.
|
||||
*/
|
||||
public function setIncludeAllPublicMethods($includeAllPublicMethods)
|
||||
{
|
||||
$this->includeAllPublicMethods = $includeAllPublicMethods;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIncludeAllPublicMethods()
|
||||
{
|
||||
return $this->includeAllPublicMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HookManager
|
||||
*/
|
||||
public function hookManager()
|
||||
{
|
||||
return $this->commandProcessor()->hookManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener that is notified immediately before the command
|
||||
* factory creates commands from a commandFile instance. This
|
||||
* listener can use this opportunity to do more setup for the commandFile,
|
||||
* and so on.
|
||||
*
|
||||
* @param CommandCreationListenerInterface $listener
|
||||
*/
|
||||
public function addListener(CommandCreationListenerInterface $listener)
|
||||
{
|
||||
$this->listeners[] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call all command creation listeners
|
||||
*
|
||||
* @param object $commandFileInstance
|
||||
*/
|
||||
protected function notify($commandFileInstance)
|
||||
{
|
||||
foreach ($this->listeners as $listener) {
|
||||
$listener->notifyCommandFileAdded($commandFileInstance);
|
||||
}
|
||||
}
|
||||
|
||||
public function addAutomaticOptionProvider(AutomaticOptionsProviderInterface $optionsProvider)
|
||||
{
|
||||
$this->automaticOptionsProviderList[] = $optionsProvider;
|
||||
}
|
||||
|
||||
public function addCommandInfoAlterer(CommandInfoAltererInterface $alterer)
|
||||
{
|
||||
$this->commandInfoAlterers[] = $alterer;
|
||||
}
|
||||
|
||||
/**
|
||||
* n.b. This registers all hooks from the commandfile instance as a side-effect.
|
||||
*/
|
||||
public function createCommandsFromClass($commandFileInstance, $includeAllPublicMethods = null)
|
||||
{
|
||||
// Deprecated: avoid using the $includeAllPublicMethods in favor of the setIncludeAllPublicMethods() accessor.
|
||||
if (!isset($includeAllPublicMethods)) {
|
||||
$includeAllPublicMethods = $this->getIncludeAllPublicMethods();
|
||||
}
|
||||
$this->notify($commandFileInstance);
|
||||
$commandInfoList = $this->getCommandInfoListFromClass($commandFileInstance);
|
||||
$this->registerCommandHooksFromClassInfo($commandInfoList, $commandFileInstance);
|
||||
return $this->createCommandsFromClassInfo($commandInfoList, $commandFileInstance, $includeAllPublicMethods);
|
||||
}
|
||||
|
||||
public function getCommandInfoListFromClass($classNameOrInstance)
|
||||
{
|
||||
$commandInfoList = [];
|
||||
|
||||
// Ignore special functions, such as __construct and __call, which
|
||||
// can never be commands.
|
||||
$commandMethodNames = array_filter(
|
||||
get_class_methods($classNameOrInstance) ?: [],
|
||||
function ($m) {
|
||||
return !preg_match('#^_#', $m);
|
||||
}
|
||||
);
|
||||
|
||||
foreach ($commandMethodNames as $commandMethodName) {
|
||||
$commandInfoList[] = new CommandInfo($classNameOrInstance, $commandMethodName);
|
||||
}
|
||||
|
||||
return $commandInfoList;
|
||||
}
|
||||
|
||||
public function createCommandInfo($classNameOrInstance, $commandMethodName)
|
||||
{
|
||||
return new CommandInfo($classNameOrInstance, $commandMethodName);
|
||||
}
|
||||
|
||||
public function createCommandsFromClassInfo($commandInfoList, $commandFileInstance, $includeAllPublicMethods = null)
|
||||
{
|
||||
// Deprecated: avoid using the $includeAllPublicMethods in favor of the setIncludeAllPublicMethods() accessor.
|
||||
if (!isset($includeAllPublicMethods)) {
|
||||
$includeAllPublicMethods = $this->getIncludeAllPublicMethods();
|
||||
}
|
||||
return $this->createSelectedCommandsFromClassInfo(
|
||||
$commandInfoList,
|
||||
$commandFileInstance,
|
||||
function ($commandInfo) use ($includeAllPublicMethods) {
|
||||
return static::isCommandMethod($commandInfo, $includeAllPublicMethods);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function createSelectedCommandsFromClassInfo($commandInfoList, $commandFileInstance, callable $commandSelector)
|
||||
{
|
||||
$commandList = [];
|
||||
|
||||
foreach ($commandInfoList as $commandInfo) {
|
||||
if ($commandSelector($commandInfo)) {
|
||||
$command = $this->createCommand($commandInfo, $commandFileInstance);
|
||||
$commandList[] = $command;
|
||||
}
|
||||
}
|
||||
|
||||
return $commandList;
|
||||
}
|
||||
|
||||
public static function isCommandMethod($commandInfo, $includeAllPublicMethods)
|
||||
{
|
||||
// Ignore everything labeled @hook
|
||||
if ($commandInfo->hasAnnotation('hook')) {
|
||||
return false;
|
||||
}
|
||||
// Include everything labeled @command
|
||||
if ($commandInfo->hasAnnotation('command')) {
|
||||
return true;
|
||||
}
|
||||
// Skip anything named like an accessor ('get' or 'set')
|
||||
if (preg_match('#^(get[A-Z]|set[A-Z])#', $commandInfo->getMethodName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default to the setting of 'include all public methods'.
|
||||
return $includeAllPublicMethods;
|
||||
}
|
||||
|
||||
public function registerCommandHooksFromClassInfo($commandInfoList, $commandFileInstance)
|
||||
{
|
||||
foreach ($commandInfoList as $commandInfo) {
|
||||
if ($commandInfo->hasAnnotation('hook')) {
|
||||
$this->registerCommandHook($commandInfo, $commandFileInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a command hook given the CommandInfo for a method.
|
||||
*
|
||||
* The hook format is:
|
||||
*
|
||||
* @hook type name type
|
||||
*
|
||||
* For example, the pre-validate hook for the core:init command is:
|
||||
*
|
||||
* @hook pre-validate core:init
|
||||
*
|
||||
* If no command name is provided, then this hook will affect every
|
||||
* command that is defined in the same file.
|
||||
*
|
||||
* If no hook is provided, then we will presume that ALTER_RESULT
|
||||
* is intended.
|
||||
*
|
||||
* @param CommandInfo $commandInfo Information about the command hook method.
|
||||
* @param object $commandFileInstance An instance of the CommandFile class.
|
||||
*/
|
||||
public function registerCommandHook(CommandInfo $commandInfo, $commandFileInstance)
|
||||
{
|
||||
// Ignore if the command info has no @hook
|
||||
if (!$commandInfo->hasAnnotation('hook')) {
|
||||
return;
|
||||
}
|
||||
$hookData = $commandInfo->getAnnotation('hook');
|
||||
$hook = $this->getNthWord($hookData, 0, HookManager::ALTER_RESULT);
|
||||
$commandName = $this->getNthWord($hookData, 1);
|
||||
|
||||
// Register the hook
|
||||
$callback = [$commandFileInstance, $commandInfo->getMethodName()];
|
||||
$this->commandProcessor()->hookManager()->add($callback, $hook, $commandName);
|
||||
|
||||
// If the hook has options, then also register the commandInfo
|
||||
// with the hook manager, so that we can add options and such to
|
||||
// the commands they hook.
|
||||
if (!$commandInfo->options()->isEmpty()) {
|
||||
$this->commandProcessor()->hookManager()->recordHookOptions($commandInfo, $commandName);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getNthWord($string, $n, $default = '', $delimiter = ' ')
|
||||
{
|
||||
$words = explode($delimiter, $string);
|
||||
if (!empty($words[$n])) {
|
||||
return $words[$n];
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
public function createCommand(CommandInfo $commandInfo, $commandFileInstance)
|
||||
{
|
||||
$this->alterCommandInfo($commandInfo, $commandFileInstance);
|
||||
$command = new AnnotatedCommand($commandInfo->getName());
|
||||
$commandCallback = [$commandFileInstance, $commandInfo->getMethodName()];
|
||||
$command->setCommandCallback($commandCallback);
|
||||
$command->setCommandProcessor($this->commandProcessor);
|
||||
$command->setCommandInfo($commandInfo);
|
||||
$automaticOptions = $this->callAutomaticOptionsProviders($commandInfo);
|
||||
$command->setCommandOptions($commandInfo, $automaticOptions);
|
||||
// Annotation commands are never bootstrap-aware, but for completeness
|
||||
// we will notify on every created command, as some clients may wish to
|
||||
// use this notification for some other purpose.
|
||||
$this->notify($command);
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give plugins an opportunity to update the commandInfo
|
||||
*/
|
||||
public function alterCommandInfo(CommandInfo $commandInfo, $commandFileInstance)
|
||||
{
|
||||
foreach ($this->commandInfoAlterers as $alterer) {
|
||||
$alterer->alterCommandInfo($commandInfo, $commandFileInstance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options that are implied by annotations, e.g. @fields implies
|
||||
* that there should be a --fields and a --format option.
|
||||
*
|
||||
* @return InputOption[]
|
||||
*/
|
||||
public function callAutomaticOptionsProviders(CommandInfo $commandInfo)
|
||||
{
|
||||
$automaticOptions = [];
|
||||
foreach ($this->automaticOptionsProviderList as $automaticOptionsProvider) {
|
||||
$automaticOptions += $automaticOptionsProvider->automaticOptions($commandInfo);
|
||||
}
|
||||
return $automaticOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options that are implied by annotations, e.g. @fields implies
|
||||
* that there should be a --fields and a --format option.
|
||||
*
|
||||
* @return InputOption[]
|
||||
*/
|
||||
public function automaticOptions(CommandInfo $commandInfo)
|
||||
{
|
||||
$automaticOptions = [];
|
||||
$formatManager = $this->commandProcessor()->formatterManager();
|
||||
if ($formatManager) {
|
||||
$annotationData = $commandInfo->getAnnotations()->getArrayCopy();
|
||||
$formatterOptions = new FormatterOptions($annotationData);
|
||||
$dataType = $commandInfo->getReturnType();
|
||||
$automaticOptions = $formatManager->automaticOptions($formatterOptions, $dataType);
|
||||
}
|
||||
return $automaticOptions;
|
||||
}
|
||||
}
|
||||
20
lib/composer/vendor/consolidation/annotated-command/src/AnnotationData.php
vendored
Normal file
20
lib/composer/vendor/consolidation/annotated-command/src/AnnotationData.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
class AnnotationData extends \ArrayObject
|
||||
{
|
||||
public function get($key, $default)
|
||||
{
|
||||
return $this->has($key) ? $this[$key] : $default;
|
||||
}
|
||||
|
||||
public function has($key)
|
||||
{
|
||||
return isset($this[$key]);
|
||||
}
|
||||
|
||||
public function keys()
|
||||
{
|
||||
return array_keys($this->getArrayCopy());
|
||||
}
|
||||
}
|
||||
15
lib/composer/vendor/consolidation/annotated-command/src/CommandCreationListenerInterface.php
vendored
Normal file
15
lib/composer/vendor/consolidation/annotated-command/src/CommandCreationListenerInterface.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
/**
|
||||
* Command cration listeners can be added to the annotation
|
||||
* command factory. These will be notified whenever a new
|
||||
* commandfile is provided to the factory. This is useful for
|
||||
* initializing new commandfile objects.
|
||||
*
|
||||
* @see AnnotatedCommandFactory::addListener()
|
||||
*/
|
||||
interface CommandCreationListenerInterface
|
||||
{
|
||||
public function notifyCommandFileAdded($command);
|
||||
}
|
||||
116
lib/composer/vendor/consolidation/annotated-command/src/CommandData.php
vendored
Normal file
116
lib/composer/vendor/consolidation/annotated-command/src/CommandData.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class CommandData
|
||||
{
|
||||
/** var AnnotationData */
|
||||
protected $annotationData;
|
||||
/** var InputInterface */
|
||||
protected $input;
|
||||
/** var OutputInterface */
|
||||
protected $output;
|
||||
/** var boolean */
|
||||
protected $usesInputInterface;
|
||||
/** var boolean */
|
||||
protected $usesOutputInterface;
|
||||
/** var boolean */
|
||||
protected $includeOptionsInArgs;
|
||||
|
||||
public function __construct(
|
||||
AnnotationData $annotationData,
|
||||
InputInterface $input,
|
||||
OutputInterface $output,
|
||||
$usesInputInterface = false,
|
||||
$usesOutputInterface = false
|
||||
) {
|
||||
$this->annotationData = $annotationData;
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->usesInputInterface = false;
|
||||
$this->usesOutputInterface = false;
|
||||
$this->includeOptionsInArgs = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal use only; indicates that the function to be called
|
||||
* should be passed an InputInterface &/or an OutputInterface.
|
||||
* @param booean $usesInputInterface
|
||||
* @param boolean $usesOutputInterface
|
||||
* @return self
|
||||
*/
|
||||
public function setUseIOInterfaces($usesInputInterface, $usesOutputInterface)
|
||||
{
|
||||
$this->usesInputInterface = $usesInputInterface;
|
||||
$this->usesOutputInterface = $usesOutputInterface;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* For backwards-compatibility mode only: disable addition of
|
||||
* options on the end of the arguments list.
|
||||
*/
|
||||
public function setIncludeOptionsInArgs($includeOptionsInArgs)
|
||||
{
|
||||
$this->includeOptionsInArgs = $includeOptionsInArgs;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function annotationData()
|
||||
{
|
||||
return $this->annotationData;
|
||||
}
|
||||
|
||||
public function input()
|
||||
{
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
public function arguments()
|
||||
{
|
||||
return $this->input->getArguments();
|
||||
}
|
||||
|
||||
public function options()
|
||||
{
|
||||
return $this->input->getOptions();
|
||||
}
|
||||
|
||||
public function getArgsWithoutAppName()
|
||||
{
|
||||
$args = $this->arguments();
|
||||
|
||||
// When called via the Application, the first argument
|
||||
// will be the command name. The Application alters the
|
||||
// input definition to match, adding a 'command' argument
|
||||
// to the beginning.
|
||||
array_shift($args);
|
||||
|
||||
if ($this->usesInputInterface) {
|
||||
array_unshift($args, $this->input());
|
||||
}
|
||||
|
||||
if ($this->usesOutputInterface) {
|
||||
array_unshift($args, $this->output());
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
public function getArgsAndOptions()
|
||||
{
|
||||
// Get passthrough args, and add the options on the end.
|
||||
$args = $this->getArgsWithoutAppName();
|
||||
if ($this->includeOptionsInArgs) {
|
||||
$args['options'] = $this->options();
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
32
lib/composer/vendor/consolidation/annotated-command/src/CommandError.php
vendored
Normal file
32
lib/composer/vendor/consolidation/annotated-command/src/CommandError.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
/**
|
||||
* Return a CommandError as the result of a command to pass a status
|
||||
* code and error message to be displayed.
|
||||
*
|
||||
* @package Consolidation\AnnotatedCommand
|
||||
*/
|
||||
class CommandError implements ExitCodeInterface, OutputDataInterface
|
||||
{
|
||||
protected $message;
|
||||
protected $exitCode;
|
||||
|
||||
public function __construct($message = null, $exitCode = 1)
|
||||
{
|
||||
$this->message = $message;
|
||||
// Ensure the exit code is non-zero. The exit code may have
|
||||
// come from an exception, and those often default to zero if
|
||||
// a specific value is not provided.
|
||||
$this->exitCode = $exitCode == 0 ? 1 : $exitCode;
|
||||
}
|
||||
public function getExitCode()
|
||||
{
|
||||
return $this->exitCode;
|
||||
}
|
||||
|
||||
public function getOutputData()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
}
|
||||
383
lib/composer/vendor/consolidation/annotated-command/src/CommandFileDiscovery.php
vendored
Normal file
383
lib/composer/vendor/consolidation/annotated-command/src/CommandFileDiscovery.php
vendored
Normal file
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
/**
|
||||
* Do discovery presuming that the namespace of the command will
|
||||
* contain the last component of the path. This is the convention
|
||||
* that should be used when searching for command files that are
|
||||
* bundled with the modules of a framework. The convention used
|
||||
* is that the namespace for a module in a framework should start with
|
||||
* the framework name followed by the module name.
|
||||
*
|
||||
* For example, if base namespace is "Drupal", then a command file in
|
||||
* modules/default_content/src/CliTools/ExampleCommands.cpp
|
||||
* will be in the namespace Drupal\default_content\CliTools.
|
||||
*
|
||||
* For global locations, the middle component of the namespace is
|
||||
* omitted. For example, if the base namespace is "Drupal", then
|
||||
* a command file in __DRUPAL_ROOT__/CliTools/ExampleCommands.cpp
|
||||
* will be in the namespace Drupal\CliTools.
|
||||
*
|
||||
* To discover namespaced commands in modules:
|
||||
*
|
||||
* $commandFiles = $discovery->discoverNamespaced($moduleList, '\Drupal');
|
||||
*
|
||||
* To discover global commands:
|
||||
*
|
||||
* $commandFiles = $discovery->discover($drupalRoot, '\Drupal');
|
||||
*/
|
||||
class CommandFileDiscovery
|
||||
{
|
||||
/** @var string[] */
|
||||
protected $excludeList;
|
||||
/** @var string[] */
|
||||
protected $searchLocations;
|
||||
/** @var string */
|
||||
protected $searchPattern = '*Commands.php';
|
||||
/** @var boolean */
|
||||
protected $includeFilesAtBase = true;
|
||||
/** @var integer */
|
||||
protected $searchDepth = 2;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->excludeList = ['Exclude'];
|
||||
$this->searchLocations = [
|
||||
'Command',
|
||||
'CliTools', // TODO: Maybe remove
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to search for files at the base directory
|
||||
* ($directoryList parameter to discover and discoverNamespaced
|
||||
* methods), or only in the directories listed in the search paths.
|
||||
*
|
||||
* @param boolean $includeFilesAtBase
|
||||
*/
|
||||
public function setIncludeFilesAtBase($includeFilesAtBase)
|
||||
{
|
||||
$this->includeFilesAtBase = $includeFilesAtBase;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of excludes to add to the finder, replacing
|
||||
* whatever was there before.
|
||||
*
|
||||
* @param array $excludeList The list of directory names to skip when
|
||||
* searching for command files.
|
||||
*/
|
||||
public function setExcludeList($excludeList)
|
||||
{
|
||||
$this->excludeList = $excludeList;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one more location to the exclude list.
|
||||
*
|
||||
* @param string $exclude One directory name to skip when searching
|
||||
* for command files.
|
||||
*/
|
||||
public function addExclude($exclude)
|
||||
{
|
||||
$this->excludeList[] = $exclude;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the search depth. By default, fills immediately in the
|
||||
* base directory are searched, plus all of the search locations
|
||||
* to this specified depth. If the search locations is set to
|
||||
* an empty array, then the base directory is searched to this
|
||||
* depth.
|
||||
*/
|
||||
public function setSearchDepth($searchDepth)
|
||||
{
|
||||
$this->searchDepth = $searchDepth;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of search locations to examine in each directory where
|
||||
* command files may be found. This replaces whatever was there before.
|
||||
*
|
||||
* @param array $searchLocations The list of locations to search for command files.
|
||||
*/
|
||||
public function setSearchLocations($searchLocations)
|
||||
{
|
||||
$this->searchLocations = $searchLocations;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one more location to the search location list.
|
||||
*
|
||||
* @param string $location One more relative path to search
|
||||
* for command files.
|
||||
*/
|
||||
public function addSearchLocation($location)
|
||||
{
|
||||
$this->searchLocations[] = $location;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the pattern / regex used by the finder to search for
|
||||
* command files.
|
||||
*/
|
||||
public function setSearchPattern($searchPattern)
|
||||
{
|
||||
$this->searchPattern = $searchPattern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of directories, e.g. Drupal modules like:
|
||||
*
|
||||
* core/modules/block
|
||||
* core/modules/dblog
|
||||
* modules/default_content
|
||||
*
|
||||
* Discover command files in any of these locations.
|
||||
*
|
||||
* @param string|string[] $directoryList Places to search for commands.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function discoverNamespaced($directoryList, $baseNamespace = '')
|
||||
{
|
||||
return $this->discover($this->convertToNamespacedList((array)$directoryList), $baseNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a simple list containing paths to directories, where
|
||||
* the last component of the path should appear in the namespace,
|
||||
* after the base namespace, this function will return an
|
||||
* associative array mapping the path's basename (e.g. the module
|
||||
* name) to the directory path.
|
||||
*
|
||||
* Module names must be unique.
|
||||
*
|
||||
* @param string[] $directoryList A list of module locations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function convertToNamespacedList($directoryList)
|
||||
{
|
||||
$namespacedArray = [];
|
||||
foreach ((array)$directoryList as $directory) {
|
||||
$namespacedArray[basename($directory)] = $directory;
|
||||
}
|
||||
return $namespacedArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for command files in the specified locations. This is the function that
|
||||
* should be used for all locations that are NOT modules of a framework.
|
||||
*
|
||||
* @param string|string[] $directoryList Places to search for commands.
|
||||
* @return array
|
||||
*/
|
||||
public function discover($directoryList, $baseNamespace = '')
|
||||
{
|
||||
$commandFiles = [];
|
||||
foreach ((array)$directoryList as $key => $directory) {
|
||||
$itemsNamespace = $this->joinNamespace([$baseNamespace, $key]);
|
||||
$commandFiles = array_merge(
|
||||
$commandFiles,
|
||||
$this->discoverCommandFiles($directory, $itemsNamespace),
|
||||
$this->discoverCommandFiles("$directory/src", $itemsNamespace)
|
||||
);
|
||||
}
|
||||
return $commandFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for command files in specific locations within a single directory.
|
||||
*
|
||||
* In each location, we will accept only a few places where command files
|
||||
* can be found. This will reduce the need to search through many unrelated
|
||||
* files.
|
||||
*
|
||||
* The default search locations include:
|
||||
*
|
||||
* .
|
||||
* CliTools
|
||||
* src/CliTools
|
||||
*
|
||||
* The pattern we will look for is any file whose name ends in 'Commands.php'.
|
||||
* A list of paths to found files will be returned.
|
||||
*/
|
||||
protected function discoverCommandFiles($directory, $baseNamespace)
|
||||
{
|
||||
$commandFiles = [];
|
||||
// In the search location itself, we will search for command files
|
||||
// immediately inside the directory only.
|
||||
if ($this->includeFilesAtBase) {
|
||||
$commandFiles = $this->discoverCommandFilesInLocation(
|
||||
$directory,
|
||||
$this->getBaseDirectorySearchDepth(),
|
||||
$baseNamespace
|
||||
);
|
||||
}
|
||||
|
||||
// In the other search locations,
|
||||
foreach ($this->searchLocations as $location) {
|
||||
$itemsNamespace = $this->joinNamespace([$baseNamespace, $location]);
|
||||
$commandFiles = array_merge(
|
||||
$commandFiles,
|
||||
$this->discoverCommandFilesInLocation(
|
||||
"$directory/$location",
|
||||
$this->getSearchDepth(),
|
||||
$itemsNamespace
|
||||
)
|
||||
);
|
||||
}
|
||||
return $commandFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Finder search depth appropriate for our selected search depth.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSearchDepth()
|
||||
{
|
||||
return $this->searchDepth <= 0 ? '== 0' : '<= ' . $this->searchDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Finder search depth for the base directory. If the
|
||||
* searchLocations array has been populated, then we will only search
|
||||
* for files immediately inside the base directory; no traversal into
|
||||
* deeper directories will be done, as that would conflict with the
|
||||
* specification provided by the search locations. If there is no
|
||||
* search location, then we will search to whatever depth was specified
|
||||
* by the client.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBaseDirectorySearchDepth()
|
||||
{
|
||||
if (!empty($this->searchLocations)) {
|
||||
return '== 0';
|
||||
}
|
||||
return $this->getSearchDepth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for command files in just one particular location. Returns
|
||||
* an associative array mapping from the pathname of the file to the
|
||||
* classname that it contains. The pathname may be ignored if the search
|
||||
* location is included in the autoloader.
|
||||
*
|
||||
* @param string $directory The location to search
|
||||
* @param string $depth How deep to search (e.g. '== 0' or '< 2')
|
||||
* @param string $baseNamespace Namespace to prepend to each classname
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function discoverCommandFilesInLocation($directory, $depth, $baseNamespace)
|
||||
{
|
||||
if (!is_dir($directory)) {
|
||||
return [];
|
||||
}
|
||||
$finder = $this->createFinder($directory, $depth);
|
||||
|
||||
$commands = [];
|
||||
foreach ($finder as $file) {
|
||||
$relativePathName = $file->getRelativePathname();
|
||||
$relativeNamespaceAndClassname = str_replace(
|
||||
['/', '.php'],
|
||||
['\\', ''],
|
||||
$relativePathName
|
||||
);
|
||||
$classname = $this->joinNamespace([$baseNamespace, $relativeNamespaceAndClassname]);
|
||||
$commandFilePath = $this->joinPaths([$directory, $relativePathName]);
|
||||
$commands[$commandFilePath] = $classname;
|
||||
}
|
||||
|
||||
return $commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Finder object for use in searching a particular directory
|
||||
* location.
|
||||
*
|
||||
* @param string $directory The location to search
|
||||
* @param string $depth The depth limitation
|
||||
*
|
||||
* @return Finder
|
||||
*/
|
||||
protected function createFinder($directory, $depth)
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->files()
|
||||
->name($this->searchPattern)
|
||||
->in($directory)
|
||||
->depth($depth);
|
||||
|
||||
foreach ($this->excludeList as $item) {
|
||||
$finder->exclude($item);
|
||||
}
|
||||
|
||||
return $finder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the items of the provied array into a backslash-separated
|
||||
* namespace string. Empty and numeric items are omitted.
|
||||
*
|
||||
* @param array $namespaceParts List of components of a namespace
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function joinNamespace(array $namespaceParts)
|
||||
{
|
||||
return $this->joinParts(
|
||||
'\\',
|
||||
$namespaceParts,
|
||||
function ($item) {
|
||||
return !is_numeric($item) && !empty($item);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the items of the provied array into a slash-separated
|
||||
* pathname. Empty items are omitted.
|
||||
*
|
||||
* @param array $pathParts List of components of a path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function joinPaths(array $pathParts)
|
||||
{
|
||||
return $this->joinParts(
|
||||
'/',
|
||||
$pathParts,
|
||||
function ($item) {
|
||||
return !empty($item);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper around implode and array_filter.
|
||||
*
|
||||
* @param string $delimiter
|
||||
* @param array $parts
|
||||
* @param callable $filterFunction
|
||||
*/
|
||||
protected function joinParts($delimiter, $parts, $filterFunction)
|
||||
{
|
||||
return implode(
|
||||
$delimiter,
|
||||
array_filter($parts, $filterFunction)
|
||||
);
|
||||
}
|
||||
}
|
||||
9
lib/composer/vendor/consolidation/annotated-command/src/CommandInfoAltererInterface.php
vendored
Normal file
9
lib/composer/vendor/consolidation/annotated-command/src/CommandInfoAltererInterface.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
|
||||
interface CommandInfoAltererInterface
|
||||
{
|
||||
public function alterCommandInfo(CommandInfo $commandInfo, $commandFileInstance);
|
||||
}
|
||||
308
lib/composer/vendor/consolidation/annotated-command/src/CommandProcessor.php
vendored
Normal file
308
lib/composer/vendor/consolidation/annotated-command/src/CommandProcessor.php
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
|
||||
use Consolidation\OutputFormatters\FormatterManager;
|
||||
use Consolidation\OutputFormatters\Options\FormatterOptions;
|
||||
use Consolidation\AnnotatedCommand\Hooks\HookManager;
|
||||
use Consolidation\AnnotatedCommand\Options\PrepareFormatter;
|
||||
|
||||
/**
|
||||
* Process a command, including hooks and other callbacks.
|
||||
* There should only be one command processor per application.
|
||||
* Provide your command processor to the AnnotatedCommandFactory
|
||||
* via AnnotatedCommandFactory::setCommandProcessor().
|
||||
*/
|
||||
class CommandProcessor
|
||||
{
|
||||
/** var HookManager */
|
||||
protected $hookManager;
|
||||
/** var FormatterManager */
|
||||
protected $formatterManager;
|
||||
/** var callable */
|
||||
protected $displayErrorFunction;
|
||||
/** var PrepareFormatterOptions[] */
|
||||
protected $prepareOptionsList = [];
|
||||
|
||||
public function __construct(HookManager $hookManager)
|
||||
{
|
||||
$this->hookManager = $hookManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hook manager
|
||||
* @return HookManager
|
||||
*/
|
||||
public function hookManager()
|
||||
{
|
||||
return $this->hookManager;
|
||||
}
|
||||
|
||||
public function addPrepareFormatter(PrepareFormatter $preparer)
|
||||
{
|
||||
$this->prepareOptionsList[] = $preparer;
|
||||
}
|
||||
|
||||
public function setFormatterManager(FormatterManager $formatterManager)
|
||||
{
|
||||
$this->formatterManager = $formatterManager;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDisplayErrorFunction(callable $fn)
|
||||
{
|
||||
$this->displayErrorFunction = $fn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the formatter manager
|
||||
* @return FormatterManager
|
||||
*/
|
||||
public function formatterManager()
|
||||
{
|
||||
return $this->formatterManager;
|
||||
}
|
||||
|
||||
public function initializeHook(
|
||||
InputInterface $input,
|
||||
$names,
|
||||
AnnotationData $annotationData
|
||||
) {
|
||||
return $this->hookManager()->initializeHook($input, $names, $annotationData);
|
||||
}
|
||||
|
||||
public function optionsHook(
|
||||
AnnotatedCommand $command,
|
||||
$names,
|
||||
AnnotationData $annotationData
|
||||
) {
|
||||
$this->hookManager()->optionsHook($command, $names, $annotationData);
|
||||
}
|
||||
|
||||
public function interact(
|
||||
InputInterface $input,
|
||||
OutputInterface $output,
|
||||
$names,
|
||||
AnnotationData $annotationData
|
||||
) {
|
||||
return $this->hookManager()->interact($input, $output, $names, $annotationData);
|
||||
}
|
||||
|
||||
public function process(
|
||||
OutputInterface $output,
|
||||
$names,
|
||||
$commandCallback,
|
||||
CommandData $commandData
|
||||
) {
|
||||
$result = [];
|
||||
try {
|
||||
$result = $this->validateRunAndAlter(
|
||||
$names,
|
||||
$commandCallback,
|
||||
$commandData
|
||||
);
|
||||
return $this->handleResults($output, $names, $result, $commandData);
|
||||
} catch (\Exception $e) {
|
||||
$result = new CommandError($e->getMessage(), $e->getCode());
|
||||
return $this->handleResults($output, $names, $result, $commandData);
|
||||
}
|
||||
}
|
||||
|
||||
public function validateRunAndAlter(
|
||||
$names,
|
||||
$commandCallback,
|
||||
CommandData $commandData
|
||||
) {
|
||||
// Validators return any object to signal a validation error;
|
||||
// if the return an array, it replaces the arguments.
|
||||
$validated = $this->hookManager()->validateArguments($names, $commandData);
|
||||
if (is_object($validated)) {
|
||||
return $validated;
|
||||
}
|
||||
|
||||
// Run the command, alter the results, and then handle output and status
|
||||
$result = $this->runCommandCallback($commandCallback, $commandData);
|
||||
return $this->processResults($names, $result, $commandData);
|
||||
}
|
||||
|
||||
public function processResults($names, $result, CommandData $commandData)
|
||||
{
|
||||
return $this->hookManager()->alterResult($names, $result, $commandData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the result output and status code calculation.
|
||||
*/
|
||||
public function handleResults(OutputInterface $output, $names, $result, CommandData $commandData)
|
||||
{
|
||||
$status = $this->hookManager()->determineStatusCode($names, $result);
|
||||
// If the result is an integer and no separate status code was provided, then use the result as the status and do no output.
|
||||
if (is_integer($result) && !isset($status)) {
|
||||
return $result;
|
||||
}
|
||||
$status = $this->interpretStatusCode($status);
|
||||
|
||||
// Get the structured output, the output stream and the formatter
|
||||
$structuredOutput = $this->hookManager()->extractOutput($names, $result);
|
||||
$output = $this->chooseOutputStream($output, $status);
|
||||
if ($status != 0) {
|
||||
return $this->writeErrorMessage($output, $status, $structuredOutput, $result);
|
||||
}
|
||||
if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) {
|
||||
return $this->writeUsingFormatter($output, $structuredOutput, $commandData);
|
||||
}
|
||||
return $this->writeCommandOutput($output, $structuredOutput);
|
||||
}
|
||||
|
||||
protected function dataCanBeFormatted($structuredOutput)
|
||||
{
|
||||
if (!isset($this->formatterManager)) {
|
||||
return false;
|
||||
}
|
||||
return
|
||||
is_object($structuredOutput) ||
|
||||
is_array($structuredOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the main command callback
|
||||
*/
|
||||
protected function runCommandCallback($commandCallback, CommandData $commandData)
|
||||
{
|
||||
$result = false;
|
||||
try {
|
||||
$args = $commandData->getArgsAndOptions();
|
||||
$result = call_user_func_array($commandCallback, $args);
|
||||
} catch (\Exception $e) {
|
||||
$result = new CommandError($e->getMessage(), $e->getCode());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the formatter that should be used to render
|
||||
* output.
|
||||
*
|
||||
* If the user specified a format via the --format option,
|
||||
* then always return that. Otherwise, return the default
|
||||
* format, unless --pipe was specified, in which case
|
||||
* return the default pipe format, format-pipe.
|
||||
*
|
||||
* n.b. --pipe is a handy option introduced in Drush 2
|
||||
* (or perhaps even Drush 1) that indicates that the command
|
||||
* should select the output format that is most appropriate
|
||||
* for use in scripts (e.g. to pipe to another command).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFormat(FormatterOptions $options)
|
||||
{
|
||||
// In Symfony Console, there is no way for us to differentiate
|
||||
// between the user specifying '--format=table', and the user
|
||||
// not specifying --format when the default value is 'table'.
|
||||
// Therefore, we must make --field always override --format; it
|
||||
// cannot become the default value for --format.
|
||||
if ($options->get('field')) {
|
||||
return 'string';
|
||||
}
|
||||
$defaults = [];
|
||||
if ($options->get('pipe')) {
|
||||
return $options->get('pipe-format', [], 'tsv');
|
||||
}
|
||||
return $options->getFormat($defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether we should use stdout or stderr.
|
||||
*/
|
||||
protected function chooseOutputStream(OutputInterface $output, $status)
|
||||
{
|
||||
// If the status code indicates an error, then print the
|
||||
// result to stderr rather than stdout
|
||||
if ($status && ($output instanceof ConsoleOutputInterface)) {
|
||||
return $output->getErrorOutput();
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the formatter to output the provided data.
|
||||
*/
|
||||
protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, CommandData $commandData)
|
||||
{
|
||||
$formatterOptions = $this->createFormatterOptions($commandData);
|
||||
$format = $this->getFormat($formatterOptions);
|
||||
$this->formatterManager->write(
|
||||
$output,
|
||||
$format,
|
||||
$structuredOutput,
|
||||
$formatterOptions
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a FormatterOptions object for use in writing the formatted output.
|
||||
* @param CommandData $commandData
|
||||
* @return FormatterOptions
|
||||
*/
|
||||
protected function createFormatterOptions($commandData)
|
||||
{
|
||||
$options = $commandData->input()->getOptions();
|
||||
$formatterOptions = new FormatterOptions($commandData->annotationData()->getArrayCopy(), $options);
|
||||
foreach ($this->prepareOptionsList as $preparer) {
|
||||
$preparer->prepare($commandData, $formatterOptions);
|
||||
}
|
||||
return $formatterOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @param OutputInterface $output
|
||||
* @param int $status
|
||||
* @param string $structuredOutput
|
||||
* @param mixed $originalResult
|
||||
* @return type
|
||||
*/
|
||||
protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
|
||||
{
|
||||
if (isset($this->displayErrorFunction)) {
|
||||
call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
|
||||
} else {
|
||||
$this->writeCommandOutput($output, $structuredOutput);
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the result object is a string, then print it.
|
||||
*/
|
||||
protected function writeCommandOutput(
|
||||
OutputInterface $output,
|
||||
$structuredOutput
|
||||
) {
|
||||
// If there is no formatter, we will print strings,
|
||||
// but can do no more than that.
|
||||
if (is_string($structuredOutput)) {
|
||||
$output->writeln($structuredOutput);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a status code was set, then return it; otherwise,
|
||||
* presume success.
|
||||
*/
|
||||
protected function interpretStatusCode($status)
|
||||
{
|
||||
if (isset($status)) {
|
||||
return $status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
12
lib/composer/vendor/consolidation/annotated-command/src/ExitCodeInterface.php
vendored
Normal file
12
lib/composer/vendor/consolidation/annotated-command/src/ExitCodeInterface.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
/**
|
||||
* If an annotated command method encounters an error, then it
|
||||
* should either throw an exception, or return a result object
|
||||
* that implements ExitCodeInterface.
|
||||
*/
|
||||
interface ExitCodeInterface
|
||||
{
|
||||
public function getExitCode();
|
||||
}
|
||||
12
lib/composer/vendor/consolidation/annotated-command/src/Hooks/AlterResultInterface.php
vendored
Normal file
12
lib/composer/vendor/consolidation/annotated-command/src/Hooks/AlterResultInterface.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
/**
|
||||
* Alter the result of a command after it has been processed.
|
||||
* An alter result interface isa process result interface.
|
||||
*
|
||||
* @see HookManager::addAlterResult()
|
||||
*/
|
||||
interface AlterResultInterface extends ProcessResultInterface
|
||||
{
|
||||
}
|
||||
14
lib/composer/vendor/consolidation/annotated-command/src/Hooks/ExtractOutputInterface.php
vendored
Normal file
14
lib/composer/vendor/consolidation/annotated-command/src/Hooks/ExtractOutputInterface.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
/**
|
||||
* Extract Output hooks are used to select the particular
|
||||
* data elements of the result that should be printed as
|
||||
* the command output -- perhaps after being formatted.
|
||||
*
|
||||
* @see HookManager::addOutputExtractor()
|
||||
*/
|
||||
interface ExtractOutputInterface
|
||||
{
|
||||
public function extractOutput($result);
|
||||
}
|
||||
700
lib/composer/vendor/consolidation/annotated-command/src/Hooks/HookManager.php
vendored
Normal file
700
lib/composer/vendor/consolidation/annotated-command/src/Hooks/HookManager.php
vendored
Normal file
@@ -0,0 +1,700 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
use Consolidation\AnnotatedCommand\ExitCodeInterface;
|
||||
use Consolidation\AnnotatedCommand\OutputDataInterface;
|
||||
use Consolidation\AnnotatedCommand\AnnotationData;
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
use Consolidation\AnnotatedCommand\CommandError;
|
||||
|
||||
/**
|
||||
* Manage named callback hooks
|
||||
*/
|
||||
class HookManager implements EventSubscriberInterface
|
||||
{
|
||||
protected $hooks = [];
|
||||
/** var CommandInfo[] */
|
||||
protected $hookOptions = [];
|
||||
|
||||
const PRE_COMMAND_EVENT = 'pre-command-event';
|
||||
const COMMAND_EVENT = 'command-event';
|
||||
const POST_COMMAND_EVENT = 'post-command-event';
|
||||
const PRE_OPTION_HOOK = 'pre-option';
|
||||
const OPTION_HOOK = 'option';
|
||||
const POST_OPTION_HOOK = 'post-option';
|
||||
const PRE_INITIALIZE = 'pre-init';
|
||||
const INITIALIZE = 'init';
|
||||
const POST_INITIALIZE = 'post-init';
|
||||
const PRE_INTERACT = 'pre-interact';
|
||||
const INTERACT = 'interact';
|
||||
const POST_INTERACT = 'post-interact';
|
||||
const PRE_ARGUMENT_VALIDATOR = 'pre-validate';
|
||||
const ARGUMENT_VALIDATOR = 'validate';
|
||||
const POST_ARGUMENT_VALIDATOR = 'post-validate';
|
||||
const PRE_COMMAND_HOOK = 'pre-command';
|
||||
const COMMAND_HOOK = 'command';
|
||||
const POST_COMMAND_HOOK = 'post-command';
|
||||
const PRE_PROCESS_RESULT = 'pre-process';
|
||||
const PROCESS_RESULT = 'process';
|
||||
const POST_PROCESS_RESULT = 'post-process';
|
||||
const PRE_ALTER_RESULT = 'pre-alter';
|
||||
const ALTER_RESULT = 'alter';
|
||||
const POST_ALTER_RESULT = 'post-alter';
|
||||
const STATUS_DETERMINER = 'status';
|
||||
const EXTRACT_OUTPUT = 'extract';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function getAllHooks()
|
||||
{
|
||||
return $this->hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hook
|
||||
*
|
||||
* @param mixed $callback The callback function to call
|
||||
* @param string $hook The name of the hook to add
|
||||
* @param string $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function add(callable $callback, $hook, $name = '*')
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = static::getClassNameFromCallback($callback);
|
||||
}
|
||||
$this->hooks[$name][$hook][] = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function recordHookOptions($commandInfo, $name)
|
||||
{
|
||||
$this->hookOptions[$name][] = $commandInfo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function getNames($command, $callback)
|
||||
{
|
||||
return array_filter(
|
||||
array_merge(
|
||||
static::getNamesUsingCommands($command),
|
||||
[static::getClassNameFromCallback($callback)]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected static function getNamesUsingCommands($command)
|
||||
{
|
||||
return array_merge(
|
||||
[$command->getName()],
|
||||
$command->getAliases()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a command hook does not specify any particular command
|
||||
* name that it should be attached to, then it will be applied
|
||||
* to every command that is defined in the same class as the hook.
|
||||
* This is controlled by using the namespace + class name of
|
||||
* the implementing class of the callback hook.
|
||||
*/
|
||||
protected static function getClassNameFromCallback($callback)
|
||||
{
|
||||
if (!is_array($callback)) {
|
||||
return '';
|
||||
}
|
||||
$reflectionClass = new \ReflectionClass($callback[0]);
|
||||
return $reflectionClass->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an configuration provider hook
|
||||
*
|
||||
* @param type InitializeHookInterface $provider
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addInitializeHook(InitializeHookInterface $initializeHook, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::INITIALIZE][] = $initializeHook;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an option hook
|
||||
*
|
||||
* @param type ValidatorInterface $validator
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addOptionHook(OptionHookInterface $interactor, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::INTERACT][] = $interactor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an interact hook
|
||||
*
|
||||
* @param type ValidatorInterface $validator
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addInteractor(InteractorInterface $interactor, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::INTERACT][] = $interactor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pre-validator hook
|
||||
*
|
||||
* @param type ValidatorInterface $validator
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addPreValidator(ValidatorInterface $validator, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::PRE_ARGUMENT_VALIDATOR][] = $validator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a validator hook
|
||||
*
|
||||
* @param type ValidatorInterface $validator
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addValidator(ValidatorInterface $validator, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::ARGUMENT_VALIDATOR][] = $validator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pre-command hook. This is the same as a validator hook, except
|
||||
* that it will run after all of the post-validator hooks.
|
||||
*
|
||||
* @param type ValidatorInterface $preCommand
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addPreCommandHook(ValidatorInterface $preCommand, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::PRE_COMMAND_HOOK][] = $preCommand;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a post-command hook. This is the same as a pre-process hook,
|
||||
* except that it will run before the first pre-process hook.
|
||||
*
|
||||
* @param type ProcessResultInterface $postCommand
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addPostCommandHook(ProcessResultInterface $postCommand, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::POST_COMMAND_HOOK][] = $postCommand;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a result processor.
|
||||
*
|
||||
* @param type ProcessResultInterface $resultProcessor
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addResultProcessor(ProcessResultInterface $resultProcessor, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::PROCESS_RESULT][] = $resultProcessor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a result alterer. After a result is processed
|
||||
* by a result processor, an alter hook may be used
|
||||
* to convert the result from one form to another.
|
||||
*
|
||||
* @param type AlterResultInterface $resultAlterer
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addAlterResult(AlterResultInterface $resultAlterer, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::ALTER_RESULT][] = $resultAlterer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a status determiner. Usually, a command should return
|
||||
* an integer on error, or a result object on success (which
|
||||
* implies a status code of zero). If a result contains the
|
||||
* status code in some other field, then a status determiner
|
||||
* can be used to call the appropriate accessor method to
|
||||
* determine the status code. This is usually not necessary,
|
||||
* though; a command that fails may return a CommandError
|
||||
* object, which contains a status code and a result message
|
||||
* to display.
|
||||
* @see CommandError::getExitCode()
|
||||
*
|
||||
* @param type StatusDeterminerInterface $statusDeterminer
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addStatusDeterminer(StatusDeterminerInterface $statusDeterminer, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::STATUS_DETERMINER][] = $statusDeterminer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an output extractor. If a command returns an object
|
||||
* object, by default it is passed directly to the output
|
||||
* formatter (if in use) for rendering. If the result object
|
||||
* contains more information than just the data to render, though,
|
||||
* then an output extractor can be used to call the appopriate
|
||||
* accessor method of the result object to get the data to
|
||||
* rendered. This is usually not necessary, though; it is preferable
|
||||
* to have complex result objects implement the OutputDataInterface.
|
||||
* @see OutputDataInterface::getOutputData()
|
||||
*
|
||||
* @param type ExtractOutputInterface $outputExtractor
|
||||
* @param type $name The name of the command to hook
|
||||
* ('*' for all)
|
||||
*/
|
||||
public function addOutputExtractor(ExtractOutputInterface $outputExtractor, $name = '*')
|
||||
{
|
||||
$this->hooks[$name][self::EXTRACT_OUTPUT][] = $outputExtractor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function initializeHook(
|
||||
InputInterface $input,
|
||||
$names,
|
||||
AnnotationData $annotationData
|
||||
) {
|
||||
$providers = $this->getInitializeHooks($names, $annotationData);
|
||||
foreach ($providers as $provider) {
|
||||
$this->callInjectConfigurationHook($provider, $input, $annotationData);
|
||||
}
|
||||
}
|
||||
|
||||
public function optionsHook(
|
||||
\Consolidation\AnnotatedCommand\AnnotatedCommand $command,
|
||||
$names,
|
||||
AnnotationData $annotationData
|
||||
) {
|
||||
$optionHooks = $this->getOptionHooks($names, $annotationData);
|
||||
foreach ($optionHooks as $optionHook) {
|
||||
$this->callOptionHook($optionHook, $command, $annotationData);
|
||||
}
|
||||
$commandInfoList = $this->getHookOptionsForCommand($command);
|
||||
$command->optionsHookForHookAnnotations($commandInfoList);
|
||||
}
|
||||
|
||||
public function getHookOptionsForCommand($command)
|
||||
{
|
||||
$names = $this->addWildcardHooksToNames($command->getNames(), $command->getAnnotationData());
|
||||
return $this->getHookOptions($names);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommandInfo[]
|
||||
*/
|
||||
public function getHookOptions($names)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($names as $name) {
|
||||
if (isset($this->hookOptions[$name])) {
|
||||
$result = array_merge($result, $this->hookOptions[$name]);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function interact(
|
||||
InputInterface $input,
|
||||
OutputInterface $output,
|
||||
$names,
|
||||
AnnotationData $annotationData
|
||||
) {
|
||||
$interactors = $this->getInteractors($names, $annotationData);
|
||||
foreach ($interactors as $interactor) {
|
||||
$this->callInteractor($interactor, $input, $output, $annotationData);
|
||||
}
|
||||
}
|
||||
|
||||
public function validateArguments($names, CommandData $commandData)
|
||||
{
|
||||
$validators = $this->getValidators($names, $commandData->annotationData());
|
||||
foreach ($validators as $validator) {
|
||||
$validated = $this->callValidator($validator, $commandData);
|
||||
if ($validated === false) {
|
||||
return new CommandError();
|
||||
}
|
||||
if (is_object($validated)) {
|
||||
return $validated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process result and decide what to do with it.
|
||||
* Allow client to add transformation / interpretation
|
||||
* callbacks.
|
||||
*/
|
||||
public function alterResult($names, $result, CommandData $commandData)
|
||||
{
|
||||
$processors = $this->getProcessResultHooks($names, $commandData->annotationData());
|
||||
foreach ($processors as $processor) {
|
||||
$result = $this->callProcessor($processor, $result, $commandData);
|
||||
}
|
||||
$alterers = $this->getAlterResultHooks($names, $commandData->annotationData());
|
||||
foreach ($alterers as $alterer) {
|
||||
$result = $this->callProcessor($alterer, $result, $commandData);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call all status determiners, and see if any of them
|
||||
* know how to convert to a status code.
|
||||
*/
|
||||
public function determineStatusCode($names, $result)
|
||||
{
|
||||
// If the result (post-processing) is an object that
|
||||
// implements ExitCodeInterface, then we will ask it
|
||||
// to give us the status code.
|
||||
if ($result instanceof ExitCodeInterface) {
|
||||
return $result->getExitCode();
|
||||
}
|
||||
|
||||
// If the result does not implement ExitCodeInterface,
|
||||
// then we'll see if there is a determiner that can
|
||||
// extract a status code from the result.
|
||||
$determiners = $this->getStatusDeterminers($names);
|
||||
foreach ($determiners as $determiner) {
|
||||
$status = $this->callDeterminer($determiner, $result);
|
||||
if (isset($status)) {
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the result object to printable output in
|
||||
* structured form.
|
||||
*/
|
||||
public function extractOutput($names, $result)
|
||||
{
|
||||
if ($result instanceof OutputDataInterface) {
|
||||
return $result->getOutputData();
|
||||
}
|
||||
|
||||
$extractors = $this->getOutputExtractors($names);
|
||||
foreach ($extractors as $extractor) {
|
||||
$structuredOutput = $this->callExtractor($extractor, $result);
|
||||
if (isset($structuredOutput)) {
|
||||
return $structuredOutput;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getCommandEventHooks($names)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_COMMAND_EVENT,
|
||||
self::COMMAND_EVENT,
|
||||
self::POST_COMMAND_EVENT
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function getInitializeHooks($names, AnnotationData $annotationData)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_INITIALIZE,
|
||||
self::INITIALIZE,
|
||||
self::POST_INITIALIZE
|
||||
],
|
||||
$annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function getOptionHooks($names, AnnotationData $annotationData)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_OPTION_HOOK,
|
||||
self::OPTION_HOOK,
|
||||
self::POST_OPTION_HOOK
|
||||
],
|
||||
$annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function getInteractors($names, AnnotationData $annotationData)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_INTERACT,
|
||||
self::INTERACT,
|
||||
self::POST_INTERACT
|
||||
],
|
||||
$annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function getValidators($names, AnnotationData $annotationData)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_ARGUMENT_VALIDATOR,
|
||||
self::ARGUMENT_VALIDATOR,
|
||||
self::POST_ARGUMENT_VALIDATOR,
|
||||
self::PRE_COMMAND_HOOK,
|
||||
self::COMMAND_HOOK,
|
||||
],
|
||||
$annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function getProcessResultHooks($names, AnnotationData $annotationData)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_PROCESS_RESULT,
|
||||
self::PROCESS_RESULT,
|
||||
self::POST_PROCESS_RESULT
|
||||
],
|
||||
$annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function getAlterResultHooks($names, AnnotationData $annotationData)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::PRE_ALTER_RESULT,
|
||||
self::ALTER_RESULT,
|
||||
self::POST_ALTER_RESULT,
|
||||
self::POST_COMMAND_HOOK,
|
||||
],
|
||||
$annotationData
|
||||
);
|
||||
}
|
||||
|
||||
protected function getStatusDeterminers($names)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::STATUS_DETERMINER,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function getOutputExtractors($names)
|
||||
{
|
||||
return $this->getHooks(
|
||||
$names,
|
||||
[
|
||||
self::EXTRACT_OUTPUT,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of hooks with the provided name(s). Include the
|
||||
* pre- and post- hooks, and also include the global hooks ('*')
|
||||
* in addition to the named hooks provided.
|
||||
*
|
||||
* @param string|array $names The name of the function being hooked.
|
||||
* @param string[] $hooks A list of hooks (e.g. [HookManager::ALTER_RESULT])
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function getHooks($names, $hooks, $annotationData = null)
|
||||
{
|
||||
return $this->get($this->addWildcardHooksToNames($names, $annotationData), $hooks);
|
||||
}
|
||||
|
||||
protected function addWildcardHooksToNames($names, $annotationData = null)
|
||||
{
|
||||
$names = array_merge(
|
||||
(array)$names,
|
||||
($annotationData == null) ? [] : array_map(function ($item) {
|
||||
return "@$item";
|
||||
}, $annotationData->keys())
|
||||
);
|
||||
$names[] = '*';
|
||||
return array_unique($names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of hooks with the provided name(s).
|
||||
*
|
||||
* @param string|array $names The name of the function being hooked.
|
||||
* @param string[] $hooks The list of hook names (e.g. [HookManager::ALTER_RESULT])
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
public function get($names, $hooks)
|
||||
{
|
||||
$result = [];
|
||||
foreach ((array)$hooks as $hook) {
|
||||
foreach ((array)$names as $name) {
|
||||
$result = array_merge($result, $this->getHook($name, $hook));
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single named hook.
|
||||
*
|
||||
* @param string $name The name of the hooked method
|
||||
* @param string $hook The specific hook name (e.g. alter)
|
||||
*
|
||||
* @return callable[]
|
||||
*/
|
||||
protected function getHook($name, $hook)
|
||||
{
|
||||
if (isset($this->hooks[$name][$hook])) {
|
||||
return $this->hooks[$name][$hook];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function callInjectConfigurationHook($provider, $input, AnnotationData $annotationData)
|
||||
{
|
||||
if ($provider instanceof InitializeHookInterface) {
|
||||
return $provider->applyConfiguration($input, $annotationData);
|
||||
}
|
||||
if (is_callable($provider)) {
|
||||
return $provider($input, $annotationData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function callOptionHook($optionHook, $command, AnnotationData $annotationData)
|
||||
{
|
||||
if ($optionHook instanceof OptionHookInterface) {
|
||||
return $optionHook->getOptions($command, $annotationData);
|
||||
}
|
||||
if (is_callable($optionHook)) {
|
||||
return $optionHook($command, $annotationData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function callInteractor($interactor, $input, $output, AnnotationData $annotationData)
|
||||
{
|
||||
if ($interactor instanceof InteractorInterface) {
|
||||
return $interactor->interact($input, $output, $annotationData);
|
||||
}
|
||||
if (is_callable($interactor)) {
|
||||
return $interactor($input, $output, $annotationData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function callValidator($validator, CommandData $commandData)
|
||||
{
|
||||
if ($validator instanceof ValidatorInterface) {
|
||||
return $validator->validate($commandData);
|
||||
}
|
||||
if (is_callable($validator)) {
|
||||
return $validator($commandData);
|
||||
}
|
||||
}
|
||||
|
||||
protected function callProcessor($processor, $result, CommandData $commandData)
|
||||
{
|
||||
$processed = null;
|
||||
if ($processor instanceof ProcessResultInterface) {
|
||||
$processed = $processor->process($result, $commandData);
|
||||
}
|
||||
if (is_callable($processor)) {
|
||||
$processed = $processor($result, $commandData);
|
||||
}
|
||||
if (isset($processed)) {
|
||||
return $processed;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function callDeterminer($determiner, $result)
|
||||
{
|
||||
if ($determiner instanceof StatusDeterminerInterface) {
|
||||
return $determiner->determineStatusCode($result);
|
||||
}
|
||||
if (is_callable($determiner)) {
|
||||
return $determiner($result);
|
||||
}
|
||||
}
|
||||
|
||||
protected function callExtractor($extractor, $result)
|
||||
{
|
||||
if ($extractor instanceof ExtractOutputInterface) {
|
||||
return $extractor->extractOutput($result);
|
||||
}
|
||||
if (is_callable($extractor)) {
|
||||
return $extractor($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConsoleCommandEvent $event
|
||||
*/
|
||||
public function callCommandEventHooks(ConsoleCommandEvent $event)
|
||||
{
|
||||
/* @var Command $command */
|
||||
$command = $event->getCommand();
|
||||
$names = [$command->getName()];
|
||||
$commandEventHooks = $this->getCommandEventHooks($names);
|
||||
foreach ($commandEventHooks as $commandEvent) {
|
||||
if (is_callable($commandEvent)) {
|
||||
$commandEvent($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function findAndAddHookOptions($command)
|
||||
{
|
||||
if (!$command instanceof \Consolidation\AnnotatedCommand\AnnotatedCommand) {
|
||||
return;
|
||||
}
|
||||
$command->optionsHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* @{@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [ConsoleEvents::COMMAND => 'callCommandEventHooks'];
|
||||
}
|
||||
}
|
||||
15
lib/composer/vendor/consolidation/annotated-command/src/Hooks/InitializeHookInterface.php
vendored
Normal file
15
lib/composer/vendor/consolidation/annotated-command/src/Hooks/InitializeHookInterface.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
use Consolidation\AnnotatedCommand\AnnotationData;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* Non-interactively (e.g. via configuration files) apply configuration values to the Input object.
|
||||
*
|
||||
* @see HookManager::addInitializeHook()
|
||||
*/
|
||||
interface InitializeHookInterface
|
||||
{
|
||||
public function initialize(InputInterface $input, Annotation $annotationData);
|
||||
}
|
||||
18
lib/composer/vendor/consolidation/annotated-command/src/Hooks/InteractorInterface.php
vendored
Normal file
18
lib/composer/vendor/consolidation/annotated-command/src/Hooks/InteractorInterface.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
use Consolidation\AnnotatedCommand\AnnotationData;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Interactively supply values for missing required arguments for
|
||||
* the current command. Note that this hook is not called if
|
||||
* the --no-interaction flag is set.
|
||||
*
|
||||
* @see HookManager::addInteractor()
|
||||
*/
|
||||
interface InteractorInterface
|
||||
{
|
||||
public function interact(InputInterface $input, OutputInterface $output, AnnotationData $annotationData);
|
||||
}
|
||||
16
lib/composer/vendor/consolidation/annotated-command/src/Hooks/OptionHookInterface.php
vendored
Normal file
16
lib/composer/vendor/consolidation/annotated-command/src/Hooks/OptionHookInterface.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
use Consolidation\AnnotatedCommand\AnnotationData;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
/**
|
||||
* Add options to a command.
|
||||
*
|
||||
* @see HookManager::addOptionHook()
|
||||
* @see AnnotatedCommandFactory::addListener()
|
||||
*/
|
||||
interface OptionHookInterface
|
||||
{
|
||||
public function getOptions(Command $command, AnnotationData $annotationData);
|
||||
}
|
||||
27
lib/composer/vendor/consolidation/annotated-command/src/Hooks/ProcessResultInterface.php
vendored
Normal file
27
lib/composer/vendor/consolidation/annotated-command/src/Hooks/ProcessResultInterface.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
|
||||
/**
|
||||
* A result processor takes a result object, processes it, and
|
||||
* returns another result object. For example, if a result object
|
||||
* represents a 'task', then a task-runner hook could run the
|
||||
* task and return the result from that execution.
|
||||
*
|
||||
* @see HookManager::addResultProcessor()
|
||||
*/
|
||||
interface ProcessResultInterface
|
||||
{
|
||||
/**
|
||||
* After a command has executed, if the result is something
|
||||
* that needs to be processed, e.g. a collection of tasks to
|
||||
* run, then execute it and return the new result.
|
||||
*
|
||||
* @param mixed $result Result to (potentially) be processed
|
||||
* @param CommandData $commandData Reference to commandline arguments and options
|
||||
*
|
||||
* @return mixed $result
|
||||
*/
|
||||
public function process($result, CommandData $commandData);
|
||||
}
|
||||
18
lib/composer/vendor/consolidation/annotated-command/src/Hooks/StatusDeterminerInterface.php
vendored
Normal file
18
lib/composer/vendor/consolidation/annotated-command/src/Hooks/StatusDeterminerInterface.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
/**
|
||||
* A StatusDeterminer maps from a result to a status exit code.
|
||||
*
|
||||
* @see HookManager::addStatusDeterminer()
|
||||
*/
|
||||
interface StatusDeterminerInterface
|
||||
{
|
||||
/**
|
||||
* Convert a result object into a status code, if
|
||||
* possible. Return null if the result object is unknown.
|
||||
*
|
||||
* @return null|integer
|
||||
*/
|
||||
public function determineStatusCode($result);
|
||||
}
|
||||
14
lib/composer/vendor/consolidation/annotated-command/src/Hooks/ValidatorInterface.php
vendored
Normal file
14
lib/composer/vendor/consolidation/annotated-command/src/Hooks/ValidatorInterface.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Hooks;
|
||||
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
|
||||
/**
|
||||
* Validate the arguments for the current command.
|
||||
*
|
||||
* @see HookManager::addValidator()
|
||||
*/
|
||||
interface ValidatorInterface
|
||||
{
|
||||
public function validate(CommandData $commandData);
|
||||
}
|
||||
85
lib/composer/vendor/consolidation/annotated-command/src/Options/AlterOptionsCommandEvent.php
vendored
Normal file
85
lib/composer/vendor/consolidation/annotated-command/src/Options/AlterOptionsCommandEvent.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Options;
|
||||
|
||||
use Consolidation\AnnotatedCommand\AnnotatedCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\ConsoleEvents;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* AlterOptionsCommandEvent is a subscriber to the Command Event
|
||||
* that looks up any additional options (e.g. from an OPTION_HOOK)
|
||||
* that should be added to the command. Options need to be added
|
||||
* in two circumstances:
|
||||
*
|
||||
* 1. When 'help' for the command is called, so that the additional
|
||||
* command options may be listed in the command description.
|
||||
*
|
||||
* 2. When the command itself is called, so that option validation
|
||||
* may be done.
|
||||
*
|
||||
* We defer the addition of options until these times so that we
|
||||
* do not invoke the option hooks for every command on every run
|
||||
* of the program, and so that we do not need to defer the addition
|
||||
* of all of the application hooks until after all of the application
|
||||
* commands have been added. (Hooks may appear in the same command files
|
||||
* as command implementations; applications may support command file
|
||||
* plug-ins, and hooks may add options to commands defined in other
|
||||
* commandfiles.)
|
||||
*/
|
||||
class AlterOptionsCommandEvent implements EventSubscriberInterface
|
||||
{
|
||||
/** var Application */
|
||||
protected $application;
|
||||
|
||||
public function __construct(Application $application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConsoleCommandEvent $event
|
||||
*/
|
||||
public function alterCommandOptions(ConsoleCommandEvent $event)
|
||||
{
|
||||
/* @var Command $command */
|
||||
$command = $event->getCommand();
|
||||
$input = $event->getInput();
|
||||
if ($command->getName() == 'help') {
|
||||
// Symfony 3.x prepares $input for us; Symfony 2.x, on the other
|
||||
// hand, passes it in prior to binding with the command definition,
|
||||
// so we have to go to a little extra work. It may be inadvisable
|
||||
// to do these steps for commands other than 'help'.
|
||||
if (!$input->hasArgument('command_name')) {
|
||||
$command->ignoreValidationErrors();
|
||||
$command->mergeApplicationDefinition();
|
||||
$input->bind($command->getDefinition());
|
||||
}
|
||||
|
||||
$nameOfCommandToDescribe = $event->getInput()->getArgument('command_name');
|
||||
$commandToDescribe = $this->application->find($nameOfCommandToDescribe);
|
||||
$this->findAndAddHookOptions($commandToDescribe);
|
||||
} else {
|
||||
$this->findAndAddHookOptions($command);
|
||||
}
|
||||
}
|
||||
|
||||
public function findAndAddHookOptions($command)
|
||||
{
|
||||
if (!$command instanceof AnnotatedCommand) {
|
||||
return;
|
||||
}
|
||||
$command->optionsHook();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @{@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [ConsoleEvents::COMMAND => 'alterCommandOptions'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Options;
|
||||
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Option providers can add options to commands based on the annotations
|
||||
* present in a command. For example, a command that specifies @fields
|
||||
* will automatically be given --format and --fields options.
|
||||
*
|
||||
* @see AnnotatedCommandFactory::addListener()
|
||||
* @see HookManager::addOptionHook()
|
||||
*/
|
||||
interface AutomaticOptionsProviderInterface
|
||||
{
|
||||
/**
|
||||
* @return InputOption[]
|
||||
*/
|
||||
public function automaticOptions(CommandInfo $commandInfo);
|
||||
}
|
||||
10
lib/composer/vendor/consolidation/annotated-command/src/Options/PrepareFormatter.php
vendored
Normal file
10
lib/composer/vendor/consolidation/annotated-command/src/Options/PrepareFormatter.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Options;
|
||||
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
use Consolidation\OutputFormatters\Options\FormatterOptions;
|
||||
|
||||
interface PrepareFormatter
|
||||
{
|
||||
public function prepare(CommandData $commandData, FormatterOptions $options);
|
||||
}
|
||||
69
lib/composer/vendor/consolidation/annotated-command/src/Options/PrepareTerminalWidthOption.php
vendored
Normal file
69
lib/composer/vendor/consolidation/annotated-command/src/Options/PrepareTerminalWidthOption.php
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Options;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Consolidation\AnnotatedCommand\CommandData;
|
||||
use Consolidation\OutputFormatters\Options\FormatterOptions;
|
||||
|
||||
class PrepareTerminalWidthOption implements PrepareFormatter
|
||||
{
|
||||
/** var Application */
|
||||
protected $application;
|
||||
|
||||
/** var int */
|
||||
protected $defaultWidth;
|
||||
|
||||
/** var int */
|
||||
protected $maxWidth = PHP_INT_MAX;
|
||||
|
||||
/** var int */
|
||||
protected $minWidth = 0;
|
||||
|
||||
public function __construct($defaultWidth = 0)
|
||||
{
|
||||
$this->defaultWidth = $defaultWidth;
|
||||
}
|
||||
|
||||
public function setApplication(Application $application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
public function prepare(CommandData $commandData, FormatterOptions $options)
|
||||
{
|
||||
$width = $this->getTerminalWidth();
|
||||
if (!$width) {
|
||||
$width = $this->defaultWidth;
|
||||
}
|
||||
|
||||
// Enforce minimum and maximum widths
|
||||
$width = min($width, $this->getMaxWidth($commandData));
|
||||
$width = max($width, $this->getMinWidth($commandData));
|
||||
|
||||
$options->setWidth($width);
|
||||
}
|
||||
|
||||
protected function getTerminalWidth()
|
||||
{
|
||||
if (!$this->application) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$dimensions = $this->application->getTerminalDimensions();
|
||||
if ($dimensions[0] == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $dimensions[0];
|
||||
}
|
||||
|
||||
protected function getMaxWidth(CommandData $commandData)
|
||||
{
|
||||
return $this->maxWidth;
|
||||
}
|
||||
|
||||
protected function getMinWidth(CommandData $commandData)
|
||||
{
|
||||
return $this->minWidth;
|
||||
}
|
||||
}
|
||||
13
lib/composer/vendor/consolidation/annotated-command/src/OutputDataInterface.php
vendored
Normal file
13
lib/composer/vendor/consolidation/annotated-command/src/OutputDataInterface.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand;
|
||||
|
||||
/**
|
||||
* If an annotated command method returns an object that
|
||||
* implements OutputDataInterface, then the getOutputData()
|
||||
* method is used to fetch the output to print from the
|
||||
* result object.
|
||||
*/
|
||||
interface OutputDataInterface
|
||||
{
|
||||
public function getOutputData();
|
||||
}
|
||||
581
lib/composer/vendor/consolidation/annotated-command/src/Parser/CommandInfo.php
vendored
Normal file
581
lib/composer/vendor/consolidation/annotated-command/src/Parser/CommandInfo.php
vendored
Normal file
@@ -0,0 +1,581 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Parser;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Consolidation\AnnotatedCommand\Parser\Internal\CommandDocBlockParser;
|
||||
use Consolidation\AnnotatedCommand\Parser\Internal\CommandDocBlockParserFactory;
|
||||
use Consolidation\AnnotatedCommand\AnnotationData;
|
||||
|
||||
/**
|
||||
* Given a class and method name, parse the annotations in the
|
||||
* DocBlock comment, and provide accessor methods for all of
|
||||
* the elements that are needed to create a Symfony Console Command.
|
||||
*
|
||||
* Note that the name of this class is now somewhat of a misnomer,
|
||||
* as we now use it to hold annotation data for hooks as well as commands.
|
||||
* It would probably be better to rename this to MethodInfo at some point.
|
||||
*/
|
||||
class CommandInfo
|
||||
{
|
||||
/**
|
||||
* @var \ReflectionMethod
|
||||
*/
|
||||
protected $reflection;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
* @var string
|
||||
*/
|
||||
protected $docBlockIsParsed;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $help = '';
|
||||
|
||||
/**
|
||||
* @var DefaultsWithDescriptions
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var DefaultsWithDescriptions
|
||||
*/
|
||||
protected $arguments;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $exampleUsage = [];
|
||||
|
||||
/**
|
||||
* @var AnnotationData
|
||||
*/
|
||||
protected $otherAnnotations;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $aliases = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $methodName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $returnType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $optionParamName;
|
||||
|
||||
/**
|
||||
* Create a new CommandInfo class for a particular method of a class.
|
||||
*
|
||||
* @param string|mixed $classNameOrInstance The name of a class, or an
|
||||
* instance of it.
|
||||
* @param string $methodName The name of the method to get info about.
|
||||
*/
|
||||
public function __construct($classNameOrInstance, $methodName)
|
||||
{
|
||||
$this->reflection = new \ReflectionMethod($classNameOrInstance, $methodName);
|
||||
$this->methodName = $methodName;
|
||||
$this->otherAnnotations = new AnnotationData();
|
||||
// Set up a default name for the command from the method name.
|
||||
// This can be overridden via @command or @name annotations.
|
||||
$this->name = $this->convertName($this->reflection->name);
|
||||
$this->options = new DefaultsWithDescriptions($this->determineOptionsFromParameters(), false);
|
||||
$this->arguments = $this->determineAgumentClassifications();
|
||||
// Remember the name of the last parameter, if it holds the options.
|
||||
// We will use this information to ignore @param annotations for the options.
|
||||
if (!empty($this->options)) {
|
||||
$this->optionParamName = $this->lastParameterName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover the method name provided to the constructor.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMethodName()
|
||||
{
|
||||
return $this->methodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the primary name for this command.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the primary name for this command.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReturnType()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
public function setReturnType($returnType)
|
||||
{
|
||||
$this->returnType = $returnType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any annotations included in the docblock comment for the
|
||||
* implementation method of this command that are not already
|
||||
* handled by the primary methods of this class.
|
||||
*
|
||||
* @return AnnotationData
|
||||
*/
|
||||
public function getRawAnnotations()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->otherAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any annotations included in the docblock comment,
|
||||
* also including default values such as @command. We add
|
||||
* in the default @command annotation late, and only in a
|
||||
* copy of the annotation data because we use the existance
|
||||
* of a @command to indicate that this CommandInfo is
|
||||
* a command, and not a hook or anything else.
|
||||
*
|
||||
* @return AnnotationData
|
||||
*/
|
||||
public function getAnnotations()
|
||||
{
|
||||
return new AnnotationData(
|
||||
$this->getRawAnnotations()->getArrayCopy() +
|
||||
[
|
||||
'command' => $this->getName(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific named annotation for this command.
|
||||
*
|
||||
* @param string $annotation The name of the annotation.
|
||||
* @return string
|
||||
*/
|
||||
public function getAnnotation($annotation)
|
||||
{
|
||||
// hasAnnotation parses the docblock
|
||||
if (!$this->hasAnnotation($annotation)) {
|
||||
return null;
|
||||
}
|
||||
return $this->otherAnnotations[$annotation];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the specified annotation exists for this command.
|
||||
*
|
||||
* @param string $annotation The name of the annotation.
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasAnnotation($annotation)
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return isset($this->otherAnnotations[$annotation]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any tag that we do not explicitly recognize in the
|
||||
* 'otherAnnotations' map.
|
||||
*/
|
||||
public function addAnnotation($name, $content)
|
||||
{
|
||||
$this->otherAnnotations[$name] = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an annotation that was previoudly set.
|
||||
*/
|
||||
public function removeAnnotation($name)
|
||||
{
|
||||
unset($this->otherAnnotations[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the synopsis of the command (~first line).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command description.
|
||||
*
|
||||
* @param string $description The description to set.
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the help text of the command (the description)
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->help;
|
||||
}
|
||||
/**
|
||||
* Set the help text for this command.
|
||||
*
|
||||
* @param string $help The help text.
|
||||
*/
|
||||
public function setHelp($help)
|
||||
{
|
||||
$this->help = $help;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of aliases for this command.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAliases()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set aliases that can be used in place of the command's primary name.
|
||||
*
|
||||
* @param string|string[] $aliases
|
||||
*/
|
||||
public function setAliases($aliases)
|
||||
{
|
||||
if (is_string($aliases)) {
|
||||
$aliases = explode(',', static::convertListToCommaSeparated($aliases));
|
||||
}
|
||||
$this->aliases = array_filter($aliases);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the examples for this command. This is @usage instead of
|
||||
* @example because the later is defined by the phpdoc standard to
|
||||
* be example method calls.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getExampleUsages()
|
||||
{
|
||||
$this->parseDocBlock();
|
||||
return $this->exampleUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an example usage for this command.
|
||||
*
|
||||
* @param string $usage An example of the command, including the command
|
||||
* name and all of its example arguments and options.
|
||||
* @param string $description An explanation of what the example does.
|
||||
*/
|
||||
public function setExampleUsage($usage, $description)
|
||||
{
|
||||
$this->exampleUsage[$usage] = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of refleaction parameters.
|
||||
*
|
||||
* @return ReflectionParameter[]
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->reflection->getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptions of commandline arguements for this command.
|
||||
*
|
||||
* @return DefaultsWithDescriptions
|
||||
*/
|
||||
public function arguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptions of commandline options for this command.
|
||||
*
|
||||
* @return DefaultsWithDescriptions
|
||||
*/
|
||||
public function options()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the last parameter if it holds the options.
|
||||
*/
|
||||
public function optionParamName()
|
||||
{
|
||||
return $this->optionParamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the inputOptions for the options associated with this CommandInfo
|
||||
* object, e.g. via @option annotations, or from
|
||||
* $options = ['someoption' => 'defaultvalue'] in the command method
|
||||
* parameter list.
|
||||
*
|
||||
* @return InputOption[]
|
||||
*/
|
||||
public function inputOptions()
|
||||
{
|
||||
$explicitOptions = [];
|
||||
|
||||
$opts = $this->options()->getValues();
|
||||
foreach ($opts as $name => $defaultValue) {
|
||||
$description = $this->options()->getDescription($name);
|
||||
|
||||
$fullName = $name;
|
||||
$shortcut = '';
|
||||
if (strpos($name, '|')) {
|
||||
list($fullName, $shortcut) = explode('|', $name, 2);
|
||||
}
|
||||
|
||||
if (is_bool($defaultValue)) {
|
||||
$explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_NONE, $description);
|
||||
} elseif ($defaultValue === InputOption::VALUE_REQUIRED) {
|
||||
$explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_REQUIRED, $description);
|
||||
} else {
|
||||
$explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_OPTIONAL, $description, $defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $explicitOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* An option might have a name such as 'silent|s'. In this
|
||||
* instance, we will allow the @option or @default tag to
|
||||
* reference the option only by name (e.g. 'silent' or 's'
|
||||
* instead of 'silent|s').
|
||||
*
|
||||
* @param string $optionName
|
||||
* @return string
|
||||
*/
|
||||
public function findMatchingOption($optionName)
|
||||
{
|
||||
// Exit fast if there's an exact match
|
||||
if ($this->options->exists($optionName)) {
|
||||
return $optionName;
|
||||
}
|
||||
$existingOptionName = $this->findExistingOption($optionName);
|
||||
if (isset($existingOptionName)) {
|
||||
return $existingOptionName;
|
||||
}
|
||||
return $this->findOptionAmongAlternatives($optionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $optionName
|
||||
* @return string
|
||||
*/
|
||||
protected function findOptionAmongAlternatives($optionName)
|
||||
{
|
||||
// Check the other direction: if the annotation contains @silent|s
|
||||
// and the options array has 'silent|s'.
|
||||
$checkMatching = explode('|', $optionName);
|
||||
if (count($checkMatching) > 1) {
|
||||
foreach ($checkMatching as $checkName) {
|
||||
if ($this->options->exists($checkName)) {
|
||||
$this->options->rename($checkName, $optionName);
|
||||
return $optionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $optionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $optionName
|
||||
* @return string|null
|
||||
*/
|
||||
protected function findExistingOption($optionName)
|
||||
{
|
||||
// Check to see if we can find the option name in an existing option,
|
||||
// e.g. if the options array has 'silent|s' => false, and the annotation
|
||||
// is @silent.
|
||||
foreach ($this->options()->getValues() as $name => $default) {
|
||||
if (in_array($optionName, explode('|', $name))) {
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the parameters of the method for this command, and
|
||||
* build a list of commandline arguements for them.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function determineAgumentClassifications()
|
||||
{
|
||||
$result = new DefaultsWithDescriptions();
|
||||
$params = $this->reflection->getParameters();
|
||||
$optionsFromParameters = $this->determineOptionsFromParameters();
|
||||
if (!empty($optionsFromParameters)) {
|
||||
array_pop($params);
|
||||
}
|
||||
foreach ($params as $param) {
|
||||
$this->addParameterToResult($result, $param);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the provided parameter, and determine whether it
|
||||
* is a parameter that will be filled in with a positional
|
||||
* commandline argument.
|
||||
*/
|
||||
protected function addParameterToResult($result, $param)
|
||||
{
|
||||
// Commandline arguments must be strings, so ignore any
|
||||
// parameter that is typehinted to any non-primative class.
|
||||
if ($param->getClass() != null) {
|
||||
return;
|
||||
}
|
||||
$result->add($param->name);
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
$defaultValue = $param->getDefaultValue();
|
||||
if (!$this->isAssoc($defaultValue)) {
|
||||
$result->setDefaultValue($param->name, $defaultValue);
|
||||
}
|
||||
} elseif ($param->isArray()) {
|
||||
$result->setDefaultValue($param->name, []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the parameters of the method for this command, and determine
|
||||
* the disposition of the options from them.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function determineOptionsFromParameters()
|
||||
{
|
||||
$params = $this->reflection->getParameters();
|
||||
if (empty($params)) {
|
||||
return [];
|
||||
}
|
||||
$param = end($params);
|
||||
if (!$param->isDefaultValueAvailable()) {
|
||||
return [];
|
||||
}
|
||||
if (!$this->isAssoc($param->getDefaultValue())) {
|
||||
return [];
|
||||
}
|
||||
return $param->getDefaultValue();
|
||||
}
|
||||
|
||||
protected function lastParameterName()
|
||||
{
|
||||
$params = $this->reflection->getParameters();
|
||||
$param = end($params);
|
||||
if (!$param) {
|
||||
return '';
|
||||
}
|
||||
return $param->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper; determine if an array is associative or not. An array
|
||||
* is not associative if its keys are numeric, and numbered sequentially
|
||||
* from zero. All other arrays are considered to be associative.
|
||||
*
|
||||
* @param arrau $arr The array
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isAssoc($arr)
|
||||
{
|
||||
if (!is_array($arr)) {
|
||||
return false;
|
||||
}
|
||||
return array_keys($arr) !== range(0, count($arr) - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from a method name to the corresponding command name. A
|
||||
* method 'fooBar' will become 'foo:bar', and 'fooBarBazBoz' will
|
||||
* become 'foo:bar-baz-boz'.
|
||||
*
|
||||
* @param string $camel method name.
|
||||
* @return string
|
||||
*/
|
||||
protected function convertName($camel)
|
||||
{
|
||||
$splitter="-";
|
||||
$camel=preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter.'$0', $camel));
|
||||
$camel = preg_replace("/$splitter/", ':', $camel, 1);
|
||||
return strtolower($camel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the docBlock comment for this command, and set the
|
||||
* fields of this class with the data thereby obtained.
|
||||
*/
|
||||
protected function parseDocBlock()
|
||||
{
|
||||
if (!$this->docBlockIsParsed) {
|
||||
// The parse function will insert data from the provided method
|
||||
// into this object, using our accessors.
|
||||
CommandDocBlockParserFactory::parse($this, $this->reflection);
|
||||
$this->docBlockIsParsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list that might be 'a b c' or 'a, b, c' or 'a,b,c',
|
||||
* convert the data into the last of these forms.
|
||||
*/
|
||||
protected static function convertListToCommaSeparated($text)
|
||||
{
|
||||
return preg_replace('#[ \t\n\r,]+#', ',', $text);
|
||||
}
|
||||
}
|
||||
160
lib/composer/vendor/consolidation/annotated-command/src/Parser/DefaultsWithDescriptions.php
vendored
Normal file
160
lib/composer/vendor/consolidation/annotated-command/src/Parser/DefaultsWithDescriptions.php
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Parser;
|
||||
|
||||
/**
|
||||
* An associative array that maps from key to default value;
|
||||
* each entry can also have a description.
|
||||
*/
|
||||
class DefaultsWithDescriptions
|
||||
{
|
||||
/**
|
||||
* @var array Associative array of key : default mappings
|
||||
*/
|
||||
protected $values;
|
||||
|
||||
/**
|
||||
* @var array Associative array used like a set to indicate default value
|
||||
* exists for the key.
|
||||
*/
|
||||
protected $hasDefault;
|
||||
|
||||
/**
|
||||
* @var array Associative array of key : description mappings
|
||||
*/
|
||||
protected $descriptions;
|
||||
|
||||
/**
|
||||
* @var mixed Default value that the default value of items in
|
||||
* the collection should take when not specified in the 'add' method.
|
||||
*/
|
||||
protected $defaultDefault;
|
||||
|
||||
public function __construct($values = [], $defaultDefault = null)
|
||||
{
|
||||
$this->values = $values;
|
||||
$this->hasDefault = [];
|
||||
$this->descriptions = [];
|
||||
$this->defaultDefault = $defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return just the key : default values mapping
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this set of options is empty
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see whether the speicifed key exists in the collection.
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function exists($key)
|
||||
{
|
||||
return array_key_exists($key, $this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of one entry.
|
||||
*
|
||||
* @param string $key The key of the item.
|
||||
* @return string
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->values)) {
|
||||
return $this->values[$key];
|
||||
}
|
||||
return $this->defaultDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description of one entry.
|
||||
*
|
||||
* @param string $key The key of the item.
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->descriptions)) {
|
||||
return $this->descriptions[$key];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another argument to this command.
|
||||
*
|
||||
* @param string $key Name of the argument.
|
||||
* @param string $description Help text for the argument.
|
||||
* @param mixed $defaultValue The default value for the argument.
|
||||
*/
|
||||
public function add($key, $description = '', $defaultValue = null)
|
||||
{
|
||||
if (!$this->exists($key) || isset($defaultValue)) {
|
||||
$this->values[$key] = isset($defaultValue) ? $defaultValue : $this->defaultDefault;
|
||||
}
|
||||
unset($this->descriptions[$key]);
|
||||
if (!empty($description)) {
|
||||
$this->descriptions[$key] = $description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the default value of an entry.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $defaultValue
|
||||
*/
|
||||
public function setDefaultValue($key, $defaultValue)
|
||||
{
|
||||
$this->values[$key] = $defaultValue;
|
||||
$this->hasDefault[$key] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the named argument definitively has a default value.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDefault($key)
|
||||
{
|
||||
return array_key_exists($key, $this->hasDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an entry
|
||||
*
|
||||
* @param string $key The entry to remove
|
||||
*/
|
||||
public function clear($key)
|
||||
{
|
||||
unset($this->values[$key]);
|
||||
unset($this->descriptions[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename an existing option to something else.
|
||||
*/
|
||||
public function rename($oldName, $newName)
|
||||
{
|
||||
$this->add($newName, $this->getDescription($oldName), $this->get($oldName));
|
||||
$this->clear($oldName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Parser\Internal;
|
||||
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
use Consolidation\AnnotatedCommand\Parser\DefaultsWithDescriptions;
|
||||
|
||||
/**
|
||||
* Given a class and method name, parse the annotations in the
|
||||
* DocBlock comment, and provide accessor methods for all of
|
||||
* the elements that are needed to create an annotated Command.
|
||||
*/
|
||||
abstract class AbstractCommandDocBlockParser
|
||||
{
|
||||
/**
|
||||
* @var CommandInfo
|
||||
*/
|
||||
protected $commandInfo;
|
||||
|
||||
/**
|
||||
* @var \ReflectionMethod
|
||||
*/
|
||||
protected $reflection;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tagProcessors = [
|
||||
'command' => 'processCommandTag',
|
||||
'name' => 'processCommandTag',
|
||||
'arg' => 'processArgumentTag',
|
||||
'param' => 'processParamTag',
|
||||
'return' => 'processReturnTag',
|
||||
'option' => 'processOptionTag',
|
||||
'default' => 'processDefaultTag',
|
||||
'aliases' => 'processAliases',
|
||||
'usage' => 'processUsageTag',
|
||||
'description' => 'processAlternateDescriptionTag',
|
||||
'desc' => 'processAlternateDescriptionTag',
|
||||
];
|
||||
|
||||
public function __construct(CommandInfo $commandInfo, \ReflectionMethod $reflection)
|
||||
{
|
||||
$this->commandInfo = $commandInfo;
|
||||
$this->reflection = $reflection;
|
||||
}
|
||||
|
||||
protected function processAllTags($phpdoc)
|
||||
{
|
||||
// Iterate over all of the tags, and process them as necessary.
|
||||
foreach ($phpdoc->getTags() as $tag) {
|
||||
$processFn = [$this, 'processGenericTag'];
|
||||
if (array_key_exists($tag->getName(), $this->tagProcessors)) {
|
||||
$processFn = [$this, $this->tagProcessors[$tag->getName()]];
|
||||
}
|
||||
$processFn($tag);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function getTagContents($tag);
|
||||
|
||||
/**
|
||||
* Parse the docBlock comment for this command, and set the
|
||||
* fields of this class with the data thereby obtained.
|
||||
*/
|
||||
abstract public function parse();
|
||||
|
||||
/**
|
||||
* Save any tag that we do not explicitly recognize in the
|
||||
* 'otherAnnotations' map.
|
||||
*/
|
||||
protected function processGenericTag($tag)
|
||||
{
|
||||
$this->commandInfo->addAnnotation($tag->getName(), $this->getTagContents($tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the command from a @command or @name annotation.
|
||||
*/
|
||||
protected function processCommandTag($tag)
|
||||
{
|
||||
$commandName = $this->getTagContents($tag);
|
||||
$this->commandInfo->setName($commandName);
|
||||
// We also store the name in the 'other annotations' so that is is
|
||||
// possible to determine if the method had a @command annotation.
|
||||
$this->commandInfo->addAnnotation($tag->getName(), $commandName);
|
||||
}
|
||||
|
||||
/**
|
||||
* The @description and @desc annotations may be used in
|
||||
* place of the synopsis (which we call 'description').
|
||||
* This is discouraged.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
protected function processAlternateDescriptionTag($tag)
|
||||
{
|
||||
$this->commandInfo->setDescription($this->getTagContents($tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @arg annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processArgumentTag($tag)
|
||||
{
|
||||
if (!$this->pregMatchNameAndDescription((string)$tag->getDescription(), $match)) {
|
||||
return;
|
||||
}
|
||||
$this->addOptionOrArgumentTag($tag, $this->commandInfo->arguments(), $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from an @option annotation in our option descriptions.
|
||||
*/
|
||||
protected function processOptionTag($tag)
|
||||
{
|
||||
if (!$this->pregMatchOptionNameAndDescription((string)$tag->getDescription(), $match)) {
|
||||
return;
|
||||
}
|
||||
$this->addOptionOrArgumentTag($tag, $this->commandInfo->options(), $match);
|
||||
}
|
||||
|
||||
protected function addOptionOrArgumentTag($tag, DefaultsWithDescriptions $set, $nameAndDescription)
|
||||
{
|
||||
$variableName = $this->commandInfo->findMatchingOption($nameAndDescription['name']);
|
||||
$desc = $nameAndDescription['description'];
|
||||
$description = static::removeLineBreaks($desc);
|
||||
$set->add($variableName, $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @default annotation in our argument or option store,
|
||||
* as appropriate.
|
||||
*/
|
||||
protected function processDefaultTag($tag)
|
||||
{
|
||||
if (!$this->pregMatchNameAndDescription((string)$tag->getDescription(), $match)) {
|
||||
return;
|
||||
}
|
||||
$variableName = $match['name'];
|
||||
$defaultValue = $this->interpretDefaultValue($match['description']);
|
||||
if ($this->commandInfo->arguments()->exists($variableName)) {
|
||||
$this->commandInfo->arguments()->setDefaultValue($variableName, $defaultValue);
|
||||
return;
|
||||
}
|
||||
$variableName = $this->commandInfo->findMatchingOption($variableName);
|
||||
if ($this->commandInfo->options()->exists($variableName)) {
|
||||
$this->commandInfo->options()->setDefaultValue($variableName, $defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @usage annotation in our example usage list.
|
||||
*/
|
||||
protected function processUsageTag($tag)
|
||||
{
|
||||
$lines = explode("\n", $this->getTagContents($tag));
|
||||
$usage = array_shift($lines);
|
||||
$description = static::removeLineBreaks(implode("\n", $lines));
|
||||
|
||||
$this->commandInfo->setExampleUsage($usage, $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the comma-separated list of aliases
|
||||
*/
|
||||
protected function processAliases($tag)
|
||||
{
|
||||
$this->commandInfo->setAliases((string)$tag->getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @param annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processParamTag($tag)
|
||||
{
|
||||
$variableName = $tag->getVariableName();
|
||||
$variableName = str_replace('$', '', $variableName);
|
||||
$description = static::removeLineBreaks((string)$tag->getDescription());
|
||||
if ($variableName == $this->commandInfo->optionParamName()) {
|
||||
return;
|
||||
}
|
||||
$this->commandInfo->arguments()->add($variableName, $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @return annotation in our argument descriptions.
|
||||
*/
|
||||
abstract protected function processReturnTag($tag);
|
||||
|
||||
protected function interpretDefaultValue($defaultValue)
|
||||
{
|
||||
$defaults = [
|
||||
'null' => null,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
"''" => '',
|
||||
'[]' => [],
|
||||
];
|
||||
foreach ($defaults as $defaultName => $defaultTypedValue) {
|
||||
if ($defaultValue == $defaultName) {
|
||||
return $defaultTypedValue;
|
||||
}
|
||||
}
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a docblock description in the form "$variable description",
|
||||
* return the variable name and description via the 'match' parameter.
|
||||
*/
|
||||
protected function pregMatchNameAndDescription($source, &$match)
|
||||
{
|
||||
$nameRegEx = '\\$(?P<name>[^ \t]+)[ \t]+';
|
||||
$descriptionRegEx = '(?P<description>.*)';
|
||||
$optionRegEx = "/{$nameRegEx}{$descriptionRegEx}/s";
|
||||
|
||||
return preg_match($optionRegEx, $source, $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a docblock description in the form "$variable description",
|
||||
* return the variable name and description via the 'match' parameter.
|
||||
*/
|
||||
protected function pregMatchOptionNameAndDescription($source, &$match)
|
||||
{
|
||||
// Strip type and $ from the text before the @option name, if present.
|
||||
$source = preg_replace('/^[a-zA-Z]* ?\\$/', '', $source);
|
||||
$nameRegEx = '(?P<name>[^ \t]+)[ \t]+';
|
||||
$descriptionRegEx = '(?P<description>.*)';
|
||||
$optionRegEx = "/{$nameRegEx}{$descriptionRegEx}/s";
|
||||
|
||||
return preg_match($optionRegEx, $source, $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list that might be 'a b c' or 'a, b, c' or 'a,b,c',
|
||||
* convert the data into the last of these forms.
|
||||
*/
|
||||
protected static function convertListToCommaSeparated($text)
|
||||
{
|
||||
return preg_replace('#[ \t\n\r,]+#', ',', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a multiline description and convert it into a single
|
||||
* long unbroken line.
|
||||
*/
|
||||
protected static function removeLineBreaks($text)
|
||||
{
|
||||
return trim(preg_replace('#[ \t\n\r]+#', ' ', $text));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Parser\Internal;
|
||||
|
||||
use phpDocumentor\Reflection\DocBlock;
|
||||
use phpDocumentor\Reflection\DocBlock\Tag\ParamTag;
|
||||
use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag;
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
use Consolidation\AnnotatedCommand\Parser\DefaultsWithDescriptions;
|
||||
|
||||
/**
|
||||
* Given a class and method name, parse the annotations in the
|
||||
* DocBlock comment, and provide accessor methods for all of
|
||||
* the elements that are needed to create an annotated Command.
|
||||
*/
|
||||
class CommandDocBlockParser2 extends AbstractCommandDocBlockParser
|
||||
{
|
||||
/**
|
||||
* Parse the docBlock comment for this command, and set the
|
||||
* fields of this class with the data thereby obtained.
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
$docblockComment = $this->reflection->getDocComment();
|
||||
$phpdoc = new DocBlock($docblockComment);
|
||||
|
||||
// First set the description (synopsis) and help.
|
||||
$this->commandInfo->setDescription((string)$phpdoc->getShortDescription());
|
||||
$this->commandInfo->setHelp((string)$phpdoc->getLongDescription());
|
||||
|
||||
$this->processAllTags($phpdoc);
|
||||
}
|
||||
|
||||
protected function getTagContents($tag)
|
||||
{
|
||||
return $tag->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @arg annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processArgumentTag($tag)
|
||||
{
|
||||
if (!$this->pregMatchNameAndDescription((string)$tag->getDescription(), $match)) {
|
||||
return;
|
||||
}
|
||||
$this->addOptionOrArgumentTag($tag, $this->commandInfo->arguments(), $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @param annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processParamTag($tag)
|
||||
{
|
||||
if (!$tag instanceof ParamTag) {
|
||||
return;
|
||||
}
|
||||
return parent::processParamTag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @return annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processReturnTag($tag)
|
||||
{
|
||||
if (!$tag instanceof ReturnTag) {
|
||||
return;
|
||||
}
|
||||
$this->commandInfo->setReturnType($tag->getType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Parser\Internal;
|
||||
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
use Consolidation\AnnotatedCommand\Parser\DefaultsWithDescriptions;
|
||||
|
||||
/**
|
||||
* Given a class and method name, parse the annotations in the
|
||||
* DocBlock comment, and provide accessor methods for all of
|
||||
* the elements that are needed to create an annotated Command.
|
||||
*/
|
||||
class CommandDocBlockParser3 extends AbstractCommandDocBlockParser
|
||||
{
|
||||
/**
|
||||
* Parse the docBlock comment for this command, and set the
|
||||
* fields of this class with the data thereby obtained.
|
||||
*/
|
||||
public function parse()
|
||||
{
|
||||
// DocBlockFactory::create fails if the comment is empty.
|
||||
$docComment = $this->reflection->getDocComment();
|
||||
if (empty($docComment)) {
|
||||
return;
|
||||
}
|
||||
$phpdoc = $this->createDocBlock();
|
||||
|
||||
// First set the description (synopsis) and help.
|
||||
$this->commandInfo->setDescription((string)$phpdoc->getSummary());
|
||||
$this->commandInfo->setHelp((string)$phpdoc->getDescription());
|
||||
|
||||
$this->processAllTags($phpdoc);
|
||||
}
|
||||
|
||||
public function createDocBlock()
|
||||
{
|
||||
$docBlockFactory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
|
||||
$contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
|
||||
|
||||
return $docBlockFactory->create(
|
||||
$this->reflection,
|
||||
$contextFactory->createFromReflector($this->reflection)
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTagContents($tag)
|
||||
{
|
||||
return (string)$tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @param annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processParamTag($tag)
|
||||
{
|
||||
if (!$tag instanceof Param) {
|
||||
return;
|
||||
}
|
||||
return parent::processParamTag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the data from a @return annotation in our argument descriptions.
|
||||
*/
|
||||
protected function processReturnTag($tag)
|
||||
{
|
||||
if (!$tag instanceof Return_) {
|
||||
return;
|
||||
}
|
||||
// If there is a spurrious trailing space on the return type, remove it.
|
||||
$this->commandInfo->setReturnType(trim($this->getTagContents($tag)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Consolidation\AnnotatedCommand\Parser\Internal;
|
||||
|
||||
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
|
||||
|
||||
/**
|
||||
* Create an appropriate CommandDocBlockParser.
|
||||
*/
|
||||
class CommandDocBlockParserFactory
|
||||
{
|
||||
public static function parse(CommandInfo $commandInfo, \ReflectionMethod $reflection)
|
||||
{
|
||||
return static::create($commandInfo, $reflection)->parse();
|
||||
}
|
||||
|
||||
private static function create(CommandInfo $commandInfo, \ReflectionMethod $reflection)
|
||||
{
|
||||
if (static::hasReflectionDocBlock3()) {
|
||||
return new CommandDocBlockParser3($commandInfo, $reflection);
|
||||
}
|
||||
return new CommandDocBlockParser2($commandInfo, $reflection);
|
||||
}
|
||||
|
||||
private static function hasReflectionDocBlock3()
|
||||
{
|
||||
return class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user