Browse Source

Merge pull request #5235 from cakephp/3.0-console-option-parser-merge

3.0: Make console option parsers mergable/combinable
Mark Story 11 years ago
parent
commit
7303e22dad

+ 10 - 0
src/Console/ConsoleInputArgument.php

@@ -83,6 +83,16 @@ class ConsoleInputArgument {
 	}
 
 /**
+ * Checks if this argument is equal to another argument.
+ *
+ * @param \Cake\Console\ConsoleInputArgument $argument ConsoleInputArgument to compare to.
+ * @return bool
+ */
+	public function isEqualTo(ConsoleInputArgument $argument) {
+		return $this->usage() === $argument->usage();
+	}
+
+/**
  * Generate the help for this argument.
  *
  * @param int $width The width to make the name of the option.

+ 72 - 7
src/Console/ConsoleOptionParser.php

@@ -184,10 +184,11 @@ class ConsoleOptionParser {
  * }}}
  *
  * @param array $spec The spec to build the OptionParser with.
+ * @param bool $defaultOptions Whether you want the verbose and quiet options set.
  * @return ConsoleOptionParser
  */
-	public static function buildFromArray($spec) {
-		$parser = new ConsoleOptionParser($spec['command']);
+	public static function buildFromArray($spec, $defaultOptions = true) {
+		$parser = new ConsoleOptionParser($spec['command'], $defaultOptions);
 		if (!empty($spec['arguments'])) {
 			$parser->addArguments($spec['arguments']);
 		}
@@ -207,8 +208,53 @@ class ConsoleOptionParser {
 	}
 
 /**
+ * Returns an array representation of this parser.
+ *
+ * @return array
+ */
+	public function toArray() {
+		$result = [
+			'command' => $this->_command,
+			'arguments' => $this->_args,
+			'options' => $this->_options,
+			'subcommands' => $this->_subcommands,
+			'description' => $this->_description,
+			'epilog' => $this->_epilog
+		];
+		return $result;
+	}
+
+/**
  * Get or set the command name for shell/task.
  *
+ * @param array|\Cake\Console\ConsoleOptionParser $spec ConsoleOptionParser or spec to merge with.
+ * @return $this
+ */
+	public function merge($spec) {
+		if ($spec instanceof ConsoleOptionParser) {
+			$spec = $spec->toArray();
+		}
+		if (!empty($spec['arguments'])) {
+			$this->addArguments($spec['arguments']);
+		}
+		if (!empty($spec['options'])) {
+			$this->addOptions($spec['options']);
+		}
+		if (!empty($spec['subcommands'])) {
+			$this->addSubcommands($spec['subcommands']);
+		}
+		if (!empty($spec['description'])) {
+			$this->description($spec['description']);
+		}
+		if (!empty($spec['epilog'])) {
+			$this->epilog($spec['epilog']);
+		}
+		return $this;
+	}
+
+/**
+ * Gets or sets the command name for shell/task.
+ *
  * @param string|null $text The text to set, or null if you want to read
  * @return string|$this If reading, the value of the command. If setting $this will be returned.
  */
@@ -280,7 +326,7 @@ class ConsoleOptionParser {
  * @return $this
  */
 	public function addOption($name, $options = []) {
-		if (is_object($name) && $name instanceof ConsoleInputOption) {
+		if ($name instanceof ConsoleInputOption) {
 			$option = $name;
 			$name = $option->name();
 		} else {
@@ -332,7 +378,7 @@ class ConsoleOptionParser {
  * @return $this
  */
 	public function addArgument($name, $params = []) {
-		if (is_object($name) && $name instanceof ConsoleInputArgument) {
+		if ($name instanceof ConsoleInputArgument) {
 			$arg = $name;
 			$index = count($this->_args);
 		} else {
@@ -348,6 +394,11 @@ class ConsoleOptionParser {
 			unset($options['index']);
 			$arg = new ConsoleInputArgument($options);
 		}
+		foreach ($this->_args as $k => $a) {
+			if ($a->isEqualTo($arg)) {
+				return $this;
+			}
+		}
 		$this->_args[$index] = $arg;
 		ksort($this->_args);
 		return $this;
@@ -363,6 +414,10 @@ class ConsoleOptionParser {
  */
 	public function addArguments(array $args) {
 		foreach ($args as $name => $params) {
+			if ($params instanceof ConsoleInputArgument) {
+				$name = $params;
+				$params = [];
+			}
 			$this->addArgument($name, $params);
 		}
 		return $this;
@@ -378,6 +433,10 @@ class ConsoleOptionParser {
  */
 	public function addOptions(array $options) {
 		foreach ($options as $name => $params) {
+			if ($params instanceof ConsoleInputOption) {
+				$name = $params;
+				$params = [];
+			}
 			$this->addOption($name, $params);
 		}
 		return $this;
@@ -399,7 +458,7 @@ class ConsoleOptionParser {
  * @return $this
  */
 	public function addSubcommand($name, $options = []) {
-		if (is_object($name) && $name instanceof ConsoleInputSubcommand) {
+		if ($name instanceof ConsoleInputSubcommand) {
 			$command = $name;
 			$name = $command->name();
 		} else {
@@ -434,6 +493,10 @@ class ConsoleOptionParser {
  */
 	public function addSubcommands(array $commands) {
 		foreach ($commands as $name => $params) {
+			if ($params instanceof ConsoleInputSubcommand) {
+				$name = $params;
+				$params = [];
+			}
 			$this->addSubcommand($name, $params);
 		}
 		return $this;
@@ -538,10 +601,12 @@ class ConsoleOptionParser {
 			$subparser->command($this->command() . ' ' . $subparser->command());
 			return $subparser->help(null, $format, $width);
 		}
+
 		$formatter = new HelpFormatter($this);
-		if ($format === 'text' || $format === true) {
+		if ($format === 'text') {
 			return $formatter->text($width);
-		} elseif ($format === 'xml') {
+		}
+		if ($format === 'xml') {
 			return $formatter->xml();
 		}
 	}

+ 65 - 0
tests/TestCase/Console/ConsoleOptionParserTest.php

@@ -658,4 +658,69 @@ TEXT;
 		$this->assertEquals($expected, $result, 'Sub parser did not parse request.');
 	}
 
+/**
+ * Tests toArray()
+ *
+ * @return void
+ */
+	public function testToArray() {
+		$spec = array(
+			'command' => 'test',
+			'arguments' => array(
+				'name' => array('help' => 'The name'),
+				'other' => array('help' => 'The other arg')
+			),
+			'options' => array(
+				'name' => array('help' => 'The name'),
+				'other' => array('help' => 'The other arg')
+			),
+			'subcommands' => array(
+				'initdb' => array('help' => 'make database')
+			),
+			'description' => 'description text',
+			'epilog' => 'epilog text'
+		);
+		$parser = ConsoleOptionParser::buildFromArray($spec);
+		$result = $parser->toArray();
+
+		$this->assertEquals($spec['description'], $result['description']);
+		$this->assertEquals($spec['epilog'], $result['epilog']);
+
+		$options = $result['options'];
+		$this->assertTrue(isset($options['name']));
+		$this->assertTrue(isset($options['other']));
+
+		$this->assertEquals(2, count($result['arguments']));
+		$this->assertEquals(1, count($result['subcommands']));
+	}
+
+/**
+ * Tests merge()
+ *
+ * @return void
+ */
+	public function testMerge() {
+		$parser = new ConsoleOptionParser('test');
+		$parser->addOption('test', array('short' => 't', 'boolean' => true))
+			->addArgument('one', array('required' => true, 'choices' => array('a', 'b')))
+			->addArgument('two', array('required' => true));
+
+		$parserTwo = new ConsoleOptionParser('test');
+		$parserTwo->addOption('file', array('short' => 'f', 'boolean' => true))
+			->addOption('output', array('short' => 'o', 'boolean' => true))
+			->addArgument('one', array('required' => true, 'choices' => array('a', 'b')));
+
+		$parser->merge($parserTwo);
+		$result = $parser->toArray();
+
+		$options = $result['options'];
+		$this->assertTrue(isset($options['quiet']));
+		$this->assertTrue(isset($options['test']));
+		$this->assertTrue(isset($options['file']));
+		$this->assertTrue(isset($options['output']));
+
+		$this->assertEquals(2, count($result['arguments']));
+		$this->assertEquals(6, count($result['options']));
+	}
+
 }