AbstractHelp::renderOption()   D
last analyzed

Complexity

Conditions 9
Paths 24

Size

Total Lines 29
Code Lines 17

Duplication

Lines 3
Ratio 10.34 %

Code Coverage

Tests 16
CRAP Score 9.0164

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 3
loc 29
ccs 16
cts 17
cp 0.9412
rs 4.909
cc 9
eloc 17
nc 24
nop 2
crap 9.0164
1
<?php
2
3
/*
4
 * This file is part of the webmozart/console package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Webmozart\Console\UI\Help;
13
14
use Webmozart\Console\Api\Args\Format\ArgsFormat;
15
use Webmozart\Console\Api\Args\Format\Argument;
16
use Webmozart\Console\Api\Args\Format\Option;
17
use Webmozart\Console\Api\IO\IO;
18
use Webmozart\Console\UI\Component;
19
use Webmozart\Console\UI\Component\EmptyLine;
20
use Webmozart\Console\UI\Component\LabeledParagraph;
21
use Webmozart\Console\UI\Component\Paragraph;
22
use Webmozart\Console\UI\Layout\BlockLayout;
23
24
/**
25
 * Base class for rendering help pages.
26
 *
27
 * @since  1.0
28
 *
29
 * @author Bernhard Schussek <[email protected]>
30
 */
31
abstract class AbstractHelp implements Component
32
{
33
    /**
34
     * Renders the usage.
35
     *
36
     * @param IO  $io          The I/O.
37
     * @param int $indentation The number of spaces to indent.
38
     */
39 46
    public function render(IO $io, $indentation = 0)
40
    {
41 46
        $layout = new BlockLayout();
42
43 46
        $this->renderHelp($layout);
44
45 46
        $layout->render($io, $indentation);
46 46
    }
47
48
    /**
49
     * Renders the usage.
50
     *
51
     * Overwrite this class in your sub-classes to implement the actual
52
     * rendering.
53
     *
54
     * @param BlockLayout $layout The layout.
55
     */
56
    abstract protected function renderHelp(BlockLayout $layout);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
57
58
    /**
59
     * Renders a list of arguments.
60
     *
61
     * @param BlockLayout $layout    The layout.
62
     * @param Argument[]  $arguments The arguments to render.
63
     */
64 23 View Code Duplication
    protected function renderArguments(BlockLayout $layout, array $arguments)
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...
65
    {
66 23
        $layout->add(new Paragraph('<b>ARGUMENTS</b>'));
67 23
        $layout->beginBlock();
68
69 23
        foreach ($arguments as $argument) {
70 23
            $this->renderArgument($layout, $argument);
71
        }
72
73 23
        $layout->endBlock();
74 23
        $layout->add(new EmptyLine());
75 23
    }
76
77
    /**
78
     * Renders an argument.
79
     *
80
     * @param BlockLayout $layout   The layout.
81
     * @param Argument    $argument The argument to render.
82
     */
83 25
    protected function renderArgument(BlockLayout $layout, Argument $argument)
84
    {
85 25
        $description = $argument->getDescription();
86 25
        $name = '<c1><'.$argument->getName().'></c1>';
87 25
        $defaultValue = $argument->getDefaultValue();
88
89 25 View Code Duplication
        if (null !== $defaultValue && (!is_array($defaultValue) || count($defaultValue))) {
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...
90
            $description .= sprintf(' <b>(default: %s)</b>', $this->formatValue($defaultValue));
91
        }
92
93 25
        $layout->add(new LabeledParagraph($name, $description));
94 25
    }
95
96
    /**
97
     * Renders a list of options.
98
     *
99
     * @param BlockLayout $layout  The layout.
100
     * @param Option[]    $options The options to render.
101
     */
102 9 View Code Duplication
    protected function renderOptions(BlockLayout $layout, array $options)
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...
103
    {
104 9
        $layout->add(new Paragraph('<b>OPTIONS</b>'));
105 9
        $layout->beginBlock();
106
107 9
        foreach ($options as $option) {
108 9
            $this->renderOption($layout, $option);
109
        }
110
111 9
        $layout->endBlock();
112 9
        $layout->add(new EmptyLine());
113 9
    }
114
115
    /**
116
     * Renders a list of global options.
117
     *
118
     * @param BlockLayout $layout  The layout.
119
     * @param Option[]    $options The global options to render.
120
     */
121 28 View Code Duplication
    protected function renderGlobalOptions(BlockLayout $layout, array $options)
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...
122
    {
123 28
        $layout->add(new Paragraph('<b>GLOBAL OPTIONS</b>'));
124 28
        $layout->beginBlock();
125
126 28
        foreach ($options as $option) {
127 28
            $this->renderOption($layout, $option);
128
        }
129
130 28
        $layout->endBlock();
131 28
        $layout->add(new EmptyLine());
132 28
    }
133
134
    /**
135
     * Renders an option.
136
     *
137
     * @param BlockLayout $layout The layout.
138
     * @param Option      $option The option to render.
139
     */
140 34
    protected function renderOption(BlockLayout $layout, Option $option)
141
    {
142 34
        $description = $option->getDescription();
143 34
        $defaultValue = $option->getDefaultValue();
144
145 34
        if ($option->isLongNamePreferred()) {
146 32
            $preferredName = '--'.$option->getLongName();
147 32
            $alternativeName = $option->getShortName() ? '-'.$option->getShortName() : null;
148
        } else {
149 26
            $preferredName = '-'.$option->getShortName();
150 26
            $alternativeName = '--'.$option->getLongName();
151
        }
152
153 34
        $name = '<c1>'.$preferredName.'</c1>';
154
155 34
        if ($alternativeName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $alternativeName of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
156 28
            $name .= sprintf(' (%s)', $alternativeName);
157
        }
158
159 34 View Code Duplication
        if ($option->acceptsValue() && null !== $defaultValue && (!is_array($defaultValue) || count($defaultValue))) {
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...
160 1
            $description .= sprintf(' <b>(default: %s)</b>', $this->formatValue($defaultValue));
161
        }
162
163 34
        if ($option->isMultiValued()) {
164
            $description .= ' <b>(multiple values allowed)</b>';
165
        }
166
167 34
        $layout->add(new LabeledParagraph($name, $description));
168 34
    }
169
170
    /**
171
     * Renders the synopsis of a console command.
172
     *
173
     * @param BlockLayout $layout       The layout.
174
     * @param ArgsFormat  $argsFormat   The console arguments to render.
175
     * @param string      $appName      The name of the application binary.
176
     * @param string      $prefix       The prefix to insert.
177
     * @param bool        $lastOptional Set to `true` if the last command of the
178
     *                                  console arguments is optional. This
179
     *                                  command will be enclosed in brackets in
180
     *                                  the output.
181
     */
182 46
    protected function renderSynopsis(BlockLayout $layout, ArgsFormat $argsFormat, $appName, $prefix = '', $lastOptional = false)
183
    {
184 46
        $nameParts = array();
185 46
        $argumentParts = array();
186
187 46
        $nameParts[] = '<u>'.($appName ?: 'console').'</u>';
188
189 46
        foreach ($argsFormat->getCommandNames() as $commandName) {
190 27
            $nameParts[] = '<u>'.$commandName->toString().'</u>';
191
        }
192
193 46
        foreach ($argsFormat->getCommandOptions() as $commandOption) {
194 4
            $nameParts[] = $commandOption->isLongNamePreferred()
195 4
                ? '--'.$commandOption->getLongName()
196 4
                : '-'.$commandOption->getShortName();
197
        }
198
199 46
        if ($lastOptional) {
200 2
            $lastIndex = count($nameParts) - 1;
201 2
            $nameParts[$lastIndex] = '['.$nameParts[$lastIndex].']';
202
        }
203
204 46
        foreach ($argsFormat->getOptions(false) as $option) {
205
            // \xC2\xA0 is a non-breaking space
206 23
            if ($option->isValueRequired()) {
207 1
                $format = "%s\xC2\xA0<%s>";
208 22
            } elseif ($option->isValueOptional()) {
209 16
                $format = "%s\xC2\xA0[<%s>]";
210
            } else {
211 17
                $format = '%s';
212
            }
213
214 23
            $optionName = $option->isLongNamePreferred()
215 21
                ? '--'.$option->getLongName()
216 23
                : '-'.$option->getShortName();
217
218 23
            $argumentParts[] = sprintf('['.$format.']', $optionName, $option->getValueName());
219
        }
220
221 46
        foreach ($argsFormat->getArguments() as $argument) {
222 27
            $argName = $argument->getName();
223
224 27
            $argumentParts[] = sprintf(
225 27
                $argument->isRequired() ? '<%s>' : '[<%s>]',
226 27
                $argName.($argument->isMultiValued() ? '1' : '')
227
            );
228
229 27
            if ($argument->isMultiValued()) {
230 27
                $argumentParts[] = sprintf('... [<%sN>]', $argName);
231
            }
232
        }
233
234 46
        $argsOpts = implode(' ', $argumentParts);
235 46
        $name = implode(' ', $nameParts);
236
237 46
        $layout->add(new LabeledParagraph($prefix.$name, $argsOpts, 1, false));
238 46
    }
239
240
    /**
241
     * Formats the default value of an argument or an option.
242
     *
243
     * @param mixed $value The default value to format.
244
     *
245
     * @return string The formatted value.
246
     */
247 1
    protected function formatValue($value)
248
    {
249 1
        if (PHP_VERSION_ID < 50400) {
250
            return str_replace('\/', '/', json_encode($value));
251
        }
252
253 1
        return json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
254
    }
255
}
256