Completed
Push — master ( 62e188...0425f4 )
by Korvin
06:09
created

Console::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 3
crap 1
1
<?php
2
3
namespace Buttress\Concrete\Console;
4
5
use Buttress\Concrete\Console\Command\CacheCommand;
6
use Buttress\Concrete\CommandBus\Command\HandlerLocator;
7
use Buttress\Concrete\CommandBus\Provider\LegacyProvider;
8
use Buttress\Concrete\CommandBus\Provider\ModernProvider;
9
use Buttress\Concrete\Console\Command\Collection\Collection;
10
use Buttress\Concrete\Console\Command\HelpCommand;
11
use Buttress\Concrete\Console\Command\PackageCommand;
12
use Buttress\Concrete\Exception\BaseException;
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\CLImate;
19
use League\Tactician\Exception\MissingHandlerException;
20
use Psr\Container\ContainerInterface;
21
22
/**
23
 * The main entry point to the c5console project
24
 */
25
class Console
26
{
27
28
    /** @var \Psr\Container\ContainerInterface */
29
    public $container;
30
31
    /** @var \Buttress\Concrete\Console\Command\Collection */
32
    public $collection;
33
34
    /** @var string The last invoked filename */
35
    public $filename;
36
37
    /** @var string The last invoked command string */
38
    public $commandName;
39
40
    /** @var \Buttress\Concrete\Locator\Site */
41
    protected $site;
42
43
    /**
44
     * A list of the available console commands
45
     * @var string[]
46
     */
47
    protected $commands = [
48
        CacheCommand::class,
49
        HelpCommand::class,
50
        PackageCommand::class
51
    ];
52
53
    /**
54
     * A list of CommandBus Handler Providers
55
     * @var string[]
56
     */
57
    protected $providers = [
58
        ModernProvider::class,
59
        LegacyProvider::class
60
    ];
61
62 9
    public function __construct(
63
        ContainerInterface $container,
64
        Collection $collection,
65
        Site $site
66
    ) {
67 9
        $this->container = $container;
68 9
        $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...
69 9
        $this->site = $site;
70 9
    }
71
72 3
    public function run(array $arguments)
73
    {
74 3
        $this->filename = array_shift($arguments);
75 3
        $this->commandName = array_shift($arguments);
76
77 3
        $site = $this->site;
78 3
        $cli = $this->container->get(CLImate::class);
79
80 3
        $dispatcher = Dispatcher::simpleDispatcher(function (RouteCollector $routes) use ($site) {
81 3
            foreach ($this->collection->all() as $command) {
82 3
                $command->registerRoutes($routes, $site);
83 2
            }
84 3
        });
85
86 3
        return $this->dispatch($dispatcher, $cli);
87
    }
88
89
    /**
90
     * Prepare to run, this method is used for loading service providers and things like that.
91
     */
92 6
    public function prepare()
93
    {
94 6
        $this->registerCommands();
95 6
        $this->registerHandlers();
96 6
        return $this;
97
    }
98
99
    /**
100
     * @param $dispatcher
101
     * @return int
102
     */
103 3
    protected function dispatch(Dispatcher $dispatcher, CLImate $cli)
104
    {
105 3
        $command = $this->commandName ?: 'help';
106
107 3
        $result = $dispatcher->dispatch($command);
108 3
        switch ($result[0]) {
109 3
            case 0:
110 3
                $cli->error(sprintf('Command "%s" not found.', $command));
111 3
                return 1;
112 3
            case 1:
113 3
                list(, $callable, $data) = $result;
114
115 3
                return $this->runCallable($cli, $callable, $data);
116
                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...
117
            case 2:
118
                $cli->error(sprintf('Unexpected routing error.'));
119
                return 1;
120
        }
121
    }
122
123 6
    protected function registerCommands()
124
    {
125 6
        foreach ($this->commands as $command) {
126 3
            $this->collection->add($this->container->get($command));
127 4
        }
128 6
    }
129
130 6
    protected function registerHandlers()
131
    {
132 6
        $handler = $this->container->get(HandlerLocator::class);
133 6
        foreach ($this->providers as $provider) {
134 3
            $this->container->get($provider)->register($handler, $this->site);
135 4
        }
136 6
    }
137
138
    /**
139
     * Run a callable
140
     * @param \League\CLImate\CLImate $cli
141
     * @param callable $callable
142
     * @param array $data
143
     * @return int
144
     */
145 3
    private function runCallable(CLImate $cli, $callable, array $data)
146
    {
147
        try {
148 3
            if (is_string($callable) && strpos($callable, '::')) {
149
                list($class, $method) = explode('::', $callable, 2);
150
                $callable = [$this->container->get($class), $method];
151
            } else {
152 3
                $callable = $this->container->get($callable);
0 ignored issues
show
Documentation introduced by
$callable is of type callable, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
153
            }
154
155 3
            if (!is_callable($callable)) {
156 3
                throw new RuntimeException('Invalid route callable');
157
            }
158
159
            return $callable($this->site, ...$data);
160 3
        } catch (VersionMismatchException $e) {
161
            $cli->error('Invalid Version: ' . $e->getMessage())
162
                ->dim(sprintf('Detected version "%s"', $e->getVersion()));
163 3
        } catch (BaseException $e) {
164 3
            $cli->error('Runtime Error: ' . $e->getMessage());
165
        }
166
167 3
        return 1;
168
    }
169
}
170