Argument::assertFlagsValid()   C
last analyzed

Complexity

Conditions 12
Paths 11

Size

Total Lines 34
Code Lines 19

Duplication

Lines 25
Ratio 73.53 %

Code Coverage

Tests 20
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 25
loc 34
ccs 20
cts 20
cp 1
rs 5.1612
cc 12
eloc 19
nc 11
nop 1
crap 12

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
 * 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\Api\Args\Format;
13
14
use InvalidArgumentException;
15
use Webmozart\Assert\Assert;
16
use Webmozart\Console\Util\StringUtil;
17
18
/**
19
 * An input argument.
20
 *
21
 * Args arguments are passed after the command name and its options. In the
22
 * example below, "localhost" is the argument to the "server -d" command.
23
 *
24
 * ```
25
 * $ console server -d localhost
26
 * ```
27
 *
28
 * Arguments can be either optional or required. By default, all arguments are
29
 * optional, but you can explicitly make an argument optional or required by
30
 * passing one of the flags {@link OPTIONAL} and {@link REQUIRED} to the
31
 * constructor:
32
 *
33
 * ```php
34
 * $argument = new Argument('server', Argument::REQUIRED);
35
 * ```
36
 *
37
 * Arguments can also be multi-valued. Multi-valued arguments can be passed any
38
 * number of times:
39
 *
40
 * ```
41
 * $ console server -d localhost google.com
42
 * ```
43
 *
44
 * To create a multi-valued argument, pass the flag {@link MULTI_VALUED} to the
45
 * constructor:
46
 *
47
 * ```php
48
 * $argument = new Argument('server', Argument::MULTI_VALUED);
49
 * ```
50
 *
51
 * You can combine the {@link MULTI_VALUED} flag with either {@link OPTIONAL}
52
 * or {@link REQUIRED} using the bitwise operator "|":
53
 *
54
 * ```php
55
 * $argument = new Argument('server', Argument::REQUIRED | Argument::MULTI_VALUED);
56
 * ```
57
 *
58
 * @since  1.0
59
 *
60
 * @author Bernhard Schussek <[email protected]>
61
 */
62
class Argument
63
{
64
    /**
65
     * Flag: The argument is required.
66
     */
67
    const REQUIRED = 1;
68
69
    /**
70
     * Flag: The argument is optional.
71
     */
72
    const OPTIONAL = 2;
73
74
    /**
75
     * Flag: The argument can be repeated multiple times.
76
     */
77
    const MULTI_VALUED = 4;
78
79
    /**
80
     * Flag: The value is parsed as string.
81
     */
82
    const STRING = 16;
83
84
    /**
85
     * Flag: The value is parsed as boolean.
86
     */
87
    const BOOLEAN = 32;
88
89
    /**
90
     * Flag: The value is parsed as integer.
91
     */
92
    const INTEGER = 64;
93
94
    /**
95
     * Flag: The value is parsed as float.
96
     */
97
    const FLOAT = 128;
98
99
    /**
100
     * Flag: The value "null" should be parsed as `null`.
101
     */
102
    const NULLABLE = 256;
103
104
    /**
105
     * @var string
106
     */
107
    private $name;
108
109
    /**
110
     * @var int
111
     */
112
    private $flags;
113
114
    /**
115
     * @var mixed
116
     */
117
    private $defaultValue;
118
119
    /**
120
     * @var string
121
     */
122
    private $description;
123
124
    /**
125
     * Creates a new argument.
126
     *
127
     * @param string $name         The argument name
128
     * @param int    $flags        A bitwise combination of the flag constants.
129
     * @param string $description  A human-readable description of the argument.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $description not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
130
     * @param mixed  $defaultValue The default value of the argument (must be
131
     *                             null for the flag {@link self::REQUIRED}).
132
     */
133 285
    public function __construct($name, $flags = 0, $description = null, $defaultValue = null)
134
    {
135 285
        Assert::string($name, 'The argument name must be a string. Got: %s');
136 283
        Assert::notEmpty($name, 'The argument name must not be empty.');
137 282
        Assert::startsWithLetter($name, 'The argument name must start with a letter.');
138 280
        Assert::regex($name, '~^[a-zA-Z0-9\-]+$~', 'The argument name must contain letters, digits and hyphens only.');
139 279
        Assert::nullOrString($description, 'The argument description must be a string or null. Got: %s');
140 278
        Assert::nullOrNotEmpty($description, 'The argument description must not be empty.');
141
142 277
        $this->assertFlagsValid($flags);
143
144 269
        $this->addDefaultFlags($flags);
145
146 269
        $this->name = $name;
147 269
        $this->flags = $flags;
148 269
        $this->description = $description;
149 269
        $this->defaultValue = $this->isMultiValued() ? array() : null;
150
151 269
        if ($this->isOptional() || null !== $defaultValue) {
152 254
            $this->setDefaultValue($defaultValue);
153
        }
154 267
    }
155
156
    /**
157
     * Returns the name of the argument.
158
     *
159
     * @return string The argument name.
160
     */
161 418
    public function getName()
162
    {
163 418
        return $this->name;
164
    }
165
166
    /**
167
     * Returns whether the argument is required.
168
     *
169
     * @return bool Returns `true` if the flag {@link REQUIRED} was passed to
170
     *              the constructor.
171
     */
172 451
    public function isRequired()
173
    {
174 451
        return (bool) (self::REQUIRED & $this->flags);
175
    }
176
177
    /**
178
     * Returns whether the argument is optional.
179
     *
180
     * @return bool Returns `true` if the flag {@link OPTIONAL} was passed to
181
     *              the constructor.
182
     */
183 451
    public function isOptional()
184
    {
185 451
        return (bool) (self::OPTIONAL & $this->flags);
186
    }
187
188
    /**
189
     * Returns whether the argument accepts multiple values.
190
     *
191
     * @return bool Returns `true` if the flag {@link MULTI_VALUED} was
192
     *              passed to the constructor.
193
     */
194 451
    public function isMultiValued()
195
    {
196 451
        return (bool) (self::MULTI_VALUED & $this->flags);
197
    }
198
199
    /**
200
     * Sets the default value.
201
     *
202
     * If the argument is required, this method throws an exception.
203
     *
204
     * If the option is multi-valued, the passed value must be an array or
205
     * `null`.
206
     *
207
     * @param mixed $defaultValue The default value.
208
     *
209
     * @throws InvalidValueException If the default value is invalid.
210
     */
211 254 View Code Duplication
    public function setDefaultValue($defaultValue = 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...
212
    {
213 254
        if ($this->isRequired()) {
214 1
            throw new InvalidValueException('Required arguments do not accept default values.');
215
        }
216
217 253
        if ($this->isMultiValued()) {
218 37
            if (null === $defaultValue) {
219 35
                $defaultValue = array();
220 2
            } elseif (!is_array($defaultValue)) {
221 1
                throw new InvalidValueException(sprintf(
222
                    'The default value of a multi-valued argument must be an '.
223 1
                    'array. Got: %s',
224 1
                    is_object($defaultValue) ? get_class($defaultValue) : gettype($defaultValue)
225
                ));
226
            }
227
        }
228
229 252
        $this->defaultValue = $defaultValue;
230 252
    }
231
232
    /**
233
     * Returns the default value of the argument.
234
     *
235
     * @return mixed The default value.
236
     */
237 297
    public function getDefaultValue()
238
    {
239 297
        return $this->defaultValue;
240
    }
241
242
    /**
243
     * Parses an argument value.
244
     *
245
     * Pass one of the flags {@link STRING}, {@link BOOLEAN}, {@link INTEGER}
246
     * and {@link FLOAT} to the constructor to configure the result of this
247
     * method. You can optionally combine the flags with {@link NULLABLE} to
248
     * support the conversion of "null" to `null`.
249
     *
250
     * @param mixed $value The value to parse.
251
     *
252
     * @return mixed The parsed value.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use null|boolean|integer|double|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
253
     *
254
     * @throws InvalidValueException
255
     */
256 182 View Code Duplication
    public function parseValue($value)
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...
257
    {
258 182
        $nullable = ($this->flags & self::NULLABLE);
259
260 182
        if ($this->flags & self::BOOLEAN) {
261 4
            return StringUtil::parseBoolean($value, $nullable);
0 ignored issues
show
Documentation introduced by
$nullable is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
262
        }
263
264 178
        if ($this->flags & self::INTEGER) {
265 8
            return StringUtil::parseInteger($value, $nullable);
0 ignored issues
show
Documentation introduced by
$nullable is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
266
        }
267
268 170
        if ($this->flags & self::FLOAT) {
269 5
            return StringUtil::parseFloat($value, $nullable);
0 ignored issues
show
Documentation introduced by
$nullable is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
270
        }
271
272 165
        return StringUtil::parseString($value, $nullable);
0 ignored issues
show
Documentation introduced by
$nullable is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
273
    }
274
275
    /**
276
     * Returns the description text.
277
     *
278
     * @return string The description text.
279
     */
280 293
    public function getDescription()
281
    {
282 293
        return $this->description;
283
    }
284
285 277
    private function assertFlagsValid($flags)
286
    {
287 277
        Assert::integer($flags, 'The argument flags must be an integer. Got: %s');
288
289 276
        if (($flags & self::REQUIRED) && ($flags & self::OPTIONAL)) {
290 1
            throw new InvalidArgumentException('The argument flags REQUIRED and OPTIONAL cannot be combined.');
291
        }
292
293 275 View Code Duplication
        if ($flags & self::STRING) {
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...
294 11
            if ($flags & self::BOOLEAN) {
295 1
                throw new InvalidArgumentException('The argument flags STRING and BOOLEAN cannot be combined.');
296
            }
297
298 10
            if ($flags & self::INTEGER) {
299 1
                throw new InvalidArgumentException('The argument flags STRING and INTEGER cannot be combined.');
300
            }
301
302 9
            if ($flags & self::FLOAT) {
303 9
                throw new InvalidArgumentException('The argument flags STRING and FLOAT cannot be combined.');
304
            }
305 264
        } elseif ($flags & self::BOOLEAN) {
306 6
            if ($flags & self::INTEGER) {
307 1
                throw new InvalidArgumentException('The argument flags BOOLEAN and INTEGER cannot be combined.');
308
            }
309
310 5
            if ($flags & self::FLOAT) {
311 5
                throw new InvalidArgumentException('The argument flags BOOLEAN and FLOAT cannot be combined.');
312
            }
313 258
        } elseif ($flags & self::INTEGER) {
314 9
            if ($flags & self::FLOAT) {
315 1
                throw new InvalidArgumentException('The argument flags INTEGER and FLOAT cannot be combined.');
316
            }
317
        }
318 269
    }
319
320 269
    private function addDefaultFlags(&$flags)
321
    {
322 269
        if (!($flags & (self::REQUIRED | self::OPTIONAL))) {
323 169
            $flags |= self::OPTIONAL;
324
        }
325
326 269 View Code Duplication
        if (!($flags & (self::STRING | self::BOOLEAN | self::INTEGER | self::FLOAT))) {
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...
327 244
            $flags |= self::STRING;
328
        }
329 269
    }
330
}
331