Console_GetoptPlus_Help::setUsage()   A
last analyzed

Complexity

Conditions 6
Paths 15

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 15
nc 15
nop 2
dl 0
loc 27
rs 9.2222
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Console GetoptPlus/Help
5
 *
6
 *
7
 * All rights reserved.
8
 * Redistribution and use in source and binary forms, with or without modification,
9
 * are permitted provided that the following conditions are met:
10
 * + Redistributions of source code must retain the above copyright notice,
11
 * this list of conditions and the following disclaimer.
12
 * + Redistributions in binary form must reproduce the above copyright notice,
13
 * this list of conditions and the following disclaimer in the documentation and/or
14
 * other materials provided with the distribution.
15
 * + The names of its contributors may not be used to endorse or promote
16
 * products derived from this software without specific prior written permission.
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 *
29
 * @category  Console
30
 * @package   Console_GetoptPlus
31
 * @author    Michel Corne <[email protected]>
32
 * @copyright 2008 Michel Corne
33
 * @license   http://www.opensource.org/licenses/bsd-license.php The BSD License
34
 * @version   SVN: $Id: Help.php 47 2008-01-10 11:03:38Z mcorne $
35
 * @link      http://pear.php.net/package/Console_GetoptPlus
36
 */
37
38
/**
39
 * Generation of the command usage/help
40
 *
41
 * @category  Console
42
 * @package   Console_GetoptPlus
43
 * @author    Michel Corne <[email protected]>
44
 * @copyright 2008 Michel Corne
45
 * @license   http://www.opensource.org/licenses/bsd-license.php The BSD License
46
 * @version   Release:@package_version@
47
 * @link      http://pear.php.net/package/Console_GetoptPlus
48
 */
49
class Console_GetoptPlus_Help
50
{
51
    /**
52
     * The option name padding within the option descrition
53
     */
54
    const optionPadding = 30;
55
    /**
56
     * The options section title
57
     */
58
    const options = 'Options:';
59
    /**
60
     * The parameter section title
61
     */
62
    const parameters = 'Parameters:';
63
    /**
64
     * The usage section title
65
     */
66
    const usage = 'Usage: ';
67
68
    /**
69
     * Aligns a set of lines
70
     *
71
     * Additional data is added to the first line.
72
     * The other lines are padded and aligned to the first one.
73
     *
74
     * @param array  $lines         the set of lines
75
     * @param string $addon         the additional data to add to the first line
76
     * @param int    $paddingLength the padding length
77
     * @return array   the aligned lines
78
     * @access public
79
     */
80
    public function alignLines($lines, $addon = '', $paddingLength = null)
81
    {
82
        $lines = (array)$lines;
83
        $addon = (string)$addon;
84
        // defaults the left alignment to the length of the additional data + 1
85
        null === $paddingLength and $paddingLength = $addon ? (mb_strlen($addon) + 1) : 0;
86
        // extracts the first line
87
        $firstLine      = (string)current($lines);
88
        $firstLineEmpty = '' == $firstLine;
89
90
        if (!$addon or $firstLineEmpty or $paddingLength > mb_strlen($addon)) {
91
            // no addon or padding larger than addon
92
            // pads the additional data and adds it to the left of the first line
93
            $addon     = str_pad($addon, $paddingLength);
94
            $firstLine = $addon . array_shift($lines);
95
        } else {
96
            // the information on the left is longer than the padding size
97
            $firstLine = $addon;
98
        }
99
        // left-pads the other lines
100
        $padding  = str_repeat(' ', $paddingLength);
101
        $callback = create_function('$string', "return '$padding' . \$string;");
102
        $lines    = array_map($callback, $lines);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type true; however, parameter $callback of array_map() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

102
        $lines    = array_map(/** @scrutinizer ignore-type */ $callback, $lines);
Loading history...
103
        // prepends the first line
104
        $firstLine = rtrim($firstLine);
105
        array_unshift($lines, $firstLine);
106
107
        return $lines;
108
    }
109
110
    /**
111
     * @param $config
112
     * @param $command
113
     * @return string
114
     */
115
    public static function get($config, $command)
116
    {
117
        $help = new self();
118
119
        return $help->set($config, $command);
120
    }
121
122
    /**
123
     * Creates the help/usage text
124
     *
125
     * @param array  $config  the command configuration
126
     * @param string $command the command name
127
     * @return string the help/usage text
128
     * @access public
129
     */
130
    public function set($config, $command)
131
    {
132
        // sets all the help/usage section texts
133
        $help = [];
134
        isset($config['header']) and $help[] = $this->tidyArray($config['header']);
135
        $help[] = $this->setUsage($config, $command);
136
        isset($config['options']) and $help[] = $this->setOptions($config['options']);
137
        isset($config['parameters']) and $help[] = $this->alignLines($config['parameters'], self::parameters);
138
        isset($config['footer']) and $help[] = $this->tidyArray($config['footer']);
139
        // merges the section texts together
140
        $callback = create_function('$array, $array1', '$array or $array = []; return array_merge($array, $array1);');
141
        $help     = array_reduce($help, $callback, []);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type true; however, parameter $callback of array_reduce() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
        $help     = array_reduce($help, /** @scrutinizer ignore-type */ $callback, []);
Loading history...
142
143
        return implode("\n", $help);
144
    }
145
146
    /**
147
     * Creates the options help text section
148
     *
149
     * @param array $optionsConfig the options descriptions
150
     * @return array  the options help text section
151
     * @access public
152
     */
153
    public function setOptions($optionsConfig)
154
    {
155
        $optionsConfig = (array)$optionsConfig;
156
157
        $padding  = str_repeat(' ', self::optionPadding);
158
        $callback = create_function('$string', "return '$padding' . \$string;");
0 ignored issues
show
Unused Code introduced by
The assignment to $callback is dead and can be removed.
Loading history...
159
160
        $lines = [];
161
        foreach ($optionsConfig as $option) {
162
            $desc = $option['desc'] ?? '';
163
            $desc = (array)$desc;
164
            // extracts the option example value from the description
165
            // encloses with angle/square brackets if mandatory/optional
166
            $value = '';
167
            empty($option['type']) or 'mandatory' == $option['type'] and $value = '<' . array_shift($desc) . '>' or 'optional' == $option['type'] and $value = '[' . array_shift($desc) . ']';
168
            // sets the option names
169
            $optionNames = [];
170
            isset($option['short']) and $optionNames[] = "-{$option['short']}";
171
            isset($option['long']) and $optionNames[] = "--{$option['long']}";
172
            $value and $optionNames[] = $value;
173
            $optionNames = implode(' ', $optionNames);
174
            // adds the option names to the description
175
            $desc  = $this->alignLines($desc, $optionNames, self::optionPadding);
176
            $lines = array_merge($lines, $desc);
177
        }
178
        // prefix the options with e.g. "Options:"
179
        $lines and array_unshift($lines, self::options);
180
181
        return $lines;
182
    }
183
184
    /**
185
     * Creates the usage help text section
186
     *
187
     * @param         $config
188
     * @param string  $command the command name
189
     * @return array  the usage help text section
190
     * @access public
191
     */
192
    public function setUsage($config, $command)
193
    {
194
        if (empty($config['usage'])) {
195
            // usage is empty, defaults to a one line usage,
196
            // e.g. [options] [parameters]
197
            $usage = [];
198
            empty($config['options']) or $usage[] = '[options]';
199
            empty($config['parameters']) or $usage[] = '[parameters]';
200
            $config['usage'] = implode(' ', $usage);
201
        }
202
        // expecting an array of arrays of usage lines,
203
        // or possibly a single usage line
204
        $config['usage'] = (array)$config['usage'];
205
        $lines           = [];
206
        $padding         = str_repeat(' ', mb_strlen(self::usage));
207
208
        foreach ($config['usage'] as $idx => $usage) {
209
            $usage = $this->tidyArray($usage);
210
            // adds the usage keywork to the first usage, e.g. "Usage:"
211
            $prefix = $idx ? $padding : self::usage;
212
            // adds the command to each usage, e.g. command [options] [parameters]
213
            $prefix .= basename($command);
214
            $usage  = $this->alignLines($usage, $prefix);
215
            $lines  = array_merge($lines, $usage);
216
        }
217
218
        return $lines;
219
    }
220
221
    /**
222
     * Tidies an array
223
     *
224
     * Makes an array if passed as a string.
225
     * Optionally forces the values to strings if there are not.
226
     *
227
     * @param array $array         the array
228
     * @param bool  $tidyString    forces the values to string if true,
229
     *                             or leaves them untouched if false
230
     * @return array   the tidied array
231
     * @access public
232
     */
233
    public function tidyArray($array, $tidyString = true)
234
    {
235
        $array = (array)$array;
236
        // tidies the array string values
237
        $tidyString and $array = array_map([$this, 'tidyString'], $array);
238
239
        return $array;
240
    }
241
242
    /**
243
     * Tidies a string
244
     *
245
     * Retains only the first value if passed as an array.
246
     *
247
     * @param string $string the string
248
     * @return string the tidy string
249
     * @access public
250
     */
251
    public function tidyString($string)
252
    {
253
        // if an array: captures the first value and converts it to a string
254
        // silently ignores the other values
255
        is_array($string) and $string = current($string);
256
257
        return trim($string);
258
    }
259
}
260