ArgsFormat::getNumberOfRequiredArguments()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4.0119

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 21
ccs 10
cts 11
cp 0.9091
rs 9.0534
cc 4
eloc 11
nc 4
nop 1
crap 4.0119
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\Api\Args\NoSuchArgumentException;
17
use Webmozart\Console\Api\Args\NoSuchOptionException;
18
19
/**
20
 * The format used to parse a {@link RawArgs} instance.
21
 *
22
 * This class is a container for {@link CommandName}, {@link CommandOption},
23
 * {@link Option} and {@link Argument} objects. The format is used to interpret
24
 * a given {@link RawArgs} instance.
25
 *
26
 * You can pass the options and arguments to the constructor of the class:
27
 *
28
 * ```php
29
 * $format = new ArgsFormat(array(
30
 *     new CommandName('server'),
31
 *     new CommandName('add'),
32
 *     new Argument('host', Argument::REQUIRED),
33
 *     new Option('port', 'p', Option::VALUE_OPTIONAL, null, 80),
34
 * ));
35
 * ```
36
 *
37
 * The previous example configures a command that can be called like this:
38
 *
39
 * ```
40
 * $ console server add localhost
41
 * $ console server add localhost --port 8080
42
 * ```
43
 *
44
 * If the "add" command should be called via an option, change the format to:
45
 *
46
 * ```php
47
 * $format = new ArgsFormat(array(
48
 *     new CommandName('server'),
49
 *     new CommandOption('add', 'a'),
50
 *     new Argument('host', Argument::REQUIRED),
51
 *     new Option('port', 'p', Option::VALUE_OPTIONAL, null, 80),
52
 * ));
53
 * ```
54
 *
55
 * The command is then called like this:
56
 *
57
 * ```
58
 * $ console server --add localhost
59
 * $ console server --add localhost --port 8080
60
 * ```
61
 *
62
 * The format is immutable after its construction. This is necessary to maintain
63
 * consistency when one format inherits from another. For example, adding a
64
 * required argument to the base format of a format that already contains
65
 * optional arguments is an illegal operation that cannot be prevented if the
66
 * formats are mutable.
67
 *
68
 * If you want to create a format stepwisely, use an {@link ArgsFormatBuilder}.
69
 *
70
 * If multiple formats share a common set of options and arguments, extract
71
 * these options and arguments into a base format and let the other formats
72
 * inherit from this base format:
73
 *
74
 * ```php
75
 * $baseFormat = new ArgsFormat(array(
76
 *     new Option('verbose', 'v'),
77
 * ));
78
 *
79
 * $format = new ArgsFormat(array(
80
 *     new CommandName('server'),
81
 *     new CommandName('add'),
82
 *     new Argument('host', Argument::REQUIRED),
83
 *     new Option('port', 'p', Option::VALUE_OPTIONAL, null, 80),
84
 * ), $baseFormat);
85
 * ```
86
 *
87
 * @since  1.0
88
 *
89
 * @author Bernhard Schussek <[email protected]>
90
 */
91
class ArgsFormat
92
{
93
    /**
94
     * @var ArgsFormat
95
     */
96
    private $baseFormat;
97
98
    /**
99
     * @var CommandName[]
100
     */
101
    private $commandNames;
102
103
    /**
104
     * @var CommandOption[]
105
     */
106
    private $commandOptions = array();
107
108
    /**
109
     * @var CommandOption[]
110
     */
111
    private $commandOptionsByShortName = array();
112
113
    /**
114
     * @var Argument[]
115
     */
116
    private $arguments;
117
118
    /**
119
     * @var Option[]
120
     */
121
    private $options;
122
123
    /**
124
     * @var Option[]
125
     */
126
    private $optionsByShortName = array();
127
128
    /**
129
     * @var bool
130
     */
131
    private $hasMultiValuedArg = false;
132
133
    /**
134
     * @var bool
135
     */
136
    private $hasOptionalArg = false;
137
138
    /**
139
     * Returns a format builder.
140
     *
141
     * You can optionally pass a base format. The built format inherits all the
142
     * arguments and options defined in the base format.
143
     *
144
     * @param ArgsFormat $baseFormat The base format.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $baseFormat not be null|ArgsFormat?

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...
145
     *
146
     * @return ArgsFormatBuilder The created builder.
147
     */
148 331
    public static function build(ArgsFormat $baseFormat = null)
149
    {
150 331
        return new ArgsFormatBuilder($baseFormat);
151
    }
152
153
    /**
154
     * Creates a new format.
155
     *
156
     * You can optionally pass a base format. The created format inherits all
157
     * the arguments and options defined in the base format.
158
     *
159
     * @param array|ArgsFormatBuilder $elements   The arguments and options or a
160
     *                                            builder instance.
161
     * @param ArgsFormat              $baseFormat The format.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $baseFormat not be null|ArgsFormat?

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...
162
     */
163 587
    public function __construct($elements = array(), ArgsFormat $baseFormat = null)
164
    {
165 587
        if ($elements instanceof ArgsFormatBuilder) {
166 366
            $builder = $elements;
167
        } else {
168 428
            $builder = $this->createBuilderForElements($elements, $baseFormat);
169
        }
170
171 586
        if (null === $baseFormat) {
172 586
            $baseFormat = $builder->getBaseFormat();
173
        }
174
175 586
        $this->baseFormat = $baseFormat;
176 586
        $this->commandNames = $builder->getCommandNames(false);
177 586
        $this->arguments = $builder->getArguments(false);
178 586
        $this->options = $builder->getOptions(false);
179 586
        $this->hasMultiValuedArg = $builder->hasMultiValuedArgument(false);
180 586
        $this->hasOptionalArg = $builder->hasOptionalArgument(false);
181
182 586
        foreach ($this->options as $option) {
183 180
            if ($option->getShortName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $option->getShortName() of type null|string 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...
184 180
                $this->optionsByShortName[$option->getShortName()] = $option;
185
            }
186
        }
187
188 586
        foreach ($builder->getCommandOptions(false) as $commandOption) {
189 68
            $this->commandOptions[$commandOption->getLongName()] = $commandOption;
190
191 68
            if ($commandOption->getShortName()) {
192 50
                $this->commandOptionsByShortName[$commandOption->getShortName()] = $commandOption;
193
            }
194
195 68
            foreach ($commandOption->getLongAliases() as $longAlias) {
0 ignored issues
show
Bug introduced by
The method getLongAliases does only exist in Webmozart\Console\Api\Args\Format\CommandOption, but not in Webmozart\Console\Api\Args\Format\Option.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
196 2
                $this->commandOptions[$longAlias] = $commandOption;
197
            }
198
199 68
            foreach ($commandOption->getShortAliases() as $shortAlias) {
0 ignored issues
show
Bug introduced by
The method getShortAliases does only exist in Webmozart\Console\Api\Args\Format\CommandOption, but not in Webmozart\Console\Api\Args\Format\Option.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
200 68
                $this->commandOptionsByShortName[$shortAlias] = $commandOption;
201
            }
202
        }
203 586
    }
204
205
    /**
206
     * Returns the base format.
207
     *
208
     * @return ArgsFormat The base format.
209
     */
210 33
    public function getBaseFormat()
211
    {
212 33
        return $this->baseFormat;
213
    }
214
215
    /**
216
     * Returns the command names.
217
     *
218
     * @param bool $includeBase Whether to include command names in the base
219
     *                          format in the result.
220
     *
221
     * @return CommandName[] The command names.
222
     */
223 361 View Code Duplication
    public function getCommandNames($includeBase = true)
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...
224
    {
225 361
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
226
227 360
        $commandNames = $this->commandNames;
228
229 360
        if ($includeBase && $this->baseFormat) {
230 292
            $commandNames = array_merge($this->baseFormat->getCommandNames(), $commandNames);
231
        }
232
233 360
        return $commandNames;
234
    }
235
236
    /**
237
     * Returns whether the format contains any command names.
238
     *
239
     * @param bool $includeBase Whether to consider command names in the base
240
     *                          format.
241
     *
242
     * @return bool Returns `true` if the format contains command names and
243
     *              `false` otherwise.
244
     */
245 6
    public function hasCommandNames($includeBase = true)
246
    {
247 6
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
248
249 5
        if (count($this->commandNames) > 0) {
250 3
            return true;
251
        }
252
253 3
        if ($includeBase && $this->baseFormat) {
254 1
            return $this->baseFormat->hasCommandNames();
255
        }
256
257 3
        return false;
258
    }
259
260
    /**
261
     * Returns a command option by its long or short name.
262
     *
263
     * @param string $name        The long or short option name.
264
     * @param bool   $includeBase Whether to include options in the base format
265
     *                            in the search.
266
     *
267
     * @return CommandOption The command option.
268
     *
269
     * @throws NoSuchOptionException If the command option with the given name
270
     *                               does not not exist.
271
     */
272 15 View Code Duplication
    public function getCommandOption($name, $includeBase = true)
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...
273
    {
274 15
        Assert::string($name, 'The option name must be a string or an integer. Got: %s');
275 13
        Assert::notEmpty($name, 'The option name must not be empty.');
276 12
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
277
278 11
        if (isset($this->commandOptions[$name])) {
279 4
            return $this->commandOptions[$name];
280
        }
281
282 8
        if (isset($this->commandOptionsByShortName[$name])) {
283 4
            return $this->commandOptionsByShortName[$name];
284
        }
285
286 5
        if ($includeBase && $this->baseFormat) {
287 2
            return $this->baseFormat->getCommandOption($name);
288
        }
289
290 3
        throw NoSuchOptionException::forOptionName($name);
291
    }
292
293
    /**
294
     * Returns all command options of the format.
295
     *
296
     * @param bool $includeBase Whether to include options of the base format
297
     *                          in the result.
298
     *
299
     * @return CommandOption[] The command options.
300
     */
301 360 View Code Duplication
    public function getCommandOptions($includeBase = true)
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...
302
    {
303 360
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
304
305 359
        $commandOptions = array_values($this->commandOptions);
306
307 359
        if ($includeBase && $this->baseFormat) {
308
            // prepend base command options
309 290
            $commandOptions = array_merge($this->baseFormat->getCommandOptions(), $commandOptions);
310
        }
311
312 359
        return $commandOptions;
313
    }
314
315
    /**
316
     * Returns whether the format contains a specific command option.
317
     *
318
     * You can either pass the long or the short name of the command option.
319
     *
320
     * @param string $name        The long or short option name.
321
     * @param bool   $includeBase Whether to include options in the base format
322
     *                            in the search.
323
     *
324
     * @return bool Returns `true` if the command option with the given name
325
     *              could be found and `false` otherwise.
326
     */
327 154 View Code Duplication
    public function hasCommandOption($name, $includeBase = true)
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...
328
    {
329 154
        Assert::string($name, 'The option name must be a string or an integer. Got: %s');
330 152
        Assert::notEmpty($name, 'The option name must not be empty.');
331 151
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
332
333 150
        if (isset($this->commandOptions[$name]) || isset($this->commandOptionsByShortName[$name])) {
334 3
            return true;
335
        }
336
337 150
        if ($includeBase && $this->baseFormat) {
338 8
            return $this->baseFormat->hasCommandOption($name);
339
        }
340
341 150
        return false;
342
    }
343
344
    /**
345
     * Returns whether the format contains command options.
346
     *
347
     * @param bool $includeBase Whether to include options in the base format
348
     *                          in the search.
349
     *
350
     * @return bool Returns `true` if the format contains command options and
351
     *              `false` otherwise.
352
     */
353 5
    public function hasCommandOptions($includeBase = true)
354
    {
355 5
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
356
357 4
        if (count($this->commandOptions) > 0) {
358 3
            return true;
359
        }
360
361 3
        if ($includeBase && $this->baseFormat) {
362 1
            return $this->baseFormat->hasCommandOptions();
363
        }
364
365 3
        return false;
366
    }
367
368
    /**
369
     * Returns an argument by its name or position.
370
     *
371
     * You can either pass the name of the argument or the 0-based position of
372
     * the argument.
373
     *
374
     * @param string|int $name        The argument name or its 0-based position
375
     *                                in the argument list.
376
     * @param bool       $includeBase Whether to include arguments in the base
377
     *                                format in the search.
378
     *
379
     * @return Argument The argument.
380
     *
381
     * @throws NoSuchArgumentException If the argument with the given name or
382
     *                                 position does not exist.
383
     */
384 167 View Code Duplication
    public function getArgument($name, $includeBase = true)
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...
385
    {
386 167
        if (!is_int($name)) {
387 163
            Assert::string($name, 'The argument name must be a string or an integer. Got: %s');
388 161
            Assert::notEmpty($name, 'The argument name must not be empty.');
389
        }
390
391 164
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
392
393 163
        if (is_int($name)) {
394 6
            $arguments = array_values($this->getArguments($includeBase));
395
396 6
            if (!isset($arguments[$name])) {
397 6
                throw NoSuchArgumentException::forPosition($name);
398
            }
399
        } else {
400 159
            $arguments = $this->getArguments($includeBase);
401
402 159
            if (!isset($arguments[$name])) {
403 4
                throw NoSuchArgumentException::forArgumentName($name);
404
            }
405
        }
406
407 157
        return $arguments[$name];
408
    }
409
410
    /**
411
     * Returns all arguments of the format.
412
     *
413
     * @param bool $includeBase Whether to include arguments of the base format
414
     *                          in the result.
415
     *
416
     * @return Argument[] The arguments.
417
     */
418 433 View Code Duplication
    public function getArguments($includeBase = true)
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...
419
    {
420 433
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
421
422 432
        $arguments = $this->arguments;
423
424 432
        if ($includeBase && $this->baseFormat) {
425
            // prepend base arguments
426 301
            $arguments = array_replace($this->baseFormat->getArguments(), $arguments);
427
        }
428
429 432
        return $arguments;
430
    }
431
432
    /**
433
     * Returns whether the format contains a specific argument.
434
     *
435
     * You can either pass the name of the argument or the 0-based position of
436
     * the argument.
437
     *
438
     * @param string|int $name        The argument name or its 0-based position
439
     *                                in the argument list.
440
     * @param bool       $includeBase Whether to include arguments in the base
441
     *                                format in the search.
442
     *
443
     * @return bool Returns `true` if the argument with the given name or
444
     *              position could be found and `false` otherwise.
445
     */
446 321 View Code Duplication
    public function hasArgument($name, $includeBase = true)
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...
447
    {
448 321
        if (!is_int($name)) {
449 319
            Assert::string($name, 'The argument name must be a string or an integer. Got: %s');
450 317
            Assert::notEmpty($name, 'The argument name must not be empty.');
451
        }
452
453 318
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
454
455 317
        $arguments = is_int($name)
456 2
            ? array_values($this->getArguments($includeBase))
457 317
            : $this->getArguments($includeBase);
458
459 317
        return isset($arguments[$name]);
460
    }
461
462
    /**
463
     * Returns whether the format contains a multi-valued argument.
464
     *
465
     * @param bool $includeBase Whether to include arguments in the base format
466
     *                          in the search.
467
     *
468
     * @return bool Returns `true` if the format contains a multi-valued
469
     *              argument and `false` otherwise.
470
     */
471 129
    public function hasMultiValuedArgument($includeBase = true)
472
    {
473 129
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
474
475 128
        if ($this->hasMultiValuedArg) {
476 5
            return true;
477
        }
478
479 125
        if ($includeBase && $this->baseFormat) {
480 7
            return $this->baseFormat->hasMultiValuedArgument();
481
        }
482
483 125
        return false;
484
    }
485
486
    /**
487
     * Returns whether the format contains an optional argument.
488
     *
489
     * @param bool $includeBase Whether to include arguments in the base format
490
     *                          in the search.
491
     *
492
     * @return bool Returns `true` if the format contains an optional argument
493
     *              and `false` otherwise.
494
     */
495 10
    public function hasOptionalArgument($includeBase = true)
496
    {
497 10
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
498
499 9
        if ($this->hasOptionalArg) {
500 4
            return true;
501
        }
502
503 7
        if ($includeBase && $this->baseFormat) {
504 1
            return $this->baseFormat->hasOptionalArgument();
505
        }
506
507 7
        return false;
508
    }
509
510
    /**
511
     * Returns whether the format contains a required argument.
512
     *
513
     * @param bool $includeBase Whether to include arguments in the base format
514
     *                          in the search.
515
     *
516
     * @return bool Returns `true` if the format contains a required argument
517
     *              and `false` otherwise.
518
     */
519 5 View Code Duplication
    public function hasRequiredArgument($includeBase = true)
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...
520
    {
521 5
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
522
523 4
        if (!$this->hasOptionalArg && count($this->arguments) > 0) {
524 3
            return true;
525
        }
526
527 3
        if ($includeBase && $this->baseFormat) {
528 1
            return $this->baseFormat->hasRequiredArgument();
529
        }
530
531 3
        return false;
532
    }
533
534
    /**
535
     * Returns whether the format contains any argument.
536
     *
537
     * @param bool $includeBase Whether to include arguments in the base format
538
     *                          in the search.
539
     *
540
     * @return bool Returns `true` if the format contains any argument and
541
     *              `false` otherwise.
542
     */
543 32
    public function hasArguments($includeBase = true)
544
    {
545 32
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
546
547 31
        if (count($this->arguments) > 0) {
548 7
            return true;
549
        }
550
551 26
        if ($includeBase && $this->baseFormat) {
552 24
            return $this->baseFormat->hasArguments();
553
        }
554
555 26
        return false;
556
    }
557
558
    /**
559
     * Returns the number of arguments.
560
     *
561
     * @param bool $includeBase Whether to include arguments in the base format
562
     *                          in the result.
563
     *
564
     * @return int The number of arguments.
565
     */
566 6
    public function getNumberOfArguments($includeBase = true)
567
    {
568 6
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
569
570 5
        if ($this->hasMultiValuedArg) {
571
            return PHP_INT_MAX;
572
        }
573
574 5
        return count($this->getArguments($includeBase));
575
    }
576
577
    /**
578
     * Returns the number of required arguments.
579
     *
580
     * @param bool $includeBase Whether to include arguments in the base format
581
     *                          in the result.
582
     *
583
     * @return int The number of required arguments.
584
     */
585 6
    public function getNumberOfRequiredArguments($includeBase = true)
586
    {
587 6
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
588
589 5
        $arguments = $this->getArguments($includeBase);
590 5
        $count = 0;
591
592 5
        foreach ($arguments as $argument) {
593 4
            if (!$argument->isRequired()) {
594 4
                continue;
595
            }
596
597 2
            if ($argument->isMultiValued()) {
598
                return PHP_INT_MAX;
599
            }
600
601 2
            ++$count;
602
        }
603
604 5
        return $count;
605
    }
606
607
    /**
608
     * Returns an option by its long or short name.
609
     *
610
     * @param string $name        The long or short option name.
611
     * @param bool   $includeBase Whether to include options in the base format
612
     *                            in the search.
613
     *
614
     * @return Option The option.
615
     *
616
     * @throws NoSuchOptionException If the option with the given name does not
617
     *                               not exist.
618
     */
619 261 View Code Duplication
    public function getOption($name, $includeBase = true)
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...
620
    {
621 261
        Assert::string($name, 'The option name must be a string or an integer. Got: %s');
622 259
        Assert::notEmpty($name, 'The option name must not be empty.');
623 258
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
624
625 257
        if (isset($this->options[$name])) {
626 249
            return $this->options[$name];
627
        }
628
629 220
        if (isset($this->optionsByShortName[$name])) {
630 13
            return $this->optionsByShortName[$name];
631
        }
632
633 208
        if ($includeBase && $this->baseFormat) {
634 203
            return $this->baseFormat->getOption($name);
635
        }
636
637 5
        throw NoSuchOptionException::forOptionName($name);
638
    }
639
640
    /**
641
     * Returns all options of the format.
642
     *
643
     * @param bool $includeBase Whether to include options of the base format
644
     *                          in the result.
645
     *
646
     * @return Option[] The options.
647
     */
648 374 View Code Duplication
    public function getOptions($includeBase = true)
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...
649
    {
650 374
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
651
652 373
        $options = $this->options;
653
654 373
        if ($includeBase && $this->baseFormat) {
655
            // append base options
656 278
            $options = array_replace($options, $this->baseFormat->getOptions());
657
        }
658
659 373
        return $options;
660
    }
661
662
    /**
663
     * Returns whether the format contains a specific option.
664
     *
665
     * You can either pass the long or the short name of the option.
666
     *
667
     * @param string $name        The long or short option name.
668
     * @param bool   $includeBase Whether to include options in the base format
669
     *                            in the search.
670
     *
671
     * @return bool Returns `true` if the option with the given name could be
672
     *              found and `false` otherwise.
673
     */
674 330 View Code Duplication
    public function hasOption($name, $includeBase = true)
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...
675
    {
676 330
        Assert::string($name, 'The option name must be a string or an integer. Got: %s');
677 328
        Assert::notEmpty($name, 'The option name must not be empty.');
678 327
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
679
680 326
        if (isset($this->options[$name]) || isset($this->optionsByShortName[$name])) {
681 235
            return true;
682
        }
683
684 321
        if ($includeBase && $this->baseFormat) {
685 215
            return $this->baseFormat->hasOption($name);
686
        }
687
688 211
        return false;
689
    }
690
691
    /**
692
     * Returns whether the format contains options.
693
     *
694
     * @param bool $includeBase Whether to include options in the base format
695
     *                          in the search.
696
     *
697
     * @return bool Returns `true` if the format contains options and `false`
698
     *              otherwise.
699
     */
700 51
    public function hasOptions($includeBase = true)
701
    {
702 51
        Assert::boolean($includeBase, 'The parameter $includeBase must be a boolean. Got: %s');
703
704 50
        if (count($this->options) > 0) {
705 37
            return true;
706
        }
707
708 32
        if ($includeBase && $this->baseFormat) {
709 1
            return $this->baseFormat->hasOptions();
710
        }
711
712 32
        return false;
713
    }
714
715
    /**
716
     * Creates a format builder for a set of arguments and options.
717
     *
718
     * @param array      $elements   The arguments and options to add to the
719
     *                               builder.
720
     * @param ArgsFormat $baseFormat The base format.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $baseFormat not be null|ArgsFormat?

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...
721
     *
722
     * @return ArgsFormatBuilder The created builder.
723
     */
724 428
    private function createBuilderForElements(array $elements, ArgsFormat $baseFormat = null)
725
    {
726 428
        $builder = new ArgsFormatBuilder($baseFormat);
727
728 428
        foreach ($elements as $element) {
729 156
            if ($element instanceof CommandName) {
730 5
                $builder->addCommandName($element);
731
            } elseif ($element instanceof CommandOption) {
732 14
                $builder->addCommandOption($element);
733
            } elseif ($element instanceof Option) {
734 114
                $builder->addOption($element);
735
            } elseif ($element instanceof Argument) {
736 45
                $builder->addArgument($element);
737
            } else {
738 1
                throw new InvalidArgumentException(sprintf(
739
                    'Expected instances of CommandName, CommandOption, '.
740 1
                    'Option or Argument. Got: %s',
741 156
                    is_object($element) ? get_class($element) : gettype($element)
742
                ));
743
            }
744
        }
745
746 427
        return $builder;
747
    }
748
}
749