Completed
Push — master ( 0d723c...aa6b9d )
by Korvin
04:30
created

Console   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 171
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 7
dl 0
loc 171
ccs 0
cts 77
cp 0
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A run() 0 16 2
A prepare() 0 21 2
B dispatch() 0 19 5
A registerCommands() 0 6 2
A registerHandlers() 0 7 2
B runCallable() 0 26 6
A registerErrorHandler() 0 4 1
1
<?php
2
3
namespace Buttress\Concrete\Console;
4
5
use Buttress\Concrete\CommandBus\Provider\DefaultProvider;
6
use Buttress\Concrete\Console\Command\CacheCommand;
7
use Buttress\Concrete\CommandBus\Command\HandlerLocator;
8
use Buttress\Concrete\Console\Command\Collection\Collection;
9
use Buttress\Concrete\Console\Command\HelpCommand;
10
use Buttress\Concrete\Console\Command\PackageCommand;
11
use Buttress\Concrete\Exception\BaseException;
12
use Buttress\Concrete\Exception\ErrorHandler;
13
use Buttress\Concrete\Exception\RuntimeException;
14
use Buttress\Concrete\Exception\VersionMismatchException;
15
use Buttress\Concrete\Locator\Site;
16
use Buttress\Concrete\Route\Dispatcher;
17
use Buttress\Concrete\Route\RouteCollector;
18
use League\CLImate\Argument\Argument;
19
use League\CLImate\Argument\Manager;
20
use League\CLImate\CLImate;
21
use Psr\Container\ContainerInterface;
22
use Psr\Log\LoggerInterface;
23
use Whoops\Handler\PlainTextHandler;
24
use Whoops\Run;
25
26
/**
27
 * The main entry point to the c5console project
28
 */
29
class Console
30
{
31
32
    /** @var \Psr\Container\ContainerInterface */
33
    public $container;
34
35
    /** @var \Buttress\Concrete\Console\Command\Collection */
36
    public $collection;
37
38
    /** @var string The last invoked filename */
39
    public $filename;
40
41
    /** @var string The last invoked command string */
42
    public $commandName;
43
44
    /** @var \Buttress\Concrete\Locator\Site */
45
    protected $site;
46
47
    /** @var \Buttress\Concrete\Exception\ErrorHandler */
48
    protected $errorHandler;
49
50
    /**
51
     * A list of the available console commands
52
     * @var string[]
53
     */
54
    protected $commands = [
55
        CacheCommand::class,
56
        HelpCommand::class,
57
        PackageCommand::class
58
    ];
59
60
    /**
61
     * A list of CommandBus Handler Providers
62
     * @var string[]
63
     */
64
    protected $providers = [
65
        DefaultProvider::class
66
    ];
67
68
    public function __construct(
69
        ContainerInterface $container,
70
        Collection $collection,
71
        Site $site,
72
        ErrorHandler $e
73
    ) {
74
        $this->container = $container;
75
        $this->collection = $collection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $collection of type object<Buttress\Concrete...\Collection\Collection> is incompatible with the declared type object<Buttress\Concrete...ole\Command\Collection> of property $collection.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
76
        $this->site = $site;
77
        $this->errorHandler = $e;
78
    }
79
80
    public function run(array $arguments)
81
    {
82
        $this->filename = array_shift($arguments);
83
        $this->commandName = array_shift($arguments);
84
85
        $site = $this->site;
86
        $cli = $this->container->get(CLImate::class);
87
88
        $dispatcher = Dispatcher::simpleDispatcher(function (RouteCollector $routes) use ($site) {
89
            foreach ($this->collection->all() as $command) {
90
                $command->registerRoutes($routes, $site);
91
            }
92
        });
93
94
        return $this->dispatch($dispatcher, $cli);
95
    }
96
97
    /**
98
     * Prepare to run, this method is used for loading service providers and things like that.
99
     */
100
    public function prepare()
101
    {
102
        $this->registerCommands();
103
        $this->registerHandlers();
104
105
106
        $manager = new Manager();
107
        $manager->add('verbose', [
108
            'prefix' => 'v',
109
            'longPrefix' => 'verbose',
110
            'noValue' => true
111
        ]);
112
        $manager->parse();
113
114
        if ($manager->get('verbose')) {
115
            $this->errorHandler->setVerbose(true);
116
        }
117
118
        $this->registerErrorHandler();
119
        return $this;
120
    }
121
122
    /**
123
     * @param $dispatcher
124
     * @return int
125
     */
126
    protected function dispatch(Dispatcher $dispatcher, CLImate $cli)
127
    {
128
        $command = $this->commandName ?: 'help';
129
130
        $result = $dispatcher->dispatch($command);
131
        switch ($result[0]) {
132
            case 0:
133
                $cli->error(sprintf('Command "%s" not found.', $command));
134
                return 1;
135
            case 1:
136
                list(, $callable, $data) = $result;
137
138
                return $this->runCallable($cli, $callable, $data);
139
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
140
            case 2:
141
                $cli->error(sprintf('Unexpected routing error.'));
142
                return 1;
143
        }
144
    }
145
146
    protected function registerCommands()
147
    {
148
        foreach ($this->commands as $command) {
149
            $this->collection->add($this->container->get($command));
150
        }
151
    }
152
153
    protected function registerHandlers()
154
    {
155
        $handler = $this->container->get(HandlerLocator::class);
156
        foreach ($this->providers as $provider) {
157
            $this->container->get($provider)->register($handler, $this->site);
158
        }
159
    }
160
161
    /**
162
     * Run a callable
163
     * @param \League\CLImate\CLImate $cli
164
     * @param callable $callable
165
     * @param array $data
166
     * @return int
167
     */
168
    private function runCallable(CLImate $cli, $callable, array $data)
169
    {
170
        try {
171
            if (is_string($callable)) {
172
                if (strpos($callable, '::')) {
173
                    list($class, $method) = explode('::', $callable, 2);
174
                    $callable = [$this->container->get($class), $method];
175
                } else {
176
                    $callable = $this->container->get($callable);
177
                }
178
            }
179
180
            if (!is_callable($callable)) {
181
                throw new RuntimeException('Invalid route callable');
182
            }
183
184
            return $callable($this->site, ...$data);
185
        } catch (VersionMismatchException $e) {
186
            $cli->error('Invalid Version: ' . $e->getMessage())
187
                ->dim(sprintf('Detected version "%s"', $e->getVersion()));
188
        } catch (BaseException $e) {
189
            $cli->error('Runtime Error: ' . $e->getMessage());
190
        }
191
192
        return 1;
193
    }
194
195
    public function registerErrorHandler()
196
    {
197
        $this->errorHandler->register();
198
    }
199
}
200