Completed
Push — master ( 4eb9f3...c55fb1 )
by Pol
13:56
created

Runner::run()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 10
ccs 0
cts 5
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace phptaskman\core;
6
7
use Composer\Autoload\ClassLoader;
8
use Consolidation\AnnotatedCommand\AnnotatedCommand;
9
use phptaskman\core\Robo\Plugin\Commands\YamlCommands;
10
use League\Container\ContainerAwareTrait;
11
use Robo\Application;
12
use Robo\Common\ConfigAwareTrait;
13
use Symfony\Component\Console\Input\ArgvInput;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\ConsoleOutput;
17
use Symfony\Component\Console\Output\OutputInterface;
18
19
/**
20
 * Class Runner.
21
 */
22
final class Runner
23
{
24
    use ConfigAwareTrait;
25
    use ContainerAwareTrait;
26
27
    /**
28
     * @var Application
29
     */
30
    private $application;
31
32
    /**
33
     * @var \Composer\Autoload\ClassLoader
34
     */
35
    private $classLoader;
36
37
    /**
38
     * @var InputInterface
39
     */
40
    private $input;
41
42
    /**
43
     * @var OutputInterface
44
     */
45
    private $output;
46
47
    /**
48
     * @var \Robo\Runner
49
     */
50
    private $runner;
51
52
    /**
53
     * @var string
54
     */
55
    private $workingDir;
56
57
    /**
58
     * Runner constructor.
59
     *
60
     * @param InputInterface $input
61
     * @param OutputInterface $output
62
     * @param ClassLoader $classLoader
63
     */
64
    public function __construct(
65
        InputInterface $input = null,
66
        OutputInterface $output = null,
67
        ClassLoader $classLoader = null
68
    ) {
69
        $this->input = $input ?? new ArgvInput();
70
        $this->output = $output ?? new ConsoleOutput();
71
        $this->classLoader = $classLoader ?? new ClassLoader();
72
73
        $this->workingDir = $this->getWorkingDir($this->input);
74
        \chdir($this->workingDir);
75
76
        $this->config = Taskman::createConfiguration(
77
            $this->workingDir
78
        );
79
        $this->application = Taskman::createDefaultApplication(
80
            null,
81
            null,
82
            $this->workingDir
83
        );
84
        $this->container = Taskman::createContainer(
85
            $this->input,
86
            $this->output,
87
            $this->application,
88
            $this->config,
89
            $this->classLoader
90
        );
91
92
        $this->runner = Taskman::createDefaultRunner($this->container);
93
    }
94
95
    /**
96
     * @param mixed $args
97
     *
98
     * @return int
99
     */
100
    public function run($args)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

100
    public function run(/** @scrutinizer ignore-unused */ $args)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
101
    {
102
        // Register command classes.
103
        $this->runner->registerCommandClasses($this->application, [YamlCommands::class]);
104
105
        // Register commands defined in task.yml file.
106
        $this->registerDynamicCommands($this->application);
107
108
        // Run command.
109
        return $this->runner->run($this->input, $this->output, $this->application);
110
    }
111
112
    /**
113
     * @param \Consolidation\AnnotatedCommand\AnnotatedCommand $command
114
     * @param array $commandDefinition
115
     */
116
    private function addOptions(AnnotatedCommand $command, array $commandDefinition)
117
    {
118
        // This command doesn't define any option.
119
        if (empty($commandDefinition['options'])) {
120
            return;
121
        }
122
123
        $defaults = \array_fill_keys(['shortcut', 'mode', 'description', 'default'], null);
124
        foreach ($commandDefinition['options'] as $optionName => $optionDefinition) {
125
            $optionDefinition += $defaults;
126
            $command->addOption(
127
                '--' . $optionName,
128
                $optionDefinition['shortcut'],
129
                $optionDefinition['mode'],
130
                $optionDefinition['description'],
131
                $optionDefinition['default']
132
            );
133
        }
134
    }
135
136
    /**
137
     * @param string $command
138
     *
139
     * @throws \InvalidArgumentException
140
     *
141
     * @return array
142
     */
143
    private function getTasks($command)
144
    {
145
        $commands = $this->getConfig()->get('commands', []);
146
147
        if (!isset($commands[$command])) {
148
            throw new \InvalidArgumentException("Custom command '${command}' not defined.");
149
        }
150
151
        return !empty($commands[$command]['tasks']) ? $commands[$command]['tasks'] : $commands[$command];
152
    }
153
154
    /**
155
     * @param \Symfony\Component\Console\Input\InputInterface $input
156
     *
157
     * @return mixed
158
     */
159
    private function getWorkingDir(InputInterface $input)
160
    {
161
        return $input->getParameterOption('--working-dir', \getcwd());
162
    }
163
164
    /**
165
     * @param \Robo\Application $application
166
     */
167
    private function registerDynamicCommands(Application $application)
168
    {
169
        $customCommands = $this->getConfig()->get('commands', []);
170
        foreach ($customCommands as $name => $commandDefinition) {
171
            /** @var \Consolidation\AnnotatedCommand\AnnotatedCommandFactory $commandFactory */
172
            $commandFileName = YamlCommands::class . 'Commands';
173
            $commandClass = $this->container->get($commandFileName);
174
            $commandFactory = $this->container->get('commandFactory');
175
            $commandInfo = $commandFactory->createCommandInfo($commandClass, 'runTasks');
176
            $command = $commandFactory->createCommand($commandInfo, $commandClass)->setName($name);
177
178
            // Dynamic commands may define their own options.
179
            $this->addOptions($command, $commandDefinition);
180
181
            // Append also options of subsequent tasks.
182
            foreach ($this->getTasks($name) as $taskEntry) {
183
                if (!\is_array($taskEntry)) {
184
                    continue;
185
                }
186
187
                if (!isset($taskEntry['task'])) {
188
                    continue;
189
                }
190
191
                if ('run' !== $taskEntry['task']) {
192
                    continue;
193
                }
194
195
                if (empty($taskEntry['command'])) {
196
                    continue;
197
                }
198
199
                // This is a 'run' task.
200
                if (!empty($customCommands[$taskEntry['command']])) {
201
                    // Add the options of another custom command.
202
                    $this->addOptions($command, $customCommands[$taskEntry['command']]);
203
                } else {
204
                    // Add the options of an already registered command.
205
                    if ($this->application->has($taskEntry['command'])) {
206
                        $subCommand = $this->application->get($taskEntry['command']);
207
                        $command->addOptions($subCommand->getDefinition()->getOptions());
208
                    }
209
                }
210
            }
211
212
            $application->add($command);
213
        }
214
    }
215
}
216