[FEATURE] Use Symfony Console in CliRequestHandler 95/42295/4
authorBenjamin Mack <benni@typo3.org>
Wed, 5 Aug 2015 20:24:12 +0000 (22:24 +0200)
committerSusanne Moog <typo3@susannemoog.de>
Thu, 6 Aug 2015 17:26:16 +0000 (19:26 +0200)
The logic for outputting the default checks when calling cli_dispatch.phpsh is now using Symfony Console showing
colored error messages when entering wrong cliKeys.

The global variable $GLOBALS['MCONF']['name'] which was
mis-used to transfer information between the CLI RequestHandler
and the BackendUser (only when in CLI mode) for authentication
was removed, as all the logic is now available inside the CliRequestHandler and can be resolved there already.

The call "$BE_USER->checkCLIuser()" on every backend request
is now removed and the method itself is now marked for deprecation,
as the same functionality (except for using exceptions instead of fwrite() calls) is now in CliRequestHandler.

The two constants TYPO3_cliKey and TYPO3_cliInclude have
been marked internally as deprecated, as they are not in use
anymore.

Resolves: #68804
Releases: master
Change-Id: Ia7730d43dd970fd33daeb78e538c8d4deb741d93
Reviewed-on: http://review.typo3.org/42295
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Susanne Moog <typo3@susannemoog.de>
Tested-by: Susanne Moog <typo3@susannemoog.de>
typo3/sysext/backend/Classes/Console/CliRequestHandler.php
typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Documentation/Changelog/master/Deprecation-68804-CLI-relatedConstantsAndMethods.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-68804-ColoredOutputForCLI-relevantErrorMessages.rst [new file with mode: 0644]

index 6b61195..c325eba 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Console;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\ConsoleOutput;
 use TYPO3\CMS\Core\Core\Bootstrap;
 use TYPO3\CMS\Core\Console\RequestHandlerInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -23,6 +25,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  * Picks up requests only when coming from the CLI mode.
  * Resolves the "cliKey" which is registered inside $TYPO3_CONF_VARS[SC_OPTIONS][GLOBAL][cliKeys]
  * and includes the CLI-based script or exits if no valid "cliKey" is found.
+ * Also logs into the system as a backend user which needs to be added to the database called _CLI_mymodule
  */
 class CliRequestHandler implements RequestHandlerInterface {
 
@@ -44,38 +47,63 @@ class CliRequestHandler implements RequestHandlerInterface {
        /**
         * Handles any commandline request
         *
-        * @param \Symfony\Component\Console\Input\InputInterface $request
+        * @param InputInterface $request
         * @return void
         */
-       public function handleRequest(\Symfony\Component\Console\Input\InputInterface $request) {
-               $commandLineKey = $this->getCommandLineKeyOrDie();
-               $commandLineScript = $this->getIncludeScriptByCommandLineKey($commandLineKey);
-
-               $this->boot();
+       public function handleRequest(InputInterface $request) {
+               $output = new ConsoleOutput();
+               $exitCode = 0;
 
                try {
+                       $command = $this->validateCommandLineKeyFromInput($request);
+
+                       // try and look up if the CLI command user exists, throws an exception if the CLI
+                       // user cannot be found
+                       list($commandLineScript, $commandLineName) = $this->getIncludeScriptByCommandLineKey($command);
+                       $this->boot($commandLineName);
+
+                       // include the CLI script
                        include($commandLineScript);
+
+               } catch (\InvalidArgumentException $e) {
+                       $output->writeln('<error>Oops, an error occurred: ' . $e->getMessage() . '</error>');
+                       $output->writeln('');
+                       $output->writeln('Valid keys are:');
+                       $output->writeln('');
+                       $cliKeys = array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys']);
+                       asort($cliKeys);
+                       foreach ($cliKeys as $key => $value) {
+                               $output->writeln('  ' . $value);
+                       }
+                       $exitCode = $e->getCode();
+
+               } catch (\RuntimeException $e) {
+                       $output->writeln('<error>Oops, an error occurred: ' . $e->getMessage() . '</error>');
+                       $exitCode = $e->getCode();
+
                } catch (\Exception $e) {
-                       fwrite(STDERR, $e->getMessage() . LF);
-                       exit(99);
+                       $output->writeln('<error>Oops, an error occurred: ' . $e->getMessage() . '</error>');
+                       $exitCode = $e->getCode();
                }
+
+               exit($exitCode);
        }
 
        /**
         * Execute TYPO3 bootstrap
+        *
+        * @throws \RuntimeException when the _CLI_ user cannot be authenticated properly
         */
-       protected function boot() {
-               // Evaluate the constant for skipping the BE user check for the bootstrap
-               if (defined('TYPO3_PROCEED_IF_NO_USER') && TYPO3_PROCEED_IF_NO_USER) {
-                       $proceedIfNoUserIsLoggedIn = TRUE;
-               } else {
-                       $proceedIfNoUserIsLoggedIn = FALSE;
-               }
-
+       protected function boot($commandLineName) {
                $this->bootstrap
                        ->loadExtensionTables(TRUE)
-                       ->initializeBackendUser()
-                       ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
+                       ->initializeBackendUser();
+
+               // Checks for a user called starting with _CLI_ e.g. "_CLI_lowlevel"
+               $this->loadCommandLineBackendUser($commandLineName);
+
+               $this->bootstrap
+                       ->initializeBackendAuthentication()
                        ->initializeLanguageObject();
 
                // Make sure output is not buffered, so command-line output and interaction can take place
@@ -87,36 +115,23 @@ class CliRequestHandler implements RequestHandlerInterface {
         * First argument is a key that points to the script configuration.
         * If it is not set or not valid, the script exits with an error message.
         *
+        * @param InputInterface $input an instance of the input given to the CLI call
         * @return string the CLI key in use
+        * @throws \InvalidArgumentException
         */
-       protected function getCommandLineKeyOrDie() {
-               $cliKey = $_SERVER['argv'][1];
-               $errorMessage = '';
+       protected function validateCommandLineKeyFromInput(InputInterface $input) {
+               $cliKey = $input->getFirstArgument();
                if (empty($cliKey)) {
-                       $errorMessage = 'This script must have a \'cliKey\' as first argument.';
+                       throw new \InvalidArgumentException('This script must have a command as first argument.', 1);
                } elseif (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys'][$cliKey])) {
-                       $errorMessage = 'The supplied \'cliKey\' is not valid.';
+                       throw new \InvalidArgumentException('This supplied command is not valid.', 1);
                }
-
-               // exit with an error message
-               if (!empty($errorMessage)) {
-                       $errorMessage .= ' Valid keys are:
-
-';
-                       $cliKeys = array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys']);
-                       asort($cliKeys);
-                       foreach ($cliKeys as $key => $value) {
-                               $errorMessage .= '  ' . $value . LF;
-                       }
-                       fwrite(STDERR, $errorMessage . LF);
-                       die(1);
-               }
-
                return $cliKey;
        }
 
        /**
-        * Define cli-related parameters and return the include script.
+        * Define cli-related parameters and return the include script as well as the command line name. Used for
+        * authentication against the backend user in the "laodCommandLineBackendUser()" action.
         *
         * @param string $cliKey the CLI key
         * @return string the absolute path to the include script
@@ -124,24 +139,47 @@ class CliRequestHandler implements RequestHandlerInterface {
        protected function getIncludeScriptByCommandLineKey($cliKey) {
                list($commandLineScript, $commandLineName) = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys'][$cliKey];
                $commandLineScript = GeneralUtility::getFileAbsFileName($commandLineScript);
-               // Note: These constants are not in use anymore
+               // Note: These constants are not in use anymore, and marked for deprecation and will be removed in TYPO3 CMS 8
                define('TYPO3_cliKey', $cliKey);
                define('TYPO3_cliInclude', $commandLineScript);
-               $GLOBALS['MCONF']['name'] = $commandLineName;
                // This is a compatibility layer: Some cli scripts rely on this, like ext:phpunit cli
+               // This layer will be removed in TYPO3 CMS 8
                $GLOBALS['temp_cliScriptPath'] = array_shift($_SERVER['argv']);
                $GLOBALS['temp_cliKey'] = array_shift($_SERVER['argv']);
                array_unshift($_SERVER['argv'], $GLOBALS['temp_cliScriptPath']);
-               return $commandLineScript;
+               return array($commandLineScript, $commandLineName);
+       }
+
+       /**
+        * If the backend script is in CLI mode, it will try to load a backend user named by the CLI module name (in lowercase)
+        *
+        * @param string $commandLineName the name of the module registered inside $TYPO3_CONF_VARS[SC_OPTIONS][GLOBAL][cliKeys] as second parameter
+        * @throws \RuntimeException if a non-admin Backend user could not be loaded
+        */
+       protected function loadCommandLineBackendUser($commandLineName) {
+               if ($GLOBALS['BE_USER']->user['uid']) {
+                       throw new \RuntimeException('Another user was already loaded which is impossible in CLI mode!', 3);
+               }
+               if (!\TYPO3\CMS\Core\Utility\StringUtility::beginsWith($commandLineName, '_CLI_')) {
+                       throw new \RuntimeException('Module name, "' . $commandLineName . '", was not prefixed with "_CLI_"', 3);
+               }
+               $userName = strtolower($commandLineName);
+               $GLOBALS['BE_USER']->setBeUserByName($userName);
+               if (!$GLOBALS['BE_USER']->user['uid']) {
+                       throw new \RuntimeException('No backend user named "' . $userName . '" was found!', 3);
+               }
+               if ($GLOBALS['BE_USER']->isAdmin()) {
+                       throw new \RuntimeException('CLI backend user "' . $userName . '" was ADMIN which is not allowed!', 3);
+               }
        }
 
        /**
         * This request handler can handle any CLI request.
         *
-        * @param \Symfony\Component\Console\Input\InputInterface $request
+        * @param InputInterface $request
         * @return bool If the request is a CLI request, TRUE otherwise FALSE
         */
-       public function canHandleRequest(\Symfony\Component\Console\Input\InputInterface $request) {
+       public function canHandleRequest(InputInterface $request) {
                return defined('TYPO3_cliMode') && (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE) && (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI);
        }
 
index 168d7a2..5a54112 100644 (file)
@@ -2274,8 +2274,10 @@ This is a dump of the failures:
         * If the backend script is in CLI mode, it will try to load a backend user named by the CLI module name (in lowercase)
         *
         * @return bool Returns TRUE if a CLI user was loaded, otherwise FALSE!
+        * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Please implement this functionality in your own application directly from the outside
         */
        public function checkCLIuser() {
+               GeneralUtility::logDeprecatedFunction();
                // First, check if cliMode is enabled:
                if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
                        if (!$this->user['uid']) {
index 6e589a0..fb2ff47 100644 (file)
@@ -1056,7 +1056,6 @@ class Bootstrap {
         * @return \TYPO3\CMS\Core\Core\Bootstrap
         */
        public function initializeBackendAuthentication($proceedIfNoUserIsLoggedIn = FALSE) {
-               $GLOBALS['BE_USER']->checkCLIuser();
                $GLOBALS['BE_USER']->backendCheckLogin($proceedIfNoUserIsLoggedIn);
                return $this;
        }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-68804-CLI-relatedConstantsAndMethods.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-68804-CLI-relatedConstantsAndMethods.rst
new file mode 100644 (file)
index 0000000..ada17d3
--- /dev/null
@@ -0,0 +1,44 @@
+=======================================================
+Deprecation: #68804 - CLI-related constants and methods
+=======================================================
+
+Description
+===========
+
+Logic regarding regular CLI-based scripts with the CLIkey option has been moved
+into the CliRequestHandler.
+
+Therefore, the following method has been marked for deprecation:
+
+.. code-block:: php
+
+       BackendUserAuthentication->checkCLIuser()
+
+Additionally, the following constants and global parameters have been marked for deprecation in CLI context.
+
+               const TYPO3_cliKey
+               const TYPO3_cliInclude
+               $GLOBALS['MCONF']['name']
+               $GLOBALS['temp_cliScriptPath']
+               $GLOBALS['temp_cliKey']
+
+The method, constants and variables will be removed in TYPO3 CMS 8.
+
+
+Impact
+======
+
+Calling ``BackendUserAuthentication->checkCLIuser()`` directly will now throw a deprecation warning.
+
+
+Affected Installations
+======================
+
+Installations with custom entry points in a CLI environment that make use of the method, constants or variables above.
+
+
+Migration
+=========
+
+Use the native ``$_SERVER['argv']`` or the given Input object directly in your code to detect the
+current CLI-relevant data.
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-68804-ColoredOutputForCLI-relevantErrorMessages.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-68804-ColoredOutputForCLI-relevantErrorMessages.rst
new file mode 100644 (file)
index 0000000..7552aa6
--- /dev/null
@@ -0,0 +1,9 @@
+================================================================
+Feature: #68804 - Colored output for CLI-relevant error messages
+================================================================
+
+Description
+===========
+
+Calling ``typo3/cli_dispatch.phpsh`` via the command line will now show a colored error message when an invalid
+or no CLI key as first parameter is given.
\ No newline at end of file