Passed
Push — master ( 068bff...27cbbc )
by Kirill
03:24
created

Application::readManifest()   A

Complexity

Conditions 3
Paths 5

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 11
cp 0
rs 9.8666
c 0
b 0
f 0
cc 3
nc 5
nop 1
crap 12
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\Console;
11
12
use Railt\Console\Language\GraphQLLanguage;
13
use Railt\Console\Language\LanguageInterface;
14
use Railt\Console\Language\PHPLanguage;
15
use Railt\Container\ContainerInterface;
16
use Symfony\Component\Console\Application as Symfony;
17
use Symfony\Component\Console\Command\Command;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Railt\Console\Command.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
18
use Symfony\Component\Console\Input\ArgvInput;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Output\ConsoleOutput;
21
use Symfony\Component\Console\Output\OutputInterface;
22
23
/**
24
 * Class Application
25
 */
26
class Application extends Symfony
27
{
28
    /**
29
     * @var string
30
     */
31
    public const APP_NAME = 'Railt';
32
33
    /**
34
     * @var string
35
     */
36
    public const APP_VERSION = '1.2';
37
38
    /**
39
     * @var ExceptionRenderer
40
     */
41
    private $renderer;
42
43
    /**
44
     * @var Highlighter
45
     */
46
    private $highlighter;
47
48
    /**
49
     * @var ContainerInterface
50
     */
51
    private $container;
52
53
    /**
54
     * Application constructor.
55
     * @param ContainerInterface $container
56
     * @throws \InvalidArgumentException
57
     * @throws \Railt\Lexer\Exception\BadLexemeException
58
     */
59
    public function __construct(ContainerInterface $container)
60
    {
61
        parent::__construct(self::APP_NAME, self::APP_VERSION);
62
63
        $this->highlighter = $this->bootHighlight();
64
        $this->renderer = new ExceptionRenderer($this->highlighter);
65
        $this->container = $container;
66
    }
67
68
    /**
69
     * @param string $path
70
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
71
     */
72
    public function tryReadManifest(string $path): void
73
    {
74
        if (\is_file($path) && \is_readable($path)) {
75
            $this->readManifest($path);
76
        }
77
    }
78
79
    /**
80
     * @param string $path
81
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
82
     */
83
    public function readManifest(string $path): void
84
    {
85
        $manifest = \json_decode(\file_get_contents($path), true);
86
87
        try {
88
            foreach ($manifest['commands'] ?? [] as $command) {
89
                $this->add(new $command);
90
            }
91
        } catch (\Throwable $e) {
92
            $this->throw($e);
93
        }
94
    }
95
96
    /**
97
     * @return ContainerInterface
98
     */
99
    public function getContainer(): ContainerInterface
100
    {
101
        return $this->container;
102
    }
103
104
    /**
105
     * @return Highlighter
106
     * @throws \InvalidArgumentException
107
     * @throws \Railt\Lexer\Exception\BadLexemeException
108
     */
109
    private function bootHighlight(): Highlighter
110
    {
111
        $hl = new Highlighter();
112
113
        $hl->add(new PHPLanguage());
114
        $hl->add(new GraphQLLanguage());
115
116
        return $hl;
117
    }
118
119
    /**
120
     * @param LanguageInterface $language
121
     * @return Application
122
     */
123
    public function addLanguage(LanguageInterface $language): Application
124
    {
125
        $this->highlighter->add($language);
126
127
        return $this;
128
    }
129
130
    /**
131
     * @param InputInterface|null $input
132
     * @param OutputInterface|null $output
133
     * @throws \Throwable
134
     */
135
    public function gracefulRun(InputInterface $input = null, OutputInterface $output = null): void
136
    {
137
        [$input, $output] = $this->bootPipes($input, $output);
138
139
        try {
140
            $code = parent::run($input, $output);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (run() instead of gracefulRun()). Are you sure this is correct? If so, you might want to change this to $this->run().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
141
        } catch (\Throwable $e) {
142
            $code = $this->exitCode($e);
143
            $this->throw($e);
144
        } finally {
145
            exit($code);
146
        }
147
    }
148
149
    /**
150
     * @param InputInterface|null $input
151
     * @param OutputInterface|null $output
152
     * @return array
153
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
154
     * @throws \Symfony\Component\Console\Exception\RuntimeException
155
     */
156
    private function bootPipes(InputInterface $input = null, OutputInterface $output = null): array
157
    {
158
        $input  = $input ?? new ArgvInput();
159
        $output = $output ?? new ConsoleOutput();
160
161
        $this->container->instance(InputInterface::class, $input);
162
        $this->container->instance(OutputInterface::class, $output);
163
164
        return [$input, $output];
165
    }
166
167
    /**
168
     * @param \Throwable $e
169
     * @return int
170
     */
171
    private function exitCode(\Throwable $e): int
172
    {
173
        return $e->getCode() ?: 1;
174
    }
175
176
    /**
177
     * @param Command $command
178
     * @return Command
179
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
180
     */
181
    public function add(Command $command): Command
182
    {
183
        try {
184
            parent::add($command);
185
        } catch (\Throwable $e) {
186
            $this->throw($e);
187
        }
188
189
        return $command;
190
    }
191
192
    /**
193
     * @param \Throwable $e
194
     * @param ConsoleOutput|null $output
195
     * @return void
196
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
197
     */
198
    public function throw(\Throwable $e, ConsoleOutput $output = null): void
199
    {
200
        try {
201
            $result = $this->renderer->render($e);
202
        } catch (\Throwable $error) {
203
            $result = (string)new $error($error->getMessage(), $e->getCode(), $e);
204
        }
205
206
        ($output ?? new ConsoleOutput())->write($result);
207
208
        exit($this->exitCode($e));
209
    }
210
}
211