Browse Source

Add CommandInterface and reference it.

The Command base class includes a bunch of integrations with the rest of
CakePHP that make it burdensome to use. By adding a CommandInterface we
can narrow the dependencies for standalone usage.

Applications that use the console standalone library would need to setup
their own run() method that uses the optionparser if they want.
Mark Story 6 years ago
parent
commit
df95a3bacf

+ 6 - 32
src/Console/Command.php

@@ -28,27 +28,13 @@ use RuntimeException;
 /**
  * Base class for console commands.
  */
-class Command
+class Command implements CommandInterface
 {
     use LocatorAwareTrait;
     use LogTrait;
     use ModelAwareTrait;
 
     /**
-     * Default error code
-     *
-     * @var int
-     */
-    public const CODE_ERROR = 1;
-
-    /**
-     * Default success code
-     *
-     * @var int
-     */
-    public const CODE_SUCCESS = 0;
-
-    /**
      * The name of this command.
      *
      * @var string
@@ -73,15 +59,7 @@ class Command
     }
 
     /**
-     * Set the name this command uses in the collection.
-     *
-     * Generally invoked by the CommandCollection when the command is added.
-     * Required to have at least one space in the name so that the root
-     * command can be calculated.
-     *
-     * @param string $name The name the command uses in the collection.
-     * @return $this
-     * @throws \InvalidArgumentException
+     * {@inheritDoc}
      */
     public function setName(string $name)
     {
@@ -185,11 +163,7 @@ class Command
     }
 
     /**
-     * Run the command.
-     *
-     * @param array $argv Arguments from the CLI environment.
-     * @param \Cake\Console\ConsoleIo $io The console io
-     * @return int|null Exit code or null for success.
+     * {@inheritDoc}
      */
     public function run(array $argv, ConsoleIo $io): ?int
     {
@@ -285,7 +259,7 @@ class Command
     /**
      * Execute another command with the provided set of arguments.
      *
-     * @param string|\Cake\Console\Command $command The command class name or command instance.
+     * @param string|\Cake\Console\CommandInterface $command The command class name or command instance.
      * @param array $args The arguments to invoke the command with.
      * @param \Cake\Console\ConsoleIo $io The ConsoleIo instance to use for the executed command.
      * @return int|null The exit code or null for success of the command.
@@ -298,10 +272,10 @@ class Command
             }
             $command = new $command();
         }
-        if (!$command instanceof Command) {
+        if (!$command instanceof CommandInterface) {
             $commandType = getTypeName($command);
             throw new InvalidArgumentException(
-                "Command '{$commandType}' is not a subclass of Cake\Console\Command."
+                "Command '{$commandType}' is not a subclass of Cake\Console\CommandInterface."
             );
         }
         $io = $io ?: new ConsoleIo();

+ 2 - 2
src/Console/CommandFactory.php

@@ -30,9 +30,9 @@ class CommandFactory implements CommandFactoryInterface
     public function create(string $className)
     {
         $command = new $className();
-        if (!($command instanceof Command) && !($command instanceof Shell)) {
+        if (!($command instanceof CommandInterface) && !($command instanceof Shell)) {
             /** @psalm-suppress DeprecatedClass */
-            $valid = implode('` or `', [Shell::class, Command::class]);
+            $valid = implode('` or `', [Shell::class, CommandInterface::class]);
             $message = sprintf('Class `%s` must be an instance of `%s`.', $className, $valid);
             throw new InvalidArgumentException($message);
         }

+ 1 - 1
src/Console/CommandFactoryInterface.php

@@ -23,7 +23,7 @@ interface CommandFactoryInterface
      * The factory method for creating Command and Shell instances.
      *
      * @param string $className Command/Shell class name.
-     * @return \Cake\Console\Shell|\Cake\Console\Command
+     * @return \Cake\Console\Shell|\Cake\Console\CommandInterface
      */
     public function create(string $className);
 }

+ 60 - 0
src/Console/CommandInterface.php

@@ -0,0 +1,60 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * 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         4.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Console;
+
+/**
+ * Describe the interface between a command
+ * and the surrounding console libraries.
+ */
+interface CommandInterface
+{
+    /**
+     * Default error code
+     *
+     * @var int
+     */
+    public const CODE_ERROR = 1;
+
+    /**
+     * Default success code
+     *
+     * @var int
+     */
+    public const CODE_SUCCESS = 0;
+
+    /**
+     * Set the name this command uses in the collection.
+     *
+     * Generally invoked by the CommandCollection when the command is added.
+     * Required to have at least one space in the name so that the root
+     * command can be calculated.
+     *
+     * @param string $name The name the command uses in the collection.
+     * @return $this
+     * @throws \InvalidArgumentException
+     */
+    public function setName(string $name);
+
+    /**
+     * Run the command.
+     *
+     * @param array $argv Arguments from the CLI environment.
+     * @param \Cake\Console\ConsoleIo $io The console io
+     * @return int|null Exit code or null for success.
+     */
+    public function run(array $argv, ConsoleIo $io): ?int;
+}

+ 6 - 6
src/Console/CommandRunner.php

@@ -165,7 +165,7 @@ class CommandRunner implements EventDispatcherInterface
         if ($shell instanceof Shell) {
             $result = $this->runShell($shell, $argv);
         }
