Passed
Push — master ( 348486...676cd5 )
by Alexander
06:45 queued 03:38
created

TextDescriptor::describeOption()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 20
nc 6
nop 2
dl 0
loc 32
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Helper\Description;
24
25
use Syscodes\Components\Console\Application;
26
use Syscodes\Components\Console\Command\Command;
27
use Syscodes\Components\Console\Input\InputOption;
28
use Syscodes\Components\Console\Input\InputArgument;
29
use Syscodes\Components\Console\Input\InputDefinition;
30
31
/**
32
 * Text descriptor.
33
 */
34
class TextDescriptor extends Descriptor
35
{
36
    /**
37
     * The output interface implementation.
38
     * 
39
     * @var \Syscodes\Components\Contracts\Console\Output $output
40
     */
41
    protected $output;
42
43
    /**
44
     * Describes an InputArgument instance.
45
     * 
46
     * @param  \Syscodes\Components\Console\Input\InputArgument  $argument  The argument implemented
47
     * @param  array  $options  The options of the console
48
     * 
49
     * @return void
50
     */
51
    protected function describeArgument(InputArgument $argument, array $options = [])
52
    {
53
        if (null !== $argument->getDefault() && ( ! is_array($argument->getDefault()) || count($argument->getDefault()))) {
54
            $default = sprintf(' [<note>default: %s</>] ', $argument->getDefault());
55
        } else {
56
            $default = '';
57
        }
58
59
        $totalWidth = strlen($argument->getName());
60
        $spacingWidth = $totalWidth - strlen($argument->getName());
61
62
        $this->writeText(sprintf('  <info>%s</>  %s%s%s',
63
            $argument->getName(),
64
            str_repeat(' ', $spacingWidth),
65
            preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 4), $argument->getDescription()),
66
            $default
67
        ), $options);
68
    }
69
70
     /**
71
     * Describes an InputOption instance.
72
     * 
73
     * @param  \Syscodes\Components\Console\Input\InputOption  $option  The option implemented
74
     * @param  array  $options  The options of the console
75
     * 
76
     * @return void
77
     */
78
    protected function describeOption(InputOption $option, array $options = [])
79
    {
80
        if ($option->isAcceptValue() && null !== $option->getDefault() && ( ! is_array($option->getDefault()) || count($option->getDefault()))) {
81
            $default = sprintf(' [<note>default: %s</>] ', $option->getDefault());
82
        } else {
83
            $default = '';
84
        }
85
86
        $value = '';
87
88
        if ($option->isAcceptValue()) {
89
            $value = '='.strtoupper($option->getName());
90
91
            if ($option->isValueOptional()) {
92
                $value = '['.$value.']';
93
            }
94
        }
95
96
        $synopsis = sprintf('%s%s',
97
            $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : '    ',
98
            sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value)
99
        );
100
101
        $spacingWidth = (20 - strlen($synopsis));
102
103
        $this->writeText(sprintf('  <fg=green>%s</>  %s%s%s%s',
104
            $synopsis,
105
            str_repeat(' ', $spacingWidth),
106
            preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', 4), $option->getDescription()),
107
            $default,
108
            $option->isArray() ? '<info> (multiple values allowed)</>' : ''
109
        ), $options);
110
    }
111
112
    /**
113
     * Describes an InputDefinition instance.
114
     * 
115
     * @param  \Syscodes\Components\Console\Input\InputDefinition  $definition  The definition implemented
116
     * @param  array  $options  The options of the console
117
     * 
118
     * @return void
119
     */
120
    protected function describeDefinition(InputDefinition $definition, array $options = [])
121
    {
122
        if ($definition->getArguments()) {
123
            $this->writeText('<comment>Arguments:</>', $options);
124
            $this->writeText("\n");
125
126
            foreach ($definition->getArguments() as $argument) {
127
                $this->describeArgument($argument, $options);
128
                $this->writeText("\n");
129
            }
130
        }
131
132
        if ($definition->getArguments() && $definition->getOptions()) {
133
            $this->writeText("\n");
134
        }
135
136
        if ($definition->getOptions()) {
137
            $laterOptions = [];
138
139
            $this->writeText('<comment>Options:</>', $options);
140
141
            foreach ($definition->getOptions() as $option) {
142
                if (\strlen($option->getShortcut() ?? '') > 1) {
143
                    $laterOptions[] = $option;
144
                    continue;
145
                }
146
                $this->writeText("\n");
147
                $this->describeOption($option, $options);
148
            }
149
150
            foreach ($laterOptions as $option) {
151
                $this->writeText("\n");
152
                $this->describeOption($option, $options);
153
            }
154
        }
155
    }
156
157
    /**
158
     * Describes an Command instance.
159
     * 
160
     * @param  \Syscodes\Components\Console\Command\Command  $command  The command implemented
161
     * @param  array  $options  The options of the console
162
     * 
163
     * @return void
164
     */
165
    protected function describeCommand(Command $command, array $options = [])
