Browse Source

Add HelpShell

This shell is a replacement for CommandListShell. It uses the provided
CommandCollection to list the commands instead of scanning the
application. This will allow it to play nicer with standalone
applications.

I've not yet fixed the hardcoded `cake` in the help output. But I'll be
doing that in my next pull request for the new console libraries.
Mark Story 8 years ago
parent
commit
fc1864eea8

+ 13 - 5
src/Console/CommandRunner.php

@@ -19,6 +19,7 @@ use Cake\Console\ConsoleIo;
 use Cake\Console\Exception\StopException;
 use Cake\Console\Shell;
 use Cake\Http\BaseApplication;
+use Cake\Shell\HelpShell;
 use Cake\Shell\VersionShell;
 use RuntimeException;
 
@@ -59,7 +60,9 @@ class CommandRunner
         $this->app = $app;
         $this->root = $root;
         $this->aliases = [
-            '--version' => 'version'
+            '--version' => 'version',
+            '--help' => 'help',
+            '-h' => 'help',
         ];
     }
 
@@ -100,6 +103,7 @@ class CommandRunner
 
         $commands = new CommandCollection([
             'version' => VersionShell::class,
+            'help' => HelpShell::class,
         ]);
         $commands = $this->app->console($commands);
         if (!($commands instanceof CommandCollection)) {
@@ -157,11 +161,15 @@ class CommandRunner
                 " Run `{$this->root} --help` to get the list of valid commands."
             );
         }
-        $classOrInstance = $commands->get($name);
-        if (is_string($classOrInstance)) {
-            return new $classOrInstance($io);
+        $instance = $commands->get($name);
+        if (is_string($instance)) {
+            $instance = new $instance($io);
+        }
+        // TODO Should this be an interface?
+        if (method_exists($instance, 'setCommandCollection')) {
+            $instance->setCommandCollection($commands);
         }
 
-        return $classOrInstance;
+        return $instance;
     }
 }

+ 2 - 1
src/Shell/CommandListShell.php

@@ -25,6 +25,7 @@ use SimpleXmlElement;
  * Shows a list of commands available from the console.
  *
  * @property \Cake\Shell\Task\CommandTask $Command
+ * @deprecated 3.5.0 Replaced by Cake\Shell\HelpShell
  */
 class CommandListShell extends Shell
 {
@@ -145,7 +146,7 @@ class CommandListShell extends Shell
             'help' => 'Get the listing as XML.',
             'boolean' => true
         ])->addOption('version', [
-            'help' => 'Prints the currently installed version of CakePHP.',
+            'help' => 'Prints the currently installed version of CakePHP. (deprecated - use `cake --version` instead)',
             'boolean' => true
         ]);
 

+ 137 - 0
src/Shell/HelpShell.php

@@ -0,0 +1,137 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         3.5.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+
+namespace Cake\Shell;
+
+use Cake\Console\CommandCollection;
+use Cake\Console\ConsoleOutput;
+use Cake\Console\Shell;
+use SimpleXmlElement;
+
+/**
+ * Print out command list
+ */
+class HelpShell extends Shell
+{
+    protected $commands;
+
+    /**
+     * startup
+     *
+     * @return void
+     */
+    public function startup()
+    {
+        if (!$this->param('xml')) {
+            parent::startup();
+        }
+    }
+
+    /**
+     * Set the command collection being used.
+     *
+     * @param \Cake\Console\CommandCollection $commands The commands to use.
+     * @return void
+     */
+    public function setCommandCollection(CommandCollection $commands)
+    {
+        $this->commands = $commands;
+    }
+
+    /**
+     * Main function Prints out the list of shells.
+     *
+     * @return void
+     */
+    public function main()
+    {
+        if (!$this->param('xml')) {
+            $this->out('<info>Current Paths:</info>', 2);
+            $this->out('* app:  ' . APP_DIR);
+            $this->out('* root: ' . rtrim(ROOT, DIRECTORY_SEPARATOR));
+            $this->out('* core: ' . rtrim(CORE_PATH, DIRECTORY_SEPARATOR));
+            $this->out('');
+
+            $this->out('<info>Available Commands:</info>', 2);
+        }
+
+        if (!$this->commands) {
+            $this->err('Could not print command list, no CommandCollection was set using setCommandCollection()');
+            return;
+        }
+
+        if ($this->param('xml')) {
+            $this->asXml($this->commands);
+            return;
+        }
+        $this->asText($this->commands);
+    }
+
+    /**
+     * Output text.
+     *
+     * @param \Cake\Console\CommandCollection $commands The command collection to output.
+     * @return void
+     */
+    protected function asText($commands)
+    {
+        foreach ($commands as $name => $class) {
+            $this->out('- ' . $name);
+        }
+        $this->out('');
+
+        $this->out('To run a command, type <info>`cake shell_name [args|options]`</info>');
+        $this->out('To get help on a specific command, type <info>`cake shell_name --help`</info>', 2);
+    }
+
+    /**
+     * Output as XML
+     *
+     * @param \Cake\Console\CommandCollection $commands The command collection to output
+     * @return void
+     */
+    protected function asXml($commands)
+    {
+        $shells = new SimpleXmlElement('<shells></shells>');
+        foreach ($commands as $name => $class) {
+            $shell = $shells->addChild('shell');
+            $shell->addAttribute('name', $name);
+            $shell->addAttribute('call_as', $name);
+            $shell->addAttribute('provider', $class);
+            $shell->addAttribute('help', $name . ' -h');
+        }
+        $this->_io->setOutputAs(ConsoleOutput::RAW);
+        $this->out($shells->saveXML());
+    }
+
+    /**
+     * Gets the option parser instance and configures it.
+     *
+     * @return \Cake\Console\ConsoleOptionParser
+     */
+    public function getOptionParser()
+    {
+        $parser = parent::getOptionParser();
+
+        $parser->setDescription(
+            'Get the list of available shells for this application.'
+        )->addOption('xml', [
+            'help' => 'Get the listing as XML.',
+            'boolean' => true
+        ]);
+
+        return $parser;
+    }
+}

