/**
  * Interactively prompt the user for input
  * based on defined synopsis and passed arguments.
  *
  * @param array $args
  * @param array $assoc_args
  * @return array
  */
 private function prompt_args($args, $assoc_args)
 {
     $synopsis = $this->get_synopsis();
     if (!$synopsis) {
         return array($args, $assoc_args);
     }
     $spec = array_filter(\WP_CLI\SynopsisParser::parse($synopsis), function ($spec_arg) {
         return in_array($spec_arg['type'], array('generic', 'positional', 'assoc', 'flag'));
     });
     $spec = array_values($spec);
     // 'positional' arguments are positional (aka zero-indexed)
     // so $args needs to be reset before prompting for new arguments
     $args = array();
     foreach ($spec as $key => $spec_arg) {
         $current_prompt = $key + 1 . '/' . count($spec) . ' ';
         $default = $spec_arg['optional'] ? '' : false;
         // 'generic' permits arbitrary key=value (e.g. [--<field>=<value>] )
         if ('generic' == $spec_arg['type']) {
             list($key_token, $value_token) = explode('=', $spec_arg['token']);
             $repeat = false;
             do {
                 if (!$repeat) {
                     $key_prompt = $current_prompt . $key_token;
                 } else {
                     $key_prompt = str_repeat(" ", strlen($current_prompt)) . $key_token;
                 }
                 $key = $this->prompt($key_prompt, $default);
                 if (false === $key) {
                     return array($args, $assoc_args);
                 }
                 if ($key) {
                     $key_prompt_count = strlen($key_prompt) - strlen($value_token) - 1;
                     $value_prompt = str_repeat(" ", $key_prompt_count) . '=' . $value_token;
                     $value = $this->prompt($value_prompt, $default);
                     if (false === $value) {
                         return array($args, $assoc_args);
                     }
                     $assoc_args[$key] = $value;
                     $repeat = true;
                     $required = false;
                 } else {
                     $repeat = false;
                 }
             } while ($required || $repeat);
         } else {
             $prompt = $current_prompt . $spec_arg['token'];
             if ('flag' == $spec_arg['type']) {
                 $prompt .= ' (Y/n)';
             }
             $response = $this->prompt($prompt, $default);
             if (false === $response) {
                 return array($args, $assoc_args);
             }
             if ($response) {
                 switch ($spec_arg['type']) {
                     case 'positional':
                         if ($spec_arg['repeating']) {
                             $response = explode(' ', $response);
                         } else {
                             $response = array($response);
                         }
                         $args = array_merge($args, $response);
                         break;
                     case 'assoc':
                         $assoc_args[$spec_arg['name']] = $response;
                         break;
                     case 'flag':
                         if ('Y' == $response) {
                             $assoc_args[$spec_arg['name']] = true;
                         }
                         break;
                 }
             }
         }
     }
     return array($args, $assoc_args);
 }
Beispiel #2
0
 /**
  * Validate the supplied arguments to the command.
  * Throws warnings or errors if arguments are missing
  * or invalid.
  *
  * @param array $args
  * @param array $assoc_args
  * @param array $extra_args
  * @return array list of invalid $assoc_args keys to unset
  */
 private function validate_args($args, $assoc_args, $extra_args)
 {
     $synopsis = $this->get_synopsis();
     if (!$synopsis) {
         return array(array(), $args, $assoc_args, $extra_args);
     }
     $validator = new \WP_CLI\SynopsisValidator($synopsis);
     $cmd_path = implode(' ', get_path($this));
     foreach ($validator->get_unknown() as $token) {
         \WP_CLI::warning(sprintf("The `%s` command has an invalid synopsis part: %s", $cmd_path, $token));
     }
     if (!$validator->enough_positionals($args)) {
         $this->show_usage();
         exit(1);
     }
     $unknown_positionals = $validator->unknown_positionals($args);
     if (!empty($unknown_positionals)) {
         \WP_CLI::error('Too many positional arguments: ' . implode(' ', $unknown_positionals));
     }
     $synopsis_spec = \WP_CLI\SynopsisParser::parse($synopsis);
     $i = 0;
     $errors = array('fatal' => array(), 'warning' => array());
     foreach ($synopsis_spec as $spec) {
         if ('positional' === $spec['type']) {
             $spec_args = $this->docparser->get_arg_args($spec['name']);
             if (!isset($args[$i])) {
                 if (isset($spec_args['default'])) {
                     $args[$i] = $spec_args['default'];
                 }
             }
             if (isset($spec_args['options'])) {
                 if (!empty($spec['repeating'])) {
                     do {
                         if (isset($args[$i]) && !in_array($args[$i], $spec_args['options'])) {
                             \WP_CLI::error('Invalid value specified for positional arg.');
                         }
                         $i++;
                     } while (isset($args[$i]));
                 } else {
                     if (!in_array($args[$i], $spec_args['options'])) {
                         \WP_CLI::error('Invalid value specified for positional arg.');
                     }
                 }
             }
             $i++;
         } else {
             if ('assoc' === $spec['type']) {
                 $spec_args = $this->docparser->get_param_args($spec['name']);
                 if (!isset($assoc_args[$spec['name']])) {
                     if (isset($spec_args['default'])) {
                         $assoc_args[$spec['name']] = $spec_args['default'];
                     }
                 }
                 if (isset($assoc_args[$spec['name']]) && isset($spec_args['options'])) {
                     if (!in_array($assoc_args[$spec['name']], $spec_args['options'])) {
                         $errors['fatal'][$spec['name']] = "Invalid value specified for '{$spec['name']}'";
                     }
                 }
             }
         }
     }
     list($returned_errors, $to_unset) = $validator->validate_assoc(array_merge(\WP_CLI::get_config(), $extra_args, $assoc_args));
     foreach (array('fatal', 'warning') as $error_type) {
         $errors[$error_type] = array_merge($errors[$error_type], $returned_errors[$error_type]);
     }
     if ($this->name != 'help') {
         foreach ($validator->unknown_assoc($assoc_args) as $key) {
             $errors['fatal'][] = "unknown --{$key} parameter";
         }
     }
     if (!empty($errors['fatal'])) {
         $out = 'Parameter errors:';
         foreach ($errors['fatal'] as $key => $error) {
             $out .= "\n {$error}";
             if ($desc = $this->docparser->get_param_desc($key)) {
                 $out .= " ({$desc})";
             }
         }
         \WP_CLI::error($out);
     }
     array_map('\\WP_CLI::warning', $errors['warning']);
     return array($to_unset, $args, $assoc_args, $extra_args);
 }