166
    {
167
        $command->mergeApplicationDefinition(false);
168
169
        $this->writeText($command->getApplication()->getConsoleVersion());
170
        $this->writeText("\n\n");
171
172
        if ($description = $command->getDescription()) {
173
            $this->writeText('<comment>Description:</>', $options);
174
            $this->writeText("\n");
175
            $this->writeText('  '.$description);
176
            $this->writeText("\n\n");
177
        }
178
        
179
        $this->writeText('<comment>Usage:</>', $options);
180
        
181
        foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
182
            $this->writeText("\n");
183
            $this->writeText('  '.$usage, $options);
184
        }
185
        
186
        $this->writeText("\n\n");
187
188
        $definition = $command->getDefinition();
189
        
190
        if ($definition->getOptions() || $definition->getArguments()) {
191
            $this->describeDefinition($definition, $options);
192
            $this->writeText("\n");
193
        }
194
195
        $help = $command->getProccesedHelp();
196
197
        if ($help && $help !== $description) {
198
            $this->writeText("\n");
199
            $this->writeText('<comment>Help:</>', $options);
200
            $this->writeText("\n");
201
            $this->writeText('  '.str_replace("\n", "\n  ", $help), $options);
202
            $this->writeText("\n");
203
        }
204
    }
205
206
    /**
207
     * Describes an Application instance.
208
     * 
209
     * @param  \Syscodes\Components\Console\Application  $application  The application implemented
210
     * @param  array  $options  The options of the console
211
     * 
212
     * @return void
213
     */
214
    protected function describeApplication(Application $application, array $options = [])
215
    {
216
        $describedNamespace = $options['namespace'] ?? null;
217
        $description = new ApplicationDescription($application, $describedNamespace);
218
        
219
        if (isset($options['raw_text']) && $options['raw_text']) {
220
            $width = ($description->getCommands());
221
            
222
            foreach ($description->getCommands() as $command) {
223
                $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options);
224
                $this->writeText("\n");
225
            }
226
        } else {
227
            if ('' != $help = $application->getHelp()) {
228
                $this->writeText("$help\n\n", $options);
229
            }
230
            
231
            $this->writeText("<comment>Usage:</>\n", $options);
232
            $this->writeText("  command [options] [arguments]\n\n", $options);
233
234
            $this->describeDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);
235
236
            $this->writeText("\n");
237
            $this->writeText("\n");
238
            
239
            $commands = $description->getCommands();
240
            $namespaces = $description->getNamespaces();
241
            
242
            if ($describedNamespace && $namespaces) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $namespaces of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
243
                // make sure all alias commands are included when describing a specific namespace
244
                $describedNamespaceInfo = reset($namespaces);
245
                
246
                foreach ($describedNamespaceInfo['commands'] as $name) {
247
                    $commands[$name] = $description->getCommand($name);
248
                }
249
            }
250
            
251
            // calculate max. width based on available commands per namespace
252
            $width = (array_merge(...array_values(array_map(fn ($namespace) => array_intersect($namespace['commands'], array_keys($commands)), array_values($namespaces)))));
0 ignored issues
show
Unused Code introduced by
The assignment to $width is dead and can be removed.
Loading history...
253
            
254
            if ($describedNamespace) {
255
                $this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
256
            } else {
257
                $this->writeText('<comment>Available commands:</comment>', $options);
258
            }
259
            
260
            foreach ($namespaces as $namespace) {
261
                $namespace['commands'] = array_filter($namespace['commands'], fn ($name) => isset($commands[$name]));
262
                
263
                if ( ! $namespace['commands']) {
264
                    continue;
265
                }
266
                
267
                if ( ! $describedNamespace && ApplicationDescription::G_NAMESPACE !== $namespace['id']) {
268
                    $this->writeText("\n");
269
                    $this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
270
                }
271
                
272
                foreach ($namespace['commands'] as $name) {
273
                    $this->writeText("\n");
274
                    $spacingWidth = 18;
275
                    $command = $commands[$name];
276
                    $commandAliases = $name === $command->getName() ? $this->getCommandAliases($command) : '';
277
                    $this->writeText(sprintf('  <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options);
278
                }
279
            }
280
            
281
            $this->writeText("\n");
282
        }
283
    }
284
285
    /**
286
     * Writes a message to the output.
287
     * 
288
     * @param  string  $content  The message to output
289
     * @param  array  $options  The option of bitmask
290
     * 
291
     * @return string
292
     */
293
    private function writeText(string $content, array $options = [])
294
    {
295
        $this->write(
296
            isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
297
            isset($options['raw_output']) ? ! $options['raw_output'] : true
298
        );
299
    }
300
    
301
    /**
302
     * Formats command aliases to show them in the command description.
303
     * 
304
     * @param  \Syscodes\Components\Console\Command\Command  $command
305
     * 
306
     * @return string
307
     */
308
    private function getCommandAliases(Command $command): string
309
    {
310
        $text = '';
311
        $aliases = $command->getAliases();
312
        
313
        if ($aliases) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $aliases of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
314
            $text = '['.implode('|', $aliases).'] ';
315
        }
316
        
317
        return $text;
318
    }
319
}