+ 25 - 2
tests/TestCase/Console/CommandRunnerTest.php

@@ -120,7 +120,19 @@ class CommandRunnerTest extends TestCase
      */
     public function testRunHelpLongOption()
     {
-        $this->markTestIncomplete();
+        $app = $this->getMockBuilder(BaseApplication::class)
+            ->setMethods(['middleware', 'bootstrap'])
+            ->setConstructorArgs([$this->config])
+            ->getMock();
+
+        $output = new ConsoleOutput();
+        $runner = new CommandRunner($app, 'cake');
+        $result = $runner->run(['cake', '--help'], $this->getMockIo($output));
+        $this->assertSame(0, $result);
+        $messages = implode("\n", $output->messages());
+        $this->assertContains('Current Paths', $messages);
+        $this->assertContains('- i18n', $messages);
+        $this->assertContains('Available Commands', $messages);
     }
 
     /**
@@ -130,7 +142,18 @@ class CommandRunnerTest extends TestCase
      */
     public function testRunHelpShortOption()
     {
-        $this->markTestIncomplete();
+        $app = $this->getMockBuilder(BaseApplication::class)
+            ->setMethods(['middleware', 'bootstrap'])
+            ->setConstructorArgs([$this->config])
+            ->getMock();
+
+        $output = new ConsoleOutput();
+        $runner = new CommandRunner($app, 'cake');
+        $result = $runner->run(['cake', '-h'], $this->getMockIo($output));
+        $this->assertSame(0, $result);
+        $messages = implode("\n", $output->messages());
+        $this->assertContains('- i18n', $messages);
+        $this->assertContains('Available Commands', $messages);
     }
 
     /**

+ 102 - 0
tests/TestCase/Shell/HelpShellTest.php

@@ -0,0 +1,102 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         3.5.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\Shell;
+
+use Cake\Console\CommandCollection;
+use Cake\Console\ConsoleIo;
+use Cake\Core\Plugin;
+use Cake\Shell\HelpShell;
+use Cake\TestSuite\Stub\ConsoleOutput;
+use Cake\TestSuite\TestCase;
+
+/**
+ * HelpShell test.
+ */
+class HelpShellTest extends TestCase
+{
+    /**
+     * setup method
+     *
+     * @return void
+     */
+    public function setUp()
+    {
+        parent::setUp();
+        $this->setAppNamespace();
+        Plugin::load('TestPlugin');
+
+        $this->out = new ConsoleOutput();
+        $this->err = new ConsoleOutput();
+        $this->io = new ConsoleIo($this->out, $this->err);
+        $this->shell = new HelpShell($this->io);
+
+        $commands = new CommandCollection();
+        $commands->addMany($commands->autoDiscover());
+        $this->shell->setCommandCollection($commands);
+    }
+
+    /**
+     * Test the command listing
+     *
+     * @return void
+     */
+    public function testMainNoCommands()
+    {
+        $shell = new HelpShell($this->io);
+        $this->assertNull($shell->main());
+        $err = $this->err->messages();
+        $this->assertContains('Could not print command list', $err[0]);
+    }
+
+    /**
+     * Test the command listing
+     *
+     * @return void
+     */
+    public function testMain()
+    {
+        $this->shell->main();
+        $output = implode("\n", $this->out->messages());
+        $this->assertContains('- sample', $output, 'app shell');
+        $this->assertContains('- routes', $output, 'core shell');
+        $this->assertContains('- test_plugin.example', $output, 'Long plugin name');
+        $this->assertContains('- example', $output, 'Short plugin name');
+        $this->assertContains('To run a command', $output, 'more info present');
+        $this->assertContains('To get help', $output, 'more info present');
+    }
+
+    /**
+     * Test help --xml
+     *
+     * @return void
+     */
+    public function testMainAsXml()
+    {
+        $this->shell->params['xml'] = true;
+        $this->shell->main();
+        $output = implode("\n", $this->out->messages());
+
+        $this->assertContains('<shells>', $output);
+
+        $find = '<shell name="sample" call_as="sample" provider="TestApp\Shell\SampleShell" help="sample -h"';
+        $this->assertContains($find, $output);
+
+        $find = '<shell name="orm_cache" call_as="orm_cache" provider="Cake\Shell\OrmCacheShell" help="orm_cache -h"';
+        $this->assertContains($find, $output);
+
+        $find = '<shell name="test_plugin.sample" call_as="test_plugin.sample" provider="TestPlugin\Shell\SampleShell" help="test_plugin.sample -h"';
+        $this->assertContains($find, $output);
+    }
+}