Beispiel #3
0
 /**
  * Add a command to the wp-cli list of commands
  *
  * @param string $name The name of the command that will be used in the CLI
  * @param string $callable The command implementation as a class, function or closure
  * @param array $args An associative array with additional parameters:
  *   'before_invoke' => callback to execute before invoking the command,
  *   'shortdesc' => short description (80 char or less) for the command,
  *   'synopsis' => the synopsis for the command (string or array)
  *   'when' => execute callback on a named WP-CLI hook (e.g. before_wp_load)
  */
 public static function add_command($name, $callable, $args = array())
 {
     $valid = false;
     if (is_object($callable) && $callable instanceof \Closure) {
         $valid = true;
     } else {
         if (is_string($callable) && function_exists($callable)) {
             $valid = true;
         } else {
             if (is_string($callable) && class_exists((string) $callable)) {
                 $valid = true;
             } else {
                 if (is_object($callable)) {
                     $valid = true;
                 } else {
                     if (is_array($callable) && is_callable($callable)) {
                         $valid = true;
                     }
                 }
             }
         }
     }
     if (!$valid) {
         if (is_array($callable)) {
             $callable[0] = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
             $callable = array($callable[0], $callable[1]);
         }
         WP_CLI::error(sprintf("Callable %s does not exist, and cannot be registered as `wp %s`.", json_encode($callable), $name));
     }
     if (isset($args['before_invoke'])) {
         self::add_hook("before_invoke:{$name}", $args['before_invoke']);
     }
     $path = preg_split('/\\s+/', $name);
     $leaf_name = array_pop($path);
     $full_path = $path;
     $command = self::get_root_command();
     while (!empty($path)) {
         $subcommand_name = $path[0];
         $subcommand = $command->find_subcommand($path);
         // create an empty container
         if (!$subcommand) {
             $subcommand = new Dispatcher\CompositeCommand($command, $subcommand_name, new \WP_CLI\DocParser(''));
             $command->add_subcommand($subcommand_name, $subcommand);
         }
         $command = $subcommand;
     }
     $leaf_command = Dispatcher\CommandFactory::create($leaf_name, $callable, $command);
     if (!$command->can_have_subcommands()) {
         throw new Exception(sprintf("'%s' can't have subcommands.", implode(' ', Dispatcher\get_path($command))));
     }
     if (isset($args['shortdesc'])) {
         $leaf_command->set_shortdesc($args['shortdesc']);
     }
     if (isset($args['synopsis'])) {
         if (is_string($args['synopsis'])) {
             $leaf_command->set_synopsis($args['synopsis']);
         } else {
             if (is_array($args['synopsis'])) {
                 $leaf_command->set_synopsis(\WP_CLI\SynopsisParser::render($args['synopsis']));
             }
         }
     }
     if (isset($args['when'])) {
         self::get_runner()->register_early_invoke($args['when'], $leaf_command);
     }
     $command->add_subcommand($leaf_name, $leaf_command);
 }
Beispiel #4
0
 function testAllowedValueCharacters()
 {
     $r = SynopsisParser::parse('--capitals=<VALUE> --hyphen=<val-ue> --combined=<VAL-ue> --disallowed=<wrong:char>');
     $this->assertCount(4, $r);
     $param = $r[0];
     $this->assertEquals('assoc', $param['type']);
     $this->assertFalse($param['optional']);
     $param = $r[1];
     $this->assertEquals('assoc', $param['type']);
     $this->assertFalse($param['optional']);
     $param = $r[2];
     $this->assertEquals('assoc', $param['type']);
     $this->assertFalse($param['optional']);
     $this->assertEquals('unknown', $r[3]['type']);
 }
