Completed
Push — master ( 9eb452...a0cd38 )
by Kirill
04:51 queued 01:32
created

Application::getContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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
     * Application constructor.
50
     * @throws \InvalidArgumentException
51
     * @throws \Railt\Lexer\Exception\BadLexemeException
52
     */
53
    public function __construct()
54
    {
55
        parent::__construct(self::APP_NAME, self::APP_VERSION);
56
57
        $this->highlighter = $this->bootHighlight();
58
        $this->renderer = new ExceptionRenderer($this->highlighter);
59
    }
60
61
    /**
62
     * @param string $path
63
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
64
     */
65
    public function tryReadManifest(string $path): void
66
    {
67
        if (\is_file($path) && \is_readable($path)) {
68
            $this->readManifest($path);
69
        }
70
    }
71
72
    /**
73
     * @param string $path
74
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
75
     */
76
    public function readManifest(string $path): void
77
    {
78
        $manifest = \json_decode(\file_get_contents($path), true);
79
80
        try {
81
            foreach ($manifest['commands'] ?? [] as $command) {
82
                $this->add(new $command);
83
            }
84
        } catch (\Throwable $e) {
85
            $this->throw($e);
86
        }
87
    }
88
89
    /**
90
     * @return Highlighter
91
     * @throws \InvalidArgumentException
92
     * @throws \Railt\Lexer\Exception\BadLexemeException
93
     */
94
    private function bootHighlight(): Highlighter
95
    {
96
        $hl = new Highlighter();
97
98
        $hl->add(new PHPLanguage());
99
        $hl->add(new GraphQLLanguage());
100
101
        return $hl;
102
    }
103
104
    /**
105
     * @param LanguageInterface $language
106
     * @return Application
107
     */
108
    public function addLanguage(LanguageInterface $language): Application
109
    {
110
        $this->highlighter->add($language);
111
112
        return $this;
113
    }
114
115
    /**
116
     * @param InputInterface|null $input
117
     * @param OutputInterface|null $output
118
     * @throws \Throwable
119
     */
120
    public function gracefulRun(InputInterface $input = null, OutputInterface $output = null): void
121
    {
122
        [$input, $output] = $this->bootPipes($input, $output);
123
124
        try {
125
            $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...
126
        } catch (\Throwable $e) {
127
            $code = $this->exitCode($e);
128
            $this->throw($e);
129
        } finally {
130
            exit($code);
131
        }
132
    }
133
134
    /**
135
     * @param InputInterface|null $input
136
     * @param OutputInterface|null $output
137
     * @return array
138
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
139
     * @throws \Symfony\Component\Console\Exception\RuntimeException
140
     */
141
    private function bootPipes(InputInterface $input = null, OutputInterface $output = null): array
142
    {
143
        $input  = $input ?? new ArgvInput();
144
        $output = $output ?? new ConsoleOutput();
145
146
        $this->container->instance(InputInterface::class, $input);
0 ignored issues
show
Bug introduced by
The property container does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
147
        $this->container->instance(OutputInterface::class, $output);
148
149
        return [$input, $output];
150
    }
151
152
    /**
153
     * @param \Throwable $e
154
     * @return int
155
     */
156
    private function exitCode(\Throwable $e): int
157
    {
158
        return $e->getCode() ?: 1;
159
    }
160
161
    /**
162
     * @param Command $command
163
     * @return Command
164
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
165
     */
166
    public function add(Command $command): Command
167
    {
168
        try {
169
            parent::add($command);
170
        } catch (\Throwable $e) {
171
            $this->throw($e);
172
        }
173
174
        return $command;
175
    }
176
177
    /**
178
     * @param \Throwable $e
179
     * @param ConsoleOutput|null $output
180
     * @return void
181
     * @throws \Symfony\Component\Console\Exception\InvalidArgumentException
182
     */
183
    public function throw(\Throwable $e, ConsoleOutput $output = null): void
184
    {
185
        try {
186
            $result = $this->renderer->render($e);
187
        } catch (\Throwable $error) {
188
            $result = (string)new $error($error->getMessage(), $e->getCode(), $e);
189
        }
190
191
        ($output ?? new ConsoleOutput())->write($result);
192
193
        exit($this->exitCode($e));
194
    }
195
}
196