Passed
Push — 0.8.x ( 1f257c...711caa )
by Alexander
09:40 queued 06:33
created

Command::getDefaultName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2023 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Components\Console\Command;
24
25
use Throwable;
26
use TypeError;
27
use LogicException;
28
use ReflectionProperty;
29
use InvalidArgumentException;
30
use Syscodes\Components\Support\Str;
31
use Syscodes\Components\Console\Application;
32
use Syscodes\Components\Console\Input\InputOption;
33
use Syscodes\Components\Console\Input\InputArgument;
34
use Syscodes\Components\Console\Input\InputDefinition;
35
use Syscodes\Components\Contracts\Console\Input\Input as InputInterface;
36
use Syscodes\Components\Contracts\Console\Output\Output as OutputInterface;
37
38
/**
39
 * Base class for all commands.
40
 */
41
class Command 
42
{
43
    /**
44
     * The default application.
45
     * 
46
     * @var \Syscodes\Components\Console\Application $application
47
     */
48
    protected $application;
49
50
    /**
51
     * The default command name.
52
     * 
53
     * @var string|null $defaultName
54
     */
55
    protected static $defaultName;
56
57
     /**
58
     * The default command description.
59
     * 
60
     * @var string|null $defaultDescription
61
     */
62
    protected static $defaultDescription;
63
64
    /**
65
     * Gets the aliases of command name.
66
     * 
67
     * @var string[] $aliases
68
     */
69
    protected $aliases = [];
70
71
    /**
72
     * Gets the Command's Arguments description.
73
     * 
74
     * @var array $arguments
75
     */
76
    protected $arguments = [];
77
78
    /**
79
     * The code to execute some command.
80
     * 
81
     * @var int $code
82
     */
83
    protected $code = 0;
84
85
    /**
86
     * The InputDefinition implement.
87
     * 
88
     * @var \Syscodes\Components\Console\Input\InputDefinition $definition
89
     */
90
    protected $definition;
91
92
    /**
93
     * The console command description.
94
     * 
95
     * @var string|null $description
96
     */
97
    protected $description;
98
99
    /**
100
     * The InputDefinition full implemented.
101
     * 
102
     * @var \Syscodes\Components\Console\Input\InputDefinition $fullDefinition
103
     */
104
    protected $fullDefinition;
105
106
    /**
107
     * The group of commands is lumped under, when listing commands.
108
     * 
109
     * @var string $group
110
     */
111
    protected $group;
112
113
    /**
114
     * The console command help text.
115
     * 
116
     * @var string $help
117
     */
118
    protected $help;
119
120
    /**
121
     * Indicates whether the command should be shown in the Prime command list.
122
     * 
123
     * @var bool $hidden
124
     */
125
    protected $hidden = false;
126
127
    /**
128
     * The validation of ignored errors 
129
     * 
130
     * @var bool $ignoreValidationErrors
131
     */
132
    protected $ignoreValidationErrors = false;
133
134
    /**
135
     * The console command name.
136
     * 
137
     * @var string $name
138
     */
139
    protected $name;
140
141
    /**
142
     * The Command's options description.
143
     * 
144
     * @var array $options
145
     */
146
    protected $options = [];
147
148
    /**
149
     * The console command synopsis.
150
     * 
151
     * @var array $synopsis
152
     */
153
    protected $synopsis = [];
154
155
    /**
156
     * The console command usages.
157
     * 
158
     * @var array $usages
159
     */
160
    protected $usages = [];
161
162
    /**
163
     * Gets the default name.
164
     * 
165
     * @return string|null
166
     */
167
    public static function getDefaultName(): ?string
168
    {
169
        $class = static::class;
170
171
        $property = new ReflectionProperty($class, 'defaultName');
172
173
        return ($class === $property->class) ? static::$defaultName : null;
174
    }
175
176
    /**
177
     * Gets the default description.
178
     * 
179
     * @return string|null
180
     */
181
    public static function getDefaultDescription(): ?string
182
    {
183
        $class = static::class;
184
185
        $property = new ReflectionProperty($class, 'default description');
186
187
        return ($class === $property->class) ? static::$defaultDescription : null;
188
    }
189
190
    /**
191
     * Constructor. Create a new base command instance.
192
     * 
193
     * @param  string|null  $name  The name command
194
     * @param  \Syscodes\Components\Console\Input\InputDefinition  $definition
195
     * 
196
     * @return void
197
     */
198
    public function __construct(string $name = null)
199
    {
200
        $this->definition = new InputDefinition();
201
        
202
        if ($name && null !== $name = static::getDefaultName()) {
203
            $aliases = explode('|', $name);
204
            
205
            if ('' === $name = array_shift($aliases)) {
206
                $this->setHidden(true);
207
                
208
                $name = array_shift($aliases);
209
            }
210
            
211
            $this->setAliases($aliases);
212
        }
213
        
214
        if (null !== $name) {
215
            $this->setName($name);
216
        }
217
        
218
        if ('' === $this->description) {
219
            $this->setDescription(static::getDefaultDescription() ?? '');
220
        }
221
        
222
        $this->define();
223
    }
224
225
    /**
226
     * Gets input definition for command.
227
     * 
228
     * @return void
229
     */
230
    protected function define() {}
231
232
    /**
233
     * Sets the command.
234
     * 
235
     * @param  \Syscodes\Components\Console\Command\Command  $command
236
     * 
237
     * @return void
238
     */
239
    public function setCommand(Command $command) {}
0 ignored issues
show
Unused Code introduced by
The parameter $command 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

239
    public function setCommand(/** @scrutinizer ignore-unused */ Command $command) {}

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...
240
241
    /**
242
     * Executes the current command.
243
     * 
244
     * @param  \Syscodes\Components\Contracts\Console\Input\Input  $input
245
     * @param  \Syscodes\Components\Contracts\Console\Output\Output  $output
246
     * 
247
     * @return int|mixed
248
     * 
249
     * @throws \LogicException
250
     */
251
    protected function execute(InputInterface $input, OutputInterface $output) {}
252
253
    /**
254
     * Runs the command.
255
     * 
256
     * @param  \Syscodes\Components\Contracts\Console\Input\Input  $input
257
     * @param  \Syscodes\Components\Contracts\Console\Output\Output  $output
258
     * 
259
     * @return int|mixed
260
     * 
261
     * @throws \InvalidArgumentException
262
     */
263
    public function run(InputInterface $input, OutputInterface $output)
264
    {
265
        // add the application arguments and options
266
        $this->mergeApplicationDefinition();
267
268
        try {
269
            $input->linked($this->getDefinition());
270
        } catch (Throwable $e) {
271
            if ( ! $this->ignoreValidationErrors) {
272
                throw $e;
273
            }
274
        }
275
        
276
        if ($input->hasArgument('command') && null === $input->getArgument('command')) {
277
            $input->setArgument('command', $this->getName());
278
        }
279
280
        $statusCode = '';
281
282
        if (0 === (int) $this->code) {
283
            $statusCode = $this->execute($input, $output);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $statusCode is correct as $this->execute($input, $output) targeting Syscodes\Components\Cons...mand\Command::execute() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
284
        } else {
285
            throw new TypeError(
286
                sprintf('Returned value in "%s::execute()" must be of the type int, "%s" returned', static::class, get_debug_type($statusCode))
287
            );
288
        }
289
290
        return is_numeric($statusCode) ? (int) $statusCode : 0;
0 ignored issues
show
introduced by
The condition is_numeric($statusCode) is always false.
Loading history...
291
    }
292
    
293
    /**
294
     * Merges the application definition with the command definition.
295
     * 
296
     * @param  bool  $mergeArgs  Whether to merge or not the Application definition arguments to Command definition arguments
297
     * 
298
     * @internal
299
     */
300
    public function mergeApplicationDefinition(bool $mergeArgs = true)
301
    {
302
        if (null === $this->application) {
303
            return;
304
        }
305
        
306
        $this->fullDefinition = new InputDefinition();
307
        $this->fullDefinition->setOptions($this->definition->getOptions());
308
        $this->fullDefinition->addOptions($this->application->getDefinition()->getOptions());
309
        
310
        if ($mergeArgs) {
311
            $this->fullDefinition->setArguments($this->application->getDefinition()->getArguments());
312
            $this->fullDefinition->addArguments($this->definition->getArguments());
313
        } else {
314
            $this->fullDefinition->setArguments($this->definition->getArguments());
315
        }
316
    }
317
318
    /**
319
     * Gets the InputDefinition to be used to representate arguments 
320
     * and options in a command.
321
     * 
322
     * @return \Syscodes\Components\Console\Input\InputDefinition
323
     */
324
    public function getDefinition()
325
    {
326
        if (null === $this->definition) {
327
            throw new LogicException(
328
                sprintf('Probably Command class "%s" is not correctly initialized  because forget to call the parent constructor', static::class)
329
            );
330
        }
331
332
        return $this->fullDefinition ?? $this->definition;
333
    }
334
    
335
    /**
336
     * Sets the InputDefinition to be used to representate arguments
337
     * and options in a command.
338
     * 
339
     * @param  array|\Syscodes\Components\Console\Input\InputDefinition  $definition  An array of InputArgument and InputOption instance
340
     * 
341
     * @return static
342
     */
343
    public function setDefinition($definition): static 
344
    {
345
        if ($definition instanceof InputDefinition) {
346
            $this->definition = $definition;
347
        } else {
348
            $this->definition->setDefinition($definition);
349
        }
350
        
351
        $this->fullDefinition = null;
352
353
        return $this;
354
    }
355
356
    /**
357
     * Adds an argument in a command.
358
     * 
359
     * @param  string  $name  The argument name
360
     * @param  int|null  $mode  The argument mode
361
     * @param  string  $description  The description text
362
     * @param  mixed|null  $default  The default value
363
     * 
364
     * @return static
365
     * 
366
     * @throws \InvalidArgumentException  When argument mode is not valid
367
     * @throws \LogicException
368
     */
369
    public function addArgument(
370
        string $name,
371
        int $mode = null,
372
        string $description = '',
373
        mixed $default = null
374
    ): static {
375
        $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
376
377
        return $this;
378
    }
379
380
    /**
381
     * Adds an option in a command.
382
     * 
383
     * @param  string  $name  The argument name
384
     * @param  string|array|null  $shortcut  The shortcut of the option
385
     * @param  int|null  $mode  The argument mode
386
     * @param  string  $description  The description text
387
     * @param  mixed  $default  The default value
388
     * 
389
     * @return static
390
     * 
391
     * @throws \InvalidArgumentException  If option mode is invalid
392
     */
393
    public function addOption(
394
        string $name, 
395
        $shortcut = null,
396
        int $mode = null,
397
        string $description = '',
398
        $default = null
399
    ): static {
400
        $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
401
402
        return $this;
403
    }
404
    
405
    /**
406
     * Gets the command name.
407
     * 
408
     * @return string|null
409
     */
410
    public function getName(): ?string
411
    {
412
        return $this->name;
413
    }
414
415
    /**
416
     * Sets the name of the command.
417
     * 
418
     * @param  string  $name  The command name
419
     * 
420
     * @return static
421
     */
422
    public function setName(string $name): static
423
    {
424
        $this->validateName($name);
425
426
        $this->name = $name;
427
428
        return $this;
429
    }
430
431
    /**
432
     * Gets the application instance for this command.
433
     * 
434
     * @return \Syscodes\Components\Console\application|null
435
     */
436
    public function getApplication()
437
    {
438
        return $this->application;
439
    }
440
441
    /**
442
     * sets the application instance for this command.
443
     * 
444
     * @return static
445
     */
446
    public function setApplication(Application $application = null): static
447
    {
448
       $this->application = $application;
449
450
       return $this;
451
    }
452
453
    /**
454
     * Gets the code when execute a command.
455
     * 
456
     * @return int
457
     */
458
    public function getcode(): int
459
    {
460
        return $this->code;
461
    }
462
463
    /**
464
     * Sets the code when execute a command.
465
     * 
466
     * @param  int  $code  The code to execute in the command
467
     * 
468
     * @return static
469
     */
470
    public function setcode(int $code): static
471
    {
472
        $this->code = $code;
473
474
        return $this;
475
    }
476
    
477
    /**
478
     * Gets whether the command should be publicly shown or not.
479
     * 
480
     * @return bool
481
     */
482
    public function isHidden(): bool
483
    {
484
        return $this->hidden;
485
    }
486
    
487
    /**
488
     * Whether or not the command should be hidden from the list of commands.
489
     * 
490
     * @param  bool  $hidden
491
     * 
492
     * @return static
493
     */
494
    public function setHidden(bool $hidden): static
495
    {
496
        $this->hidden = $hidden;
497
        
498
        return $this;
499
    }
500
    
501
    /**
502
     * Returns the description for the command.
503
     * 
504
     * @return string|null
505
     */
506
    public function getDescription(): ?string
507
    {
508
        return $this->description;
509
    }
510
    
511
    /**
512
     * Sets the description for the command.
513
     * 
514
     * @param  string  $description The command description
515
     * 
516
     * @return static
517
     */
518
    public function setDescription(string $description): static
519
    {
520
        $this->description = $description;
521
        
522
        return $this;
523
    }
524
525
    /**
526
     * Returns the help for the command.
527
     *
528
     * @return string
529
     */
530
    public function getHelp(): string
531
    {
532
        return $this->help;
533
    }
534
535
    /**
536
     * Returns the proccesed help for the command.
537
     * 
538
     * @return string
539
     */
540
    public function getProccesedHelp(): string
541
    {
542
        $name = $this->getName();
543
544
        $isSingleCommand = $this->application && $this->application->isSingleCommand();
545
546
        $placeholder = [
547
            '%command-name%',
548
            '%command-fullname%',
549
        ];
550
551
        $replacement = [
552
            $name,
553
            $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
554
        ];
555
556
        return str_replace($placeholder, $replacement, $this->getHelp() ?: $this->getDescription());
557
    }
558
559
    /**
560
     * Sets the help for the command.
561
     *
562
     * @return static
563
     */
564
    public function setHelp(string $help): static
565
    {
566
        $this->help = $help;
567
568
        return $this;
569
    }
570
571
    /**
572
     * Returns alternative usages of the command.
573
     * 
574
     * @return array
575
     */
576
    public function getUsages(): array
577
    {
578
        return $this->usages;
579
    }
580
581
    /**
582
     * Add a command usage as example.
583
     * 
584
     * @param  string  $usage  The command name usage
585
     * 
586
     * @return static
587
     */
588
    public function addUsage(string $usage): static
589
    {
590
        if ( ! Str::startsWith($usage, $this->name)) {
591
            $usage = sprintf('%s %s', $this->name, $usage);
592
        }
593
594
        $this->usages[] = $usage;
595
596
        return $this;
597
    }
598
599
    /**
600
     * Returns the synopsis for the command.
601
     * 
602
     * @param  bool  short
0 ignored issues
show
Bug introduced by
The type Syscodes\Components\Console\Command\short 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...
603
     * 
604
     * @return string
605
     */
606
    public function getSynopsis(bool $short = false): string
607
    {
608
        $value = $short ? 'short' : 'long';
609
610
        if ( ! isset($this->synopsis[$value])) {
611
            $this->synopsis[$value] = sprintf('%s %s', $this->name, $this->definition->getSynopsis($short));
612
        }
613
614
        return $this->synopsis[$value];
615
    }
616
617
    /**
618
     * Gets the aliases for the command.
619
     * 
620
     * @return string[]
621
     */
622
    public function getAliases(): array
623
    {
624
        return $this->aliases;
625
    }
626
    
627
    /**
628
     * Sets the aliases for the command.
629
     * 
630
     * @param string[] $aliases An array of aliases for the command
631
     * 
632
     * @return static
633
     * 
634
     * @throws InvalidArgumentException When an alias is invalid
635
     */
636
    public function setAliases(iterable $aliases): static
637
    {
638
        $list = [];
639
        
640
        foreach ($aliases as $alias) {
641
            $this->validateName($alias);
642
            $list[] = $alias;
643
        }
644
        
645
        $this->aliases = is_array($aliases) ? $aliases : $list;
646
        
647
        return $this;
648
    }
649
    
650
    /**
651
     * Checks whether the command is enabled or not in the current environment.
652
     * 
653
     * @return bool
654
     */
655
    public function isEnabled(): bool
656
    {
657
        return true;
658
    }
659
660
    /**
661
     * Validates a command name. e.g: php prime make:example.
662
     * 
663
     * @param  string  $name
664
     * 
665
     * @return void
666
     * 
667
     * @throws \InvalidArgumentException
668
     */
669
    private function validateName(string $name): void
670
    {
671
        if ( ! preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
672
            throw new InvalidArgumentException(sprintf('Command name "%s" is invalid', $name));
673
        }
674
    }
675
}