Passed
Pull Request — master (#656)
by Abdul Malik
11:30 queued 05:15
created

Console::start()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 7
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Console;
13
14
use Throwable;
15
use Psr\Container\ContainerInterface;
16
use Spiral\Console\Config\ConsoleConfig;
17
use Spiral\Console\Exception\LocatorException;
18
use Spiral\Core\Container;
19
use Spiral\Core\ContainerScope;
20
use Symfony\Component\Console\Application;
21
use Symfony\Component\Console\Exception\CommandNotFoundException;
22
use Symfony\Component\Console\Input\ArgvInput;
23
use Symfony\Component\Console\Input\ArrayInput;
24
use Symfony\Component\Console\Input\InputInterface;
25
use Symfony\Component\Console\Input\StreamableInputInterface;
26
use Symfony\Component\Console\Output\BufferedOutput;
27
use Symfony\Component\Console\Output\ConsoleOutput;
28
use Symfony\Component\Console\Output\OutputInterface;
29
30
final class Console
31
{
32
    // Undefined response code for command (errors). See below.
33
    public const CODE_NONE = 102;
34
35
    private ConsoleConfig $config;
36
37
    private ?LocatorInterface $locator;
38
39
    private ContainerInterface $container;
40
41
    private ?Application $application = null;
42
43 133
    public function __construct(
44
        ConsoleConfig $config,
45
        LocatorInterface $locator = null,
46
        ContainerInterface $container = null
47
    ) {
48 133
        $this->config = $config;
49 133
        $this->locator = $locator;
50 133
        $this->container = $container ?? new Container();
51
    }
52
53
    /**
54
     * Run console application.
55
     *
56
     * @param InputInterface|null  $input
57
     * @param OutputInterface|null $output
58
     *
59
     * @throws Throwable
60
     */
61 6
    public function start(InputInterface $input = null, OutputInterface $output = null): int
62
    {
63 6
        $input ??= new ArgvInput();
64 6
        $output ??= new ConsoleOutput();
65
66 6
        return ContainerScope::runScope($this->container, fn () => $this->run(
67 6
            $input->getFirstArgument() ?? 'list',
68
            $input,
69
            $output
70 6
        )->getCode());
71
    }
72
73
    /**
74
     * Run selected command.
75
     *
76
     * @param string               $command
77
     * @param InputInterface|array $input
78
     * @param OutputInterface|null $output
79
     *
80
     * @throws Throwable
81
     * @throws CommandNotFoundException
82
     */
83 133
    public function run(
84
        ?string $command,
85
        $input = [],
86
        OutputInterface $output = null
87
    ): CommandOutput {
88 133
        $input = is_array($input) ? new ArrayInput($input) : $input;
89 133
        $output ??= new BufferedOutput();
90
91 133
        $this->configureIO($input, $output);
92
93 133
        if ($command !== null) {
94 132
            $input = new InputProxy($input, ['firstArgument' => $command]);
95
        }
96
97 133
        $code = ContainerScope::runScope($this->container, fn () => $this->getApplication()->doRun($input, $output));
98
99 124
        return new CommandOutput($code ?? self::CODE_NONE, $output);
100
    }
101
102
    /**
103
     * Get associated Symfony Console Application.
104
     *
105
     *
106
     * @throws LocatorException
107
     */
108 133
    public function getApplication(): Application
109
    {
110 133
        if ($this->application !== null) {
111 36
            return $this->application;
112
        }
113
114 133
        $this->application = new Application($this->config->getName(), $this->config->getVersion());
115 133
        $this->application->setCatchExceptions(false);
0 ignored issues
show
Bug introduced by
The method setCatchExceptions() does not exist on null. ( Ignorable by Annotation )

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

115
        $this->application->/** @scrutinizer ignore-call */ 
116
                            setCatchExceptions(false);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
116 133
        $this->application->setAutoExit(false);
117
118 133
        if ($this->locator !== null) {
119 90
            $this->addCommands($this->locator->locateCommands());
120
        }
121
122
        // Register user defined commands
123 133
        $static = new StaticLocator($this->config->getCommands(), $this->container);
124 133
        $this->addCommands($static->locateCommands());
125
126 133
        return $this->application;
127
    }
128
129 133
    private function addCommands(iterable $commands): void
130
    {
131 133
        foreach ($commands as $command) {
132 133
            if ($command instanceof Command) {
133 125
                $command->setContainer($this->container);
134
            }
135
136 133
            $this->application->add($command);
137
        }
138
    }
139
140
    /**
141
     * Extracted in order to manage command lifecycle.
142
     *
143
     * @see Application::configureIO()
144
     */
145 133
    private function configureIO(InputInterface $input, OutputInterface $output): void
146
    {
147 133
        if (true === $input->hasParameterOption(['--ansi'], true)) {
148
            $output->setDecorated(true);
149 133
        } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
150
            $output->setDecorated(false);
151
        }
152
153 133
        if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
154
            $input->setInteractive(false);
155 133
        } elseif (\function_exists('posix_isatty')) {
156 133
            $inputStream = null;
157
158 133
            if ($input instanceof StreamableInputInterface) {
159 133
                $inputStream = $input->getStream();
160
            }
161
162 133
            if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
163
                $input->setInteractive(false);
164
            }
165
        }
166
167 133
        switch ($shellVerbosity = (int)getenv('SHELL_VERBOSITY')) {
168
            case -1:
169
                $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
170
                break;
171 133
            case 1:
172 63
                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
173 63
                break;
174 71
            case 2:
175
                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
176
                break;
177 71
            case 3:
178 64
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
179 64
                break;
180
            default:
181 7
                $shellVerbosity = 0;
182 7
                break;
183
        }
184
185
        if (
186 133
            true === $input->hasParameterOption(['--quiet', '-q'], true)
187
        ) {
188
            $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
189
            $shellVerbosity = -1;
190
        } else {
191
            if (
192 133
                $input->hasParameterOption('-vvv', true)
193 133
                || $input->hasParameterOption('--verbose=3', true)
194 133
                || 3 === $input->getParameterOption('--verbose', false, true)
195
            ) {
196 4
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
197 4
                $shellVerbosity = 3;
198
            } elseif (
199 133
                $input->hasParameterOption('-vv', true)
200 133
                || $input->hasParameterOption('--verbose=2', true)
201 133
                || 2 === $input->getParameterOption('--verbose', false, true)
202
            ) {
203
                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
204
                $shellVerbosity = 2;
205
            } elseif (
206 133
                $input->hasParameterOption('-v', true)
207 133
                || $input->hasParameterOption('--verbose=1', true)
208 133
                || $input->hasParameterOption('--verbose', true)
209 133
                || $input->getParameterOption('--verbose', false, true)
210
            ) {
211 1
                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
212 1
                $shellVerbosity = 1;
213
            }
214
        }
215
216 133
        if (-1 === $shellVerbosity) {
217
            $input->setInteractive(false);
218
        }
219
220 133
        putenv('SHELL_VERBOSITY=' . $shellVerbosity);
221 133
        $_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
222 133
        $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
223
    }
224
}
225