Passed
Pull Request — master (#756)
by Maxim
07:24
created

Console::run()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 3
dl 0
loc 19
ccs 9
cts 9
cp 1
crap 3
rs 10
c 0
b 0
f 0
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 Psr\Container\ContainerInterface;
15
use Spiral\Console\Config\ConsoleConfig;
16
use Spiral\Console\Exception\LocatorException;
17
use Spiral\Core\Container;
18
use Spiral\Core\ContainerScope;
19
use Symfony\Component\Console\Application;
20
use Symfony\Component\Console\Exception\CommandNotFoundException;
21
use Symfony\Component\Console\Input\ArgvInput;
22
use Symfony\Component\Console\Input\ArrayInput;
23
use Symfony\Component\Console\Input\InputInterface;
24
use Symfony\Component\Console\Input\StreamableInputInterface;
25
use Symfony\Component\Console\Output\BufferedOutput;
26
use Symfony\Component\Console\Output\ConsoleOutput;
27
use Symfony\Component\Console\Output\OutputInterface;
28
29
final class Console
30
{
31
    // Undefined response code for command (errors). See below.
32
    public const CODE_NONE = 102;
33
34
    /** @var ConsoleConfig */
35
    private $config;
36
37
    /** @var LocatorInterface|null */
38
    private $locator;
39
40
    /** @var ContainerInterface */
41
    private $container;
42
43
    /** @var Application|null */
44
    private $application;
45
46 133
    public function __construct(
47
        ConsoleConfig $config,
48
        LocatorInterface $locator = null,
49
        ContainerInterface $container = null
50
    ) {
51 133
        $this->config = $config;
52 133
        $this->locator = $locator;
53 133
        $this->container = $container ?? new Container();
54
    }
55
56
    /**
57
     * Run console application.
58
     *
59
     * @param InputInterface|null  $input
60
     * @param OutputInterface|null $output
61
     *
62
     * @throws \Throwable
63
     */
64 6
    public function start(InputInterface $input = null, OutputInterface $output = null): int
65
    {
66 6
        $input = $input ?? new ArgvInput();
67 6
        $output = $output ?? new ConsoleOutput();
68
69 6
        return ContainerScope::runScope($this->container, function () use ($input, $output) {
70 6
            return $this->run(
71 6
                $input->getFirstArgument() ?? 'list',
72
                $input,
73
                $output
74 3
            )->getCode();
75
        });
76
    }
77
78
    /**
79
     * Run selected command.
80
     *
81
     * @param string               $command
82
     * @param InputInterface|array $input
83
     * @param OutputInterface|null $output
84
     *
85
     * @throws \Throwable
86
     * @throws CommandNotFoundException
87
     */
88 133
    public function run(
89
        ?string $command,
90
        $input = [],
91
        OutputInterface $output = null
92
    ): CommandOutput {
93 133
        $input = is_array($input) ? new ArrayInput($input) : $input;
94 133
        $output = $output ?? new BufferedOutput();
95
96 133
        $this->configureIO($input, $output);
97
98 133
        if ($command !== null) {
99 132
            $input = new InputProxy($input, ['firstArgument' => $command]);
100
        }
101
102 133
        $code = ContainerScope::runScope($this->container, function () use ($input, $output) {
103 133
            return $this->getApplication()->doRun($input, $output);
104
        });
105
106 124
        return new CommandOutput($code ?? self::CODE_NONE, $output);
107
    }
108
109
    /**
110
     * Get associated Symfony Console Application.
111
     *
112
     *
113
     * @throws LocatorException
114
     */
115 133
    public function getApplication(): Application
116
    {
117 133
        if ($this->application !== null) {
118 36
            return $this->application;
119
        }
120
121 133
        $this->application = new Application($this->config->getName(), $this->config->getVersion());
122 133
        $this->application->setCatchExceptions(false);
123 133
        $this->application->setAutoExit(false);
124
125 133
        if ($this->locator !== null) {
126 90
            $this->addCommands($this->locator->locateCommands());
127
        }
128
129
        // Register user defined commands
130 133
        $static = new StaticLocator($this->config->getCommands(), $this->container);
131 133
        $this->addCommands($static->locateCommands());
132
133 133
        return $this->application;
134
    }
135
136 133
    private function addCommands(iterable $commands): void
137
    {
138 133
        foreach ($commands as $command) {
139 133
            if ($command instanceof Command) {
140 125
                $command->setContainer($this->container);
141
            }
142
143 133
            $this->application->add($command);
0 ignored issues
show
Bug introduced by
The method add() 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

143
            $this->application->/** @scrutinizer ignore-call */ 
144
                                add($command);

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...
144
        }
145
    }
146
147
    /**
148
     * Extracted in order to manage command lifecycle.
149
     *
150
     * @see Application::configureIO()
151
     */
152 133
    private function configureIO(InputInterface $input, OutputInterface $output): void
153
    {
154 133
        if (true === $input->hasParameterOption(['--ansi'], true)) {
155
            $output->setDecorated(true);
156 133
        } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
157
            $output->setDecorated(false);
158
        }
159
160 133
        if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
161
            $input->setInteractive(false);
162 133
        } elseif (\function_exists('posix_isatty')) {
163 133
            $inputStream = null;
164
165 133
            if ($input instanceof StreamableInputInterface) {
166 133
                $inputStream = $input->getStream();
167
            }
168
169 133
            if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
170
                $input->setInteractive(false);
171
            }
172
        }
173
174 133
        switch ($shellVerbosity = (int)getenv('SHELL_VERBOSITY')) {
175
            case -1:
176
                $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
177
                break;
178 133
            case 1:
179 63
                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
180 63
                break;
181 71
            case 2:
182
                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
183
                break;
184 71
            case 3:
185 64
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
186 64
                break;
187
            default:
188 7
                $shellVerbosity = 0;
189 7
                break;
190
        }
191
192
        if (
193 133
            true === $input->hasParameterOption(['--quiet', '-q'], true)
194
        ) {
195
            $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
196
            $shellVerbosity = -1;
197
        } else {
198
            if (
199 133
                $input->hasParameterOption('-vvv', true)
200 133
                || $input->hasParameterOption('--verbose=3', true)
201 133
                || 3 === $input->getParameterOption('--verbose', false, true)
202
            ) {
203 4
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
204 4
                $shellVerbosity = 3;
205
            } elseif (
206 133
                $input->hasParameterOption('-vv', true)
207 133
                || $input->hasParameterOption('--verbose=2', true)
208 133
                || 2 === $input->getParameterOption('--verbose', false, true)
209
            ) {
210
                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
211
                $shellVerbosity = 2;
212
            } elseif (
213 133
                $input->hasParameterOption('-v', true)
214 133
                || $input->hasParameterOption('--verbose=1', true)
215 133
                || $input->hasParameterOption('--verbose', true)
216 133
                || $input->getParameterOption('--verbose', false, true)
217
            ) {
218 1
                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
219 1
                $shellVerbosity = 1;
220
            }
221
        }
222
223 133
        if (-1 === $shellVerbosity) {
224
            $input->setInteractive(false);
225
        }
226
227 133
        putenv('SHELL_VERBOSITY=' . $shellVerbosity);
228 133
        $_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
229 133
        $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
230
    }
231
}
232