Command::commands()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 10
rs 10
1
<?php namespace Tarsana\Command;
2
3
use Tarsana\Command\Commands\HelpCommand;
4
use Tarsana\Command\Commands\InteractiveCommand;
5
use Tarsana\Command\Commands\VersionCommand;
6
use Tarsana\Command\Config\ConfigLoader;
7
use Tarsana\Command\Console\Console;
8
use Tarsana\Command\Console\ExceptionPrinter;
9
use Tarsana\Command\Interfaces\Console\ConsoleInterface;
10
use Tarsana\Command\Interfaces\Template\TemplateLoaderInterface;
11
use Tarsana\Command\SubCommand;
12
use Tarsana\Command\Template\TemplateLoader;
13
use Tarsana\IO\Filesystem;
14
use Tarsana\IO\FilesystemInterface;
0 ignored issues
show
Bug introduced by
The type Tarsana\IO\FilesystemInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Tarsana\Syntax\Exceptions\ParseException;
16
use Tarsana\Syntax\Factory as S;
17
use Tarsana\Syntax\Text as T;
18
19
class Command {
20
21
    protected $name;
22
    protected $version;
23
    protected $description;
24
25
    protected $syntax;
26
    protected $descriptions;
27
28
    protected $options;
29
    protected $args;
30
31
    protected $action;
32
    protected $commands;
33
34
    protected $console;
35
    protected $fs;
36
    protected $templatesLoader;
37
    protected $config;
38
39
    public static function create(callable $action = null) {
40
        $command = new Command;
41
        if (null !== $action)
42
            $command->action($action);
43
        return $command;
44
    }
45
46
    public function __construct()
47
    {
48
        $this->commands([])
49
             ->name('Unknown')
50
             ->version('1.0.0')
51
             ->description('...')
52
             ->descriptions([])
53
             ->options([])
54
             ->console(new Console)
55
             ->fs(new Filesystem('.'))
56
             ->configPaths([])
57
             ->setupSubCommands()
58
             ->init();
59
    }
60
61
    /**
62
     * name getter and setter.
63
     *
64
     * @param  string
65
     * @return mixed
66
     */
67
    public function name(string $value = null)
68
    {
69
        if (null === $value) {
70
            return $this->name;
71
        }
72
        $this->name = $value;
73
        return $this;
74
    }
75
76
    /**
77
     * version getter and setter.
78
     *
79
     * @param  string
80
     * @return mixed
81
     */
82
    public function version(string $value = null)
83
    {
84
        if (null === $value) {
85
            return $this->version;
86
        }
87
        $this->version = $value;
88
        return $this;
89
    }
90
91
    /**
92
     * description getter and setter.
93
     *
94
     * @param  string
95
     * @return mixed
96
     */
97
    public function description(string $value = null)
98
    {
99
        if (null === $value) {
100
            return $this->description;
101
        }
102
        $this->description = $value;
103
        return $this;
104
    }
105
106
    /**
107
     * descriptions getter and setter.
108
     *
109
     * @param  string
110
     * @return mixed
111
     */
112
    public function descriptions(array $value = null)
113
    {
114
        if (null === $value) {
115
            return $this->descriptions;
116
        }
117
        $this->descriptions = $value;
118
        return $this;
119
    }
120
121
    /**
122
     * syntax getter and setter.
123
     *
124
     * @param  string|null $syntax
125
     * @return Syntax|self
0 ignored issues
show
Bug introduced by
The type Tarsana\Command\Syntax was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
126
     */
127
    public function syntax(string $syntax = null)
128
    {
129
        if (null === $syntax)
130
            return $this->syntax;
131
132
        $this->syntax = S::syntax()->parse("{{$syntax}| }");
133
        return $this;
134
    }
135
136
    /**
137
     * options getter and setter.
138
     *
139
     * @param  array
140
     * @return mixed
141
     */
142
    public function options(array $options = null)
143
    {
144
        if (null === $options) {
145
            return $this->options;
146
        }
147
148
        $this->options = [];
149
        foreach($options as $option)
150
            $this->options[$option] = false;
151
152
        return $this;
153
    }
154
155
    /**
156
     * option getter.
157
     *
158
     * @param  string
159
     * @return mixed
160
     */
161
    public function option(string $name)
162
    {
163
        if (!array_key_exists($name, $this->options))
164
            throw new \InvalidArgumentException("Unknown option '{$name}'");
165
        return $this->options[$name];
166
    }
167
168
    /**
169
     * args getter and setter.
170
     *
171
     * @param  stdClass
172
     * @return mixed
173
     */
174
    public function args(\stdClass $value = null)
175
    {
176
        if (null === $value) {
177
            return $this->args;
178
        }
179
        $this->args = $value;
180
        return $this;
181
    }
182
183
    /**
184
     * console getter and setter.
185
     *
186
     * @param  ConsoleInterface
187
     * @return mixed
188
     */
189
    public function console(ConsoleInterface $value = null)
190
    {
191
        if (null === $value) {
192
            return $this->console;
193
        }
194
        $this->console = $value;
195
        foreach ($this->commands as $name => $command) {
196
            $command->console = $value;
197
        }
198
        return $this;
199
    }
200
201
    /**
202
     * fs getter and setter.
203
     *
204
     * @param  Tarsana\IO\Filesystem|string
205
     * @return mixed
206
     */
207
    public function fs($value = null)
208
    {
209
        if (null === $value) {
210
            return $this->fs;
211
        }
212
        if (is_string($value))
213
            $value = new Filesystem($value);
214
        $this->fs = $value;
215
        foreach ($this->commands as $name => $command) {
216
            $command->fs = $value;
217
        }
218
        return $this;
219
    }
220
221
    /**
222
     * templatesLoader getter and setter.
223
     *
224
     * @param  Tarsana\Command\Interfaces\Template\TemplateLoaderInterface
225
     * @return mixed
226
     */
227
    public function templatesLoader(TemplateLoaderInterface $value = null)
228
    {
229
        if (null === $value) {
230
            return $this->templatesLoader;
231
        }
232
        $this->templatesLoader = $value;
233
        foreach ($this->commands as $name => $command) {
234
            $command->templatesLoader = $value;
235
        }
236
        return $this;
237
    }
238
239
    public function templatesPath(string $path, string $cachePath = null)
240
    {
241
        $this->templatesLoader = new TemplateLoader($path, $cachePath);
242
        foreach ($this->commands as $name => $command) {
243
            $command->templatesLoader = $this->templatesLoader();
244
        }
245
        return $this;
246
    }
247
248
    public function template(string $name)
249
    {
250
        if (null === $this->templatesLoader)
251
            throw new \Exception("Please initialize the templates loader before trying to load templates!");
252
        return $this->templatesLoader->load($name);
253
    }
254
255
    public function configPaths(array $paths)
256
    {
257
        $configLoader = new ConfigLoader($this->fs);
258
        $this->config = $configLoader->load($paths);
259
        foreach ($this->commands as $name => $command) {
260
            $command->config = $this->config;
261
        }
262
        return $this;
263
    }
264
265
    public function config(string $path = null)
266
    {
267
        return $this->config->get($path);
268
    }
269
270
    /**
271
     * action getter and setter.
272
     *
273
     * @param  callable
274
     * @return mixed
275
     */
276
    public function action(callable $value = null)
277
    {
278
        if (null === $value) {
279
            return $this->action;
280
        }
281
        $this->action = $value;
282
        return $this;
283
    }
284
285
    /**
286
     * commands getter and setter.
287
     *
288
     * @param  array
289
     * @return mixed
290
     */
291
    public function commands(array $value = null)
292
    {
293
        if (null === $value) {
294
            return $this->commands;
295
        }
296
        $this->commands = [];
297
        foreach ($value as $name => $command) {
298
            $this->command($name, $command);
299
        }
300
        return $this;
301
    }
302
303
    public function command(string $name, Command $command = null)
304
    {
305
        if (null === $command) {
306
            if (!array_key_exists($name, $this->commands))
307
                throw new \InvalidArgumentException("subcommand '{$name}' not found!");
308
            return $this->commands[$name];
309
        }
310
        $this->commands[$name] = $command;
311
        return $this;
312
    }
313
314
    public function hasCommand(string $name) : bool
315
    {
316
        return array_key_exists($name, $this->commands);
317
    }
318
319
    protected function setupSubCommands()
320
    {
321
        return $this->command('--help', new HelpCommand($this))
322
             ->command('--version', new VersionCommand($this))
323
             ->command('-i', new InteractiveCommand($this));
324
    }
325
326
    public function describe(string $name, string $description = null)
327
    {
328
        if (null === $description)
329
            return array_key_exists($name, $this->descriptions)
330
                ? $this->descriptions[$name] : '';
331
        if (substr($name, 0, 2) == '--' && array_key_exists($name, $this->options())) {
332
            $this->descriptions[$name] = $description;
333
            return $this;
334
        }
335
        try {
336
            $this->syntax->field($name);
337
            // throws exception if field is missing
338
            $this->descriptions[$name] = $description;
339
            return $this;
340
        } catch (\Exception $e) {
341
            throw new \InvalidArgumentException("Unknown field '{$name}'");
342
        }
343
    }
344
345
    public function run(array $args = null, array $options = [], bool $rawArgs = true)
346
    {
347
        try {
348
            $this->clear();
349
350
            if ($rawArgs) {
351
                if (null === $args) {
352
                    $args = $GLOBALS['argv'];
353
                    array_shift($args);
354
                }
355
356
                if (!empty($args) && array_key_exists($args[0], $this->commands)) {
357
                    $name = $args[0];
358
                    array_shift($args);
359
                    return $this->command($name)->run($args);
360
                }
361
362
                $this->parseArguments($args);
363
            } else {
364
                $this->args = (object) $args;
365
                foreach ($options as $name) {
366
                    if (!array_key_exists($name, $this->options))
367
                        throw new \Exception("Unknown option '{$name}'");
368
                    $this->options[$name] = true;
369
                }
370
            }
371
372
            return $this->fire();
373
        } catch (\Exception $e) {
374
            $this->handleError($e);
375
        }
376
    }
377
378
    protected function fire()
379
    {
380
        return (null === $this->action)
381
            ? $this->execute()
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->execute() targeting Tarsana\Command\Command::execute() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
382
            : ($this->action)($this);
383
    }
384
385
    protected function clear()
386
    {
387
        $this->args = null;
388
        foreach($this->options as $name => $value) {
389
            $this->options[$name] = false;
390
        }
391
    }
392
393
    protected function parseArguments(array $args)
394
    {
395
        $arguments = [];
396
        foreach ($args as &$arg) {
397
            if (array_key_exists($arg, $this->options))
398
                $this->options[$arg] = true;
399
            else
400
                $arguments[] = $arg;
401
        }
402
        if (null === $this->syntax) {
403
            $this->args = null;
404
        } else {
405
            $arguments = T::join($arguments, ' ');
406
            $this->args = $this->syntax->parse($arguments);
407
        }
408
    }
409
410
    protected function handleError(\Exception $e) {
411
        $output = (new ExceptionPrinter)->print($e);
412
        $this->console()->error($output);
413
    }
414
415
    protected function init() {}
416
    protected function execute() {}
417
418
}
419