Completed
Push — master ( 09df77...d7ded3 )
by Pol
02:26
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
eloc 1
dl 0
loc 3
ccs 0
cts 3
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
     * Create application.
138
     *
139
     * @return \Robo\Application
140
     */
141
    private function createApplication()
0 ignored issues
show
Unused Code introduced by
The method createApplication() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
142
    {
143
        $application = new Application(self::APPLICATION_NAME, 'UNKNOWN');
0 ignored issues
show
Bug introduced by
The constant phptaskman\core\Runner::APPLICATION_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
144
145
        $application
146
            ->getDefinition()
147
            ->addOption(
148
                new InputOption(
149
                    '--working-dir',
150
                    null,
151
                    InputOption::VALUE_REQUIRED,
152
                    'Working directory, defaults to current working directory.',
153
                    $this->workingDir
154
                )
155
            );
156
157
        return $application;
158
    }
159
160
    /**
161
     * @param string $command
162
     *
163
     * @throws \InvalidArgumentException
164
     *
165
     * @return array
166
     */
167
    private function getTasks($command)
168
    {
169
        $commands = $this->getConfig()->get('commands', []);
170
171
        if (!isset($commands[$command])) {
172
            throw new \InvalidArgumentException("Custom command '${command}' not defined.");
173
        }
174
175
        return !empty($commands[$command]['tasks']) ? $commands[$command]['tasks'] : $commands[$command];
176
    }
177
178
    /**
179
     * @param \Symfony\Component\Console\Input\InputInterface $input
180
     *
181
     * @return mixed
182
     */
183
    private function getWorkingDir(InputInterface $input)
184
    {
185
        return $input->getParameterOption('--working-dir', \getcwd());
186
    }
187
188
    /**
189
     * @param \Robo\Application $application
190
     */
191
    private function registerDynamicCommands(Application $application)
192
    {
193
        $customCommands = $this->getConfig()->get('commands', []);
194
        foreach ($customCommands as $name => $commandDefinition) {
195
            /** @var \Consolidation\AnnotatedCommand\AnnotatedCommandFactory $commandFactory */
196
            $commandFileName = YamlCommands::class . 'Commands';
197
            $commandClass = $this->container->get($commandFileName);
198
            $commandFactory = $this->container->get('commandFactory');
199
            $commandInfo = $commandFactory->createCommandInfo($commandClass, 'runTasks');
200
            $command = $commandFactory->createCommand($commandInfo, $commandClass)->setName($name);
201
202
            // Dynamic commands may define their own options.
203
            $this->addOptions($command, $commandDefinition);
204
205
            // Append also options of subsequent tasks.
206
            foreach ($this->getTasks($name) as $taskEntry) {
207
                if (!\is_array($taskEntry)) {
208
                    continue;
209
                }
210
211
                if (!isset($taskEntry['task'])) {
212
                    continue;
213
                }
214
215
                if ('run' !== $taskEntry['task']) {
216
                    continue;
217
                }
218
219
                if (empty($taskEntry['command'])) {
220
                    continue;
221
                }
222
223
                // This is a 'run' task.
224
                if (!empty($customCommands[$taskEntry['command']])) {
225
                    // Add the options of another custom command.
226
                    $this->addOptions($command, $customCommands[$taskEntry['command']]);
227
                } else {
228
                    // Add the options of an already registered command.
229
                    if ($this->application->has($taskEntry['command'])) {
230
                        $subCommand = $this->application->get($taskEntry['command']);
231
                        $command->addOptions($subCommand->getDefinition()->getOptions());
232
                    }
233
                }
234
            }
235
236
            $application->add($command);
237
        }
238
    }
239
}
240