Completed
Push — master ( ad5456...f93915 )
by Amine
10s
created

Command::configPaths()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
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;
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 View Code Duplication
    public function name(string $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function description(string $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
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 View Code Duplication
    public function console(ConsoleInterface $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function templatesLoader(TemplateLoaderInterface $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function templatesPath(string $path, string $cachePath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function action(callable $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function commands(array $value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                foreach ($options as $name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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()
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
        if (null === $this->syntax) {
396
            $this->args = null;
397
            return;
398
        }
399
400
        $arguments = [];
401 View Code Duplication
        foreach ($args as &$arg) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
402
            if (array_key_exists($arg, $this->options))
403
                $this->options[$arg] = true;
404
            else
405
                $arguments[] = $arg;
406
        }
407
        $arguments = T::join($arguments, ' ');
408
        $this->args = $this->syntax->parse($arguments);
409
    }
410
411
    protected function handleError(\Exception $e) {
412
        $output = (new ExceptionPrinter)->print($e);
413
        $this->console()->error($output);
414
    }
415
416
    protected function init() {}
417
    protected function execute() {}
418
419
}
420