/** * 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); }
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']); }
/** * 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); }
function testParseThenRender() { $o = '<positional> --assoc=<assoc> --<field>=<value> [--flag]'; $a = SynopsisParser::parse($o); $r = SynopsisParser::render($a); $this->assertEquals($o, $r); }