[FEATURE] Allow Extbase CLI commands to be available via new CLI API 86/51886/8
authorBenni Mack <benni@typo3.org>
Tue, 28 Feb 2017 07:51:15 +0000 (08:51 +0100)
committerFrank Nägler <frank.naegler@typo3.org>
Mon, 27 Mar 2017 12:37:25 +0000 (14:37 +0200)
This ensures to call extbase commands & command controllers automatically
via the new CLI binary.

Extbase now also overrides the help command by symfony to allow extbase
custom HelpCommandController to come into play when an Extbase command
was called.

This patch is critical for v8 so all core-internal CLI commands are available via
"typo3/sysext/core/bin/typo3", otherwise we'd need to explain every admin
that there are two CLI entrypoints for v8 which are relevant, because Extbase is
still solely using "typo3/cli_dispatch.phpsh" until now, while all other entry
points have been migrated already.

Resolves: #80452
Releases: master
Change-Id: I70e56c2cbef993cf16dfb212ba1a11add0017a01
Reviewed-on: https://review.typo3.org/51886
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Frank Nägler <frank.naegler@typo3.org>
Tested-by: Frank Nägler <frank.naegler@typo3.org>
typo3/sysext/core/Documentation/Changelog/master/Feature-80452-ExtbaseCLICommandsAvailableViaNewCLIAPI.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Command/CoreCommand.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Command/ExtbaseCommand.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Command/HelpCommand.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Mvc/Cli/RequestHandler.php
typo3/sysext/extbase/Configuration/Commands.php [new file with mode: 0644]

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-80452-ExtbaseCLICommandsAvailableViaNewCLIAPI.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-80452-ExtbaseCLICommandsAvailableViaNewCLIAPI.rst
new file mode 100644 (file)
index 0000000..9312119
--- /dev/null
@@ -0,0 +1,18 @@
+.. include:: ../../Includes.txt
+
+================================================================
+Feature: #80452 - Extbase CLI commands available via new CLI API
+================================================================
+
+See :issue:`80452`
+
+Description
+===========
+
+Any Extbase Command Controller can now be accessed via the new Symfony Console CLI entrypoint by
+simply calling `typo3/sysext/core/bin/typo3 controller:command`.
+
+Using the existing CLI entrypoint via `typo3/cli_dispatch.phpsh extbase controller:command` still
+works as expected.
+
+.. index:: CLI
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Classes/Command/CoreCommand.php b/typo3/sysext/extbase/Classes/Command/CoreCommand.php
new file mode 100644 (file)
index 0000000..15f1bff
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+namespace TYPO3\CMS\Extbase\Command;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Command\Command;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Core\Bootstrap;
+use TYPO3\CMS\Extbase\Mvc\Cli\CommandManager;
+use TYPO3\CMS\Extbase\Object\ObjectManager;
+
+/**
+ * Main call to register any Extbase command from Extbase command controllers
+ *
+ * Fetches all registered Extbase commands and adds them to the application as custom Extbase commands
+ */
+class CoreCommand extends Command
+{
+    /**
+     * @var Bootstrap
+     */
+    protected $extbaseBootstrap;
+
+    /**
+     * Configure the command, since this is a command
+     */
+    protected function configure()
+    {
+        $this->setHidden(true);
+    }
+
+    /**
+     * Sets the application instance for this command.
+     * This is done in setApplication() because configure() is called too early to do it in that place.
+     * The method 'setApplication()' is done right afterwards but has the application object to call.
+     * Then registers additional commands that act as wrappers to the actual Extbase commands.
+     *
+     * @param Application $application An Application instance
+     */
+    public function setApplication(Application $application = null)
+    {
+        parent::setApplication($application);
+
+        // Find any registered Extbase commands
+        $this->extbaseBootstrap = GeneralUtility::makeInstance(Bootstrap::class);
+        $this->extbaseBootstrap->initialize([]);
+
+        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
+
+        /** @var CommandManager $commandManager */
+        $commandManager = $objectManager->get(CommandManager::class);
+        $commands = $commandManager->getAvailableCommands();
+        foreach ($commands as $command) {
+            $commandName = $commandManager->getShortestIdentifierForCommand($command);
+            $fullCommandName = $command->getCommandIdentifier();
+            if ($fullCommandName === 'extbase:help:error' || $fullCommandName === 'extbase:help:helpstub') {
+                continue;
+            }
+            if ($commandName === 'help') {
+                $commandName = 'extbase:help';
+            }
+            $extbaseCommand = GeneralUtility::makeInstance(ExtbaseCommand::class, $fullCommandName);
+
+            if ($commandName !== $fullCommandName) {
+                $extbaseCommand->setAliases([$commandName]);
+            }
+
+            $extbaseCommand->setExtbaseCommand($command);
+            $this->getApplication()->add($extbaseCommand);
+        }
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Command/ExtbaseCommand.php b/typo3/sysext/extbase/Classes/Command/ExtbaseCommand.php
new file mode 100644 (file)
index 0000000..34c000c
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+namespace TYPO3\CMS\Extbase\Command;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Core\Bootstrap;
+
+/**
+ * Wrapper to wrap an Extbase command from a command controller into a Symfony Command
+ */
+class ExtbaseCommand extends Command
+{
+    /**
+     * Extbase's command
+     * @var \TYPO3\CMS\Extbase\Mvc\Cli\Command
+     */
+    protected $command;
+
+    /**
+     * Extbase has its own validation logic, so it is disabled in this place
+     */
+    protected function configure()
+    {
+        $this->ignoreValidationErrors();
+    }
+
+    /**
+     * Sets the extbase command to be used for fetching the description etc.
+     *
+     * @param \TYPO3\CMS\Extbase\Mvc\Cli\Command $command
+     */
+    public function setExtbaseCommand(\TYPO3\CMS\Extbase\Mvc\Cli\Command $command)
+    {
+        $this->command = $command;
+    }
+
+    /**
+     * Sets the application instance for this command.
+     * Also uses the setApplication call now, as $this->configure() is called
+     * too early
+     *
+     * @param Application $application An Application instance
+     */
+    public function setApplication(Application $application = null)
+    {
+        parent::setApplication($application);
+        $description = $this->command->getDescription();
+        $description = str_replace(LF, ' ', $description);
+        $this->setDescription($description);
+    }
+
+    /**
+     * Executes the command to find any Extbase command
+     *
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     * @return void
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        // ugly hack because extbase only knows "help" (hardcoded, but already defined by symfony)
+        // and "extbase:help:help"
+        if ($_SERVER['argv'][1] === 'extbase:help') {
+            $_SERVER['argv'][1] = 'extbase:help:help';
+        }
+        $bootstrap = GeneralUtility::makeInstance(Bootstrap::class);
+        $bootstrap->run('', []);
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Command/HelpCommand.php b/typo3/sysext/extbase/Classes/Command/HelpCommand.php
new file mode 100644 (file)
index 0000000..065eb77
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+namespace TYPO3\CMS\Extbase\Command;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Core\Bootstrap;
+
+/**
+ * Extends the help command of symfony to show the specific help for Extbase commands
+ */
+class HelpCommand extends \Symfony\Component\Console\Command\HelpCommand
+{
+    /**
+     * This needs to be re-set as the parent command has this property declared as "private" as well.
+     *
+     * @var Command
+     */
+    private $command;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function configure()
+    {
+        parent::configure();
+        $this->setAliases([]);
+    }
+
+    /**
+     * Sets the command.
+     *
+     * @param Command $command The command to set
+     */
+    public function setCommand(Command $command)
+    {
+        $this->command = $command;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        if (null === $this->command) {
+            $this->command = $this->getApplication()->find($input->getArgument('command_name'));
+        }
+
+        // Extbase help was explicitly called
+        if ($input->getArgument('command') === 'extbase:help' || $input->getArgument('command') === 'extbase:help:help') {
+            $_SERVER['argv'][1] = 'extbase:help:help';
+            $bootstrap = GeneralUtility::makeInstance(Bootstrap::class);
+            $bootstrap->run('', []);
+
+        // An extbase command was originally called, but is now required to show the help information
+        } elseif ($this->command instanceof ExtbaseCommand) {
+            // Ugly hack to modify 'argv' so the help command for a specific command is shown
+            $args = [$_SERVER['argv'][0], 'help'];
+            foreach ($_SERVER['argv'] as $k => $value) {
+                if ($k === 0 || $value === '--help' || $value === '-h') {
+                    continue;
+                }
+                $args[] = $value;
+            }
+            $_SERVER['argv'] = $args;
+
+            // run Extbase bootstrap
+            $bootstrap = GeneralUtility::makeInstance(Bootstrap::class);
+            $bootstrap->run('', []);
+        } else {
+            // Any other symfony command should just show up the regular info
+            parent::execute($input, $output);
+        }
+    }
+}
index f2d3b20..01f4f7f 100644 (file)
@@ -85,7 +85,12 @@ class RequestHandler implements \TYPO3\CMS\Extbase\Mvc\RequestHandlerInterface
         if ($callingScript !== $_SERVER['_']) {
             $callingScript = $_SERVER['_'] . ' ' . $callingScript;
         }
-        $request = $this->requestBuilder->build($commandLine, $callingScript . ' extbase');
+
+        // Add the "extbase" prefix for the cli_dispatch command line tool
+        if (strpos($callingScript, 'cli_dispatch') !== false) {
+            $callingScript .= ' extbase';
+        }
+        $request = $this->requestBuilder->build($commandLine, $callingScript);
         /** @var $response \TYPO3\CMS\Extbase\Mvc\Cli\Response */
         $response = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Cli\Response::class);
         $this->dispatcher->dispatch($request, $response);
diff --git a/typo3/sysext/extbase/Configuration/Commands.php b/typo3/sysext/extbase/Configuration/Commands.php
new file mode 100644 (file)
index 0000000..7bb11c5
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Commands to be executed by typo3, where the key of the array
+ * is the name of the command (to be called as the first argument after typo3).
+ * Required parameter is the "class" of the command which needs to be a subclass
+ * of Symfony/Console/Command.
+ */
+return [
+    'extbase' => [
+        'class' => \TYPO3\CMS\Extbase\Command\CoreCommand::class
+    ],
+    // Overriding Symfony Help command to use Extbase-specific output
+    '_extbase_help' => [
+        'class' => \TYPO3\CMS\Extbase\Command\HelpCommand::class
+    ]
+];