Passed
Pull Request — master (#806)
by Maxim
19:17
created

Console::getApplication()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4.0039

Importance

Changes 0
Metric Value
eloc 15
c 0
b 0
f 0
dl 0
loc 27
ccs 15
cts 16
cp 0.9375
rs 9.7666
cc 4
nc 5
nop 0
crap 4.0039
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Console;
6
7
use Psr\Container\ContainerInterface;
8
use Psr\EventDispatcher\EventDispatcherInterface;
9
use Spiral\Console\Config\ConsoleConfig;
10
use Spiral\Console\Exception\LocatorException;
11
use Spiral\Core\Container;
12
use Spiral\Core\ScopeInterface;
13
use Spiral\Events\EventDispatcherAwareInterface;
14
use Symfony\Component\Console\Application;
15
use Symfony\Component\Console\Exception\CommandNotFoundException;
16
use Symfony\Component\Console\Input\ArgvInput;
17
use Symfony\Component\Console\Input\ArrayInput;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Console\Input\ArrayInput was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Input\StreamableInputInterface;
20
use Symfony\Component\Console\Output\BufferedOutput;
21
use Symfony\Component\Console\Output\ConsoleOutput;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as SymfonyEventDispatcherInterface;
24
25
final class Console
26
{
27
    // Undefined response code for command (errors). See below.
28
    public const CODE_NONE = 102;
29
30
    private ?Application $application = null;
31
32 69
    public function __construct(
33
        private readonly ConsoleConfig $config,
34
        private readonly ?LocatorInterface $locator = null,
35
        private readonly ContainerInterface $container = new Container(),
36
        private readonly ScopeInterface $scope = new Container(),
37
        private readonly ?EventDispatcherInterface $dispatcher = null
38
    ) {
39
    }
40
41
    /**
42
     * Run console application.
43
     *
44
     * @throws \Throwable
45
     */
46 5
    public function start(InputInterface $input = new ArgvInput(), OutputInterface $output = new ConsoleOutput()): int
47
    {
48 5
        return $this->scope->runScope(
49 5
            [],
50 5
            fn () => $this->run(
51 5
                $input->getFirstArgument() ?? 'list',
52
                $input,
53
                $output
54 2
            )->getCode()
55
        );
56
    }
57
58
    /**
59
     * Run selected command.
60
     *
61
     * @throws \Throwable
62
     * @throws CommandNotFoundException
63
     */
64 68
    public function run(
65
        ?string $command,
66
        array|InputInterface $input = [],
67
        OutputInterface $output = new BufferedOutput()
68
    ): CommandOutput {
69 68
        $input = \is_array($input) ? new ArrayInput($input) : $input;
0 ignored issues
show
introduced by
The condition is_array($input) is always true.
Loading history...
70
71 68
        $this->configureIO($input, $output);
72
73 68
        if ($command !== null) {
74 67
            $input = new InputProxy($input, ['firstArgument' => $command]);
75
        }
76
77 68
        $code = $this->scope->runScope([
78
            InputInterface::class => $input,
79
            OutputInterface::class => $output,
80 68
        ], fn () => $this->getApplication()->doRun($input, $output));
81
82 65
        return new CommandOutput($code ?? self::CODE_NONE, $output);
83
    }
84
85
    /**
86
     * Get associated Symfony Console Application.
87
     *
88
     * @throws LocatorException
89
     */
90 68
    public function getApplication(): Application
91
    {
92 68
        if ($this->application !== null) {
93 29
            return $this->application;
94
        }
95
96 68
        $this->application = new Application($this->config->getName(), $this->config->getVersion());
97 68
        $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

97
        $this->application->/** @scrutinizer ignore-call */ 
98
                            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...
98 68
        $this->application->setAutoExit(false);
99 68
        if ($this->dispatcher instanceof SymfonyEventDispatcherInterface) {
100
            $this->application->setDispatcher($this->dispatcher);
101
        }
102
103 68
        if ($this->locator !== null) {
104 21
            $this->addCommands($this->locator->locateCommands());
105
        }
106
107
        // Register user defined commands
108 68
        $static = new StaticLocator(
109 68
            $this->config->getCommands(),
110 68
            $this->config->getInterceptors(),
111 68
            $this->container
112
        );
113
114 68
        $this->addCommands($static->locateCommands());
115
116 68
        return $this->application;
117
    }
118
119 68
    private function addCommands(iterable $commands): void
120
    {
121 68
        $interceptors = $this->config->getInterceptors();
122
123 68
        foreach ($commands as $command) {
124 68
            if ($command instanceof Command) {
125 50
                $command->setContainer($this->container);
126 50
                $command->setInterceptors($interceptors);
127
            }
128
129 68
            if ($this->dispatcher !== null && $command instanceof EventDispatcherAwareInterface) {
130 1
                $command->setEventDispatcher($this->dispatcher);
131
            }
132
133 68
            $this->application->add($command);
134
        }
135
    }
136
137
    /**
138
     * Extracted in order to manage command lifecycle.
139
     *
140
     * @see Application::configureIO()
141
     */
142 68
    private function configureIO(InputInterface $input, OutputInterface $output): void
143
    {
144 68
        if ($input->hasParameterOption(['--ansi'], true)) {
145
            $output->setDecorated(true);
146 68
        } elseif ($input->hasParameterOption(['--no-ansi'], true)) {
147
            $output->setDecorated(false);
148
        }
149
150 68
        if ($input->hasParameterOption(['--no-interaction', '-n'], true)) {
151
            $input->setInteractive(false);
152 68
        } elseif (\function_exists('posix_isatty')) {
153 68
            $inputStream = null;
154
155 68
            if ($input instanceof StreamableInputInterface) {
156 68
                $inputStream = $input->getStream();
157
            }
158
159 68
            if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
160
                $input->setInteractive(false);
161
            }
162
        }
163
164 68
        match ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
0 ignored issues
show
Unused Code introduced by
The assignment to $shellVerbosity is dead and can be removed.
Loading history...
165
            -1 => $output->setVerbosity(OutputInterface::VERBOSITY_QUIET),
166 60
            1 => $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE),
167
            2 => $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE),
168
            3 => $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG),
169 8
            default => $shellVerbosity = 0
170
        };
171
172 68
        if ($input->hasParameterOption(['--quiet', '-q'], true)) {
173
            $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
174
            $shellVerbosity = -1;
175
        } else {
176
            if (
177 68
                $input->hasParameterOption('-vvv', true)
178 68
                || $input->hasParameterOption('--verbose=3', true)
179 68
                || 3 === $input->getParameterOption('--verbose', false, true)
180
            ) {
181
                $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
182
                $shellVerbosity = 3;
183
            } elseif (
184 68
                $input->hasParameterOption('-vv', true)
185 68
                || $input->hasParameterOption('--verbose=2', true)
186 68
                || 2 === $input->getParameterOption('--verbose', false, true)
187
            ) {
188
                $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
189
                $shellVerbosity = 2;
190
            } elseif (
191 68
                $input->hasParameterOption('-v', true)
192 68
                || $input->hasParameterOption('--verbose=1', true)
193 68
                || $input->hasParameterOption('--verbose', true)
194 68
                || $input->getParameterOption('--verbose', false, true)
195
            ) {
196 1
                $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
197 1
                $shellVerbosity = 1;
198
            }
199
        }
200
201 68
        if (-1 === $shellVerbosity) {
202
            $input->setInteractive(false);
203
        }
204
205 68
        \putenv('SHELL_VERBOSITY=' . $shellVerbosity);
206 68
        $_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
207 68
        $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
208
    }
209
}
210