Beispiel #5
0
 function testParseThenRender()
 {
     $o = '<positional> --assoc=<assoc> --<field>=<value> [--flag]';
     $a = SynopsisParser::parse($o);
     $r = SynopsisParser::render($a);
     $this->assertEquals($o, $r);
 }
Beispiel #6
0
 /**
  * Register a command to WP-CLI.
  *
  * WP-CLI supports using any callable class, function, or closure as a
  * command. `WP_CLI::add_command()` is used for both internal and
  * third-party command registration.
  *
  * Command arguments are parsed from PHPDoc by default, but also can be
  * supplied as an optional third argument during registration.
  *
  * ```
  * # Register a custom 'foo' command to output a supplied positional param.
  * #
  * # $ wp foo bar
  * # Success: bar
  *
  * /**
  *  * My awesome closure command
  *  *
  *  * <message>
  *  * : An awesome message to display
  *  *
  *  * @when before_wp_load
  *  *\/
  * $foo = function( $args ) {
  *     WP_CLI::success( $args[0] );
  * };
  * WP_CLI::add_command( 'foo', $foo );
  * ```
  *
  * @access public
  * @category Registration
  *
  * @param string $name Name for the command (e.g. "post list" or "site empty").
  * @param string $callable Command implementation as a class, function or closure.
  * @param array $args {
  *      Optional An associative array with additional registration parameters.
  *      'before_invoke' => callback to execute before invoking the command,
  *      'after_invoke' => callback to execute after invoking the command,
  *      'shortdesc' => short description (80 char or less) for the command,
  *      'synopsis' => the synopsis for the command (string or array),
  *      'when' => execute callback on a named WP-CLI hook (e.g. before_wp_load),
  * }
  * @return true True on success, hard error if registration failed.
  */
 public static function add_command($name, $callable, $args = array())
 {
     $valid = false;
     if (is_callable($callable)) {
         $valid = true;
     } else {
         if (is_string($callable) && class_exists((string) $callable)) {
             $valid = true;
         } else {
             if (is_object($callable)) {
                 $valid = true;
             }
         }
     }
     if (!$valid) {
         if (is_array($callable)) {
             $callable[0] = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
             $callable = array($callable[0], $callable[1]);
         }
         WP_CLI::error(sprintf("Callable %s does not exist, and cannot be registered as `wp %s`.", json_encode($callable), $name));
     }
     foreach (array('before_invoke', 'after_invoke') as $when) {
         if (isset($args[$when])) {
             self::add_hook("{$when}:{$name}", $args[$when]);
         }
     }
     $path = preg_split('/\\s+/', $name);
     $leaf_name = array_pop($path);
     $full_path = $path;
     $command = self::get_root_command();
     while (!empty($path)) {
         $subcommand_name = $path[0];
         $subcommand = $command->find_subcommand($path);
         // create an empty container
         if (!$subcommand) {
             $subcommand = new Dispatcher\CompositeCommand($command, $subcommand_name, new \WP_CLI\DocParser(''));
             $command->add_subcommand($subcommand_name, $subcommand);
         }
         $command = $subcommand;
     }
     $leaf_command = Dispatcher\CommandFactory::create($leaf_name, $callable, $command);
     if (!$command->can_have_subcommands()) {
         throw new Exception(sprintf("'%s' can't have subcommands.", implode(' ', Dispatcher\get_path($command))));
     }
     if (isset($args['shortdesc'])) {
         $leaf_command->set_shortdesc($args['shortdesc']);
     }
     if (isset($args['synopsis'])) {
         if (is_string($args['synopsis'])) {
             $leaf_command->set_synopsis($args['synopsis']);
         } else {
             if (is_array($args['synopsis'])) {
                 $synopsis = \WP_CLI\SynopsisParser::render($args['synopsis']);
                 $leaf_command->set_synopsis($synopsis);
                 $long_desc = '';
                 $bits = explode(' ', $synopsis);
                 foreach ($args['synopsis'] as $key => $arg) {
                     $long_desc .= $bits[$key] . PHP_EOL;
                     if (!empty($arg['description'])) {
                         $long_desc .= ': ' . $arg['description'] . PHP_EOL;
                     }
                     $yamlify = array();
                     foreach (array('default', 'options') as $key) {
                         if (isset($arg[$key])) {
                             $yamlify[$key] = $arg[$key];
                         }
                     }
                     if (!empty($yamlify)) {
                         $long_desc .= \Spyc::YAMLDump($yamlify);
                         $long_desc .= '---' . PHP_EOL;
                     }
                     $long_desc .= PHP_EOL;
                 }
                 if (!empty($long_desc)) {
                     $long_desc = rtrim($long_desc, PHP_EOL);
                     $long_desc = '## OPTIONS' . PHP_EOL . PHP_EOL . $long_desc;
                     $leaf_command->set_longdesc($long_desc);
                 }
             }
         }
     }
     if (isset($args['when'])) {
         self::get_runner()->register_early_invoke($args['when'], $leaf_command);
     }
     $command->add_subcommand($leaf_name, $leaf_command);
     return true;
 }