Passed
Push — master ( 9e0b2c...e0b23c )
by Pol
03:34
created

Runner::getWorkingDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
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\Output\ConsoleOutput;
16
use Symfony\Component\Console\Output\OutputInterface;
17
18
/**
19
 * Class Runner.
20
 */
21
final class Runner
22
{
23
    use ConfigAwareTrait;
24
    use ContainerAwareTrait;
25
26
    /**
27
     * @var Application
28
     */
29
    private $application;
30
31
    /**
32
     * @var \Composer\Autoload\ClassLoader
33
     */
34
    private $classLoader;
35
36
    /**
37
     * @var InputInterface
38
     */
39
    private $input;
40
41
    /**
42
     * @var OutputInterface
43
     */
44
    private $output;
45
46
    /**
47
     * @var \Robo\Runner
48
     */
49
    private $runner;
50
51
    /**
52
     * @var string
53
     */
54
    private $workingDir;
55
56
    /**
57
     * Runner constructor.
58
     *
59
     * @param InputInterface $input
60
     * @param OutputInterface $output
61
     * @param ClassLoader $classLoader
62
     */
63
    public function __construct(
64
        InputInterface $input = null,
65
        OutputInterface $output = null,
66
        ClassLoader $classLoader = null
67
    ) {
68
        $this->input = $input ?? new ArgvInput();
69
        $this->output = $output ?? new ConsoleOutput();
70
        $this->classLoader = $classLoader ?? new ClassLoader();
71
72
        $this->workingDir = $this->getWorkingDir($this->input);
73
        \chdir($this->workingDir);
74
75
        $this->config = Taskman::createConfiguration(
76
            $this->workingDir
77
        );
78
        $this->application = Taskman::createDefaultApplication(
79
            null,
80
            null,
81
            $this->workingDir
82
        );
83
        $this->container = Taskman::createContainer(
84
            $this->input,
85
            $this->output,
86
            $this->application,
87
            $this->config,
88
            $this->classLoader
89
        );
90
91
        $this->runner = Taskman::createDefaultRunner($this->container);
92
    }
93
94
    /**
95
     * @param mixed $args
96
     *
97
     * @return int
98
     */
99
    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

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