Passed
Push — master ( 39145a...3500a5 )
by
unknown
15:58
created

CommandApplication::initializeContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Core\Console;
17
18
use Symfony\Component\Console\Application;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Exception\ExceptionInterface;
21
use Symfony\Component\Console\Input\ArgvInput;
22
use Symfony\Component\Console\Output\ConsoleOutput;
23
use TYPO3\CMS\Core\Authentication\CommandLineUserAuthentication;
24
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
25
use TYPO3\CMS\Core\Context\Context;
26
use TYPO3\CMS\Core\Context\DateTimeAspect;
27
use TYPO3\CMS\Core\Context\UserAspect;
28
use TYPO3\CMS\Core\Context\VisibilityAspect;
29
use TYPO3\CMS\Core\Context\WorkspaceAspect;
30
use TYPO3\CMS\Core\Core\ApplicationInterface;
31
use TYPO3\CMS\Core\Core\BootService;
32
use TYPO3\CMS\Core\Core\Bootstrap;
33
use TYPO3\CMS\Core\Core\Environment;
34
use TYPO3\CMS\Core\Information\Typo3Version;
35
use TYPO3\CMS\Core\Localization\LanguageService;
36
37
/**
38
 * Entry point for the TYPO3 Command Line for Commands
39
 * In addition to a simple Symfony Command, this also sets up a CLI user
40
 */
41
class CommandApplication implements ApplicationInterface
42
{
43
    protected Context $context;
44
45
    protected CommandRegistry $commandRegistry;
46
47
    protected ConfigurationManager $configurationManager;
48
49
    protected BootService $bootService;
50
51
    protected Application $application;
52
53
    public function __construct(
54
        Context $context,
55
        CommandRegistry $commandRegistry,
56
        ConfigurationManager $configurationMananger,
57
        BootService $bootService
58
    ) {
59
        $this->context = $context;
60
        $this->commandRegistry = $commandRegistry;
61
        $this->configurationManager = $configurationMananger;
62
        $this->bootService = $bootService;
63
64
        $this->checkEnvironmentOrDie();
65
        $this->application = new Application('TYPO3 CMS', sprintf(
66
            '%s (Application Context: <comment>%s</comment>)',
67
            (new Typo3Version())->getVersion(),
68
            Environment::getContext()
69
        ));
70
        $this->application->setAutoExit(false);
71
        $this->application->setCommandLoader($commandRegistry);
72
        // Replace default list command with TYPO3 override
73
        $this->application->add($commandRegistry->get('list'));
74
    }
75
76
    /**
77
     * Run the Symfony Console application in this TYPO3 application
78
     *
79
     * @param callable $execute
80
     */
81
    public function run(callable $execute = null)
82
    {
83
        $input = new ArgvInput();
84
        $output = new ConsoleOutput();
85
86
        $commandName = $this->getCommandName($input);
87
        if ($this->wantsFullBoot($commandName)) {
88
            // Do a full boot if command is not a low-level command
89
            $container = $this->bootService->getContainer();
90
            $this->application->setCommandLoader($container->get(CommandRegistry::class));
91
            $this->context = $container->get(Context::class);
92
93
            $isLowLevelCommandShortcut = false;
94
            try {
95
                $realName = $this->application->find($commandName)->getName();
96
                // Do not load ext_localconf if a low level command was found
97
                // due to using a shortcut
98
                $isLowLevelCommandShortcut = !$this->wantsFullBoot($realName);
0 ignored issues
show
Bug introduced by
It seems like $realName can also be of type null; however, parameter $commandName of TYPO3\CMS\Core\Console\C...cation::wantsFullBoot() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
                $isLowLevelCommandShortcut = !$this->wantsFullBoot(/** @scrutinizer ignore-type */ $realName);
Loading history...
99
            } catch (ExceptionInterface $e) {
100
                // Errors must be ignored, full binding/validation happens later when the console application runs.
101
            }
102
            if (!$isLowLevelCommandShortcut && $this->essentialConfigurationExists()) {
103
                $this->bootService->loadExtLocalconfDatabaseAndExtTables();
104
            }
105
        }
106
107
        $this->initializeContext();
108
        // create the BE_USER object (not logged in yet)
109
        Bootstrap::initializeBackendUser(CommandLineUserAuthentication::class);
110
        $GLOBALS['LANG'] = LanguageService::createFromUserPreferences($GLOBALS['BE_USER']);
111
        // Make sure output is not buffered, so command-line output and interaction can take place
112
        ob_clean();
113
114
        $exitCode = $this->application->run($input, $output);
115
116
        if ($execute !== null) {
117
            call_user_func($execute);
118
        }
119
120
        exit($exitCode);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
121
    }
122
123
    protected function wantsFullBoot(string $commandName): bool
124
    {
125
        if ($commandName === 'help') {
126
            return true;
127
        }
128
        return !$this->commandRegistry->has($commandName);
129
    }
130
131
    protected function getCommandName(ArgvInput $input): string
132
    {
133
        try {
134
            $input->bind($this->application->getDefinition());
135
        } catch (ExceptionInterface $e) {
136
            // Errors must be ignored, full binding/validation happens later when the console application runs.
137
        }
138
139
        return $input->getFirstArgument() ?? 'list';
140
    }
141
142
    /**
143
     * Check if LocalConfiguration.php and PackageStates.php exist
144
     *
145
     * @return bool TRUE when the essential configuration is available, otherwise FALSE
146
     */
147
    protected function essentialConfigurationExists(): bool
148
    {
149
        return file_exists($this->configurationManager->getLocalConfigurationFileLocation())
150
            && file_exists(Environment::getLegacyConfigPath() . '/PackageStates.php');
151
    }
152
153
    /**
154
     * Check the script is called from a cli environment.
155
     */
156
    protected function checkEnvironmentOrDie(): void
157
    {
158
        if (PHP_SAPI !== 'cli') {
159
            die('Not called from a command line interface (e.g. a shell or scheduler).' . LF);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
160
        }
161
    }
162
163
    /**
164
     * Initializes the Context used for accessing data and finding out the current state of the application
165
     */
166
    protected function initializeContext(): void
167
    {
168
        $this->context->setAspect('date', new DateTimeAspect(new \DateTimeImmutable('@' . $GLOBALS['EXEC_TIME'])));
169
        $this->context->setAspect('visibility', new VisibilityAspect(true, true));
170
        $this->context->setAspect('workspace', new WorkspaceAspect(0));
171
        $this->context->setAspect('backend.user', new UserAspect(null));
172
    }
173
}
174