-        if ($shell instanceof Command) {
+        if ($shell instanceof CommandInterface) {
             $result = $this->runCommand($shell, $argv, $io);
         }
 
@@ -237,7 +237,7 @@ class CommandRunner implements EventDispatcherInterface
      * @param \Cake\Console\ConsoleIo $io The IO wrapper for the created shell class.
      * @param \Cake\Console\CommandCollection $commands The command collection to find the shell in.
      * @param string $name The command name to find
-     * @return \Cake\Console\Shell|\Cake\Console\Command
+     * @return \Cake\Console\Shell|\Cake\Console\CommandInterface
      */
     protected function getShell(ConsoleIo $io, CommandCollection $commands, string $name)
     {
@@ -248,7 +248,7 @@ class CommandRunner implements EventDispatcherInterface
         if ($instance instanceof Shell) {
             $instance->setRootName($this->root);
         }
-        if ($instance instanceof Command) {
+        if ($instance instanceof CommandInterface) {
             $instance->setName("{$this->root} {$name}");
         }
         if ($instance instanceof CommandCollectionAwareInterface) {
@@ -323,12 +323,12 @@ class CommandRunner implements EventDispatcherInterface
     /**
      * Execute a Command class.
      *
-     * @param \Cake\Console\Command $command The command to run.
+     * @param \Cake\Console\CommandInterface $command The command to run.
      * @param array $argv The CLI arguments to invoke.
      * @param \Cake\Console\ConsoleIo $io The console io
      * @return int|null Exit code
      */
-    protected function runCommand(Command $command, array $argv, ConsoleIo $io): ?int
+    protected function runCommand(CommandInterface $command, array $argv, ConsoleIo $io): ?int
     {
         try {
             return $command->run($argv, $io);
@@ -362,7 +362,7 @@ class CommandRunner implements EventDispatcherInterface
      *
      * @param string $className Shell class name.
      * @param \Cake\Console\ConsoleIo $io The IO wrapper for the created shell class.
-     * @return \Cake\Console\Shell|\Cake\Console\Command
+     * @return \Cake\Console\Shell|\Cake\Console\CommandInterface
      */
     protected function createShell(string $className, ConsoleIo $io)
     {

+ 2 - 2
src/Console/CommandScanner.php

@@ -132,11 +132,11 @@ class CommandScanner
             /** @psalm-suppress DeprecatedClass */
             if (
                 !is_subclass_of($class, Shell::class)
-                && !is_subclass_of($class, Command::class)
+                && !is_subclass_of($class, CommandInterface::class)
             ) {
                 continue;
             }
-            if (is_subclass_of($class, Command::class)) {
+            if (is_subclass_of($class, CommandInterface::class)) {
                 /** @var \Cake\Console\Command $class */
                 $name = $class::defaultName();
             }

+ 42 - 0
src/Console/composer.json

@@ -0,0 +1,42 @@
+{
+    "name": "cakephp/console",
+    "description": "Build beautiful console applications with CakePHP",
+    "type": "library",
+    "keywords": [
+        "cakephp",
+        "console",
+        "cli",
+        "framework"
+    ],
+    "homepage": "https://cakephp.org",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "CakePHP Community",
+            "homepage": "https://github.com/cakephp/cache/graphs/contributors"
+        }
+    ],
+    "support": {
+        "issues": "https://github.com/cakephp/cakephp/issues",
+        "forum": "https://stackoverflow.com/tags/cakephp",
+        "irc": "irc://irc.freenode.org/cakephp",
+        "source": "https://github.com/cakephp/cache"
+    },
+    "require": {
+        "php": ">=7.2.0",
+        "cakephp/core": "^4.0",
+        "cakephp/event": "^4.0",
+        "cakephp/filesystem": "^4.0",
+        "cakephp/log": "^4.0",
+        "cakephp/utility": "^4.0"
+    },
+    "suggest": {
+        "cakephp/datasource": "To use the Shell or Command base classes",
+        "cakephp/orm": "To use the Shell or Command base classes"
+    },
+    "autoload": {
+        "psr-4": {
+            "Cake\\Console\\": "."
+        }
+    }
+}

+ 6 - 1
tests/TestCase/Console/CommandFactoryTest.php

@@ -15,6 +15,7 @@ declare(strict_types=1);
 namespace Cake\Test\TestCase\Console;
 
 use Cake\Console\CommandFactory;
+use Cake\Console\CommandInterface;
 use Cake\TestSuite\TestCase;
 use InvalidArgumentException;
 use TestApp\Command\DemoCommand;
@@ -28,6 +29,7 @@ class CommandFactoryTest extends TestCase
 
         $command = $factory->create(DemoCommand::class);
         $this->assertInstanceOf(DemoCommand::class, $command);
+        $this->assertInstanceOf(CommandInterface::class, $command);
     }
 
     public function testCreateShell()
@@ -43,7 +45,10 @@ class CommandFactoryTest extends TestCase
         $factory = new CommandFactory();
 
         $this->expectException(InvalidArgumentException::class);
-        $this->expectExceptionMessage('Class `Cake\Test\TestCase\Console\CommandFactoryTest` must be an instance of `Cake\Console\Shell` or `Cake\Console\Command`.');
+        $this->expectExceptionMessage(
+            'Class `Cake\Test\TestCase\Console\CommandFactoryTest` must be an instance of ' .
+            '`Cake\Console\Shell` or `Cake\Console\CommandInterface`.'
+        );
 
         $factory->create(static::class);
     }