DefaultArgsParser::copyArgumentValues()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 64
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 64
ccs 17
cts 17
cp 1
rs 8.6346
cc 6
eloc 17
nc 6
nop 4
crap 6

How to fix   Long Method   

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\Args;
13
14
use RuntimeException;
15
use Symfony\Component\Console\Input\ArgvInput;
16
use Symfony\Component\Console\Input\InputArgument;
17
use Webmozart\Console\Adapter\ArgsFormatInputDefinition;
18
use Webmozart\Console\Api\Args\Args;
19
use Webmozart\Console\Api\Args\ArgsParser;
20
use Webmozart\Console\Api\Args\CannotParseArgsException;
21
use Webmozart\Console\Api\Args\Format\ArgsFormat;
22
use Webmozart\Console\Api\Args\RawArgs;
23
24
/**
25
 * Default parser for {@link RawArgs} instances.
26
 *
27
 * This parser delegates most of the work to Symfony's {@link ArgvInput} class.
28
 *
29
 * @since  1.0
30
 *
31
 * @author Bernhard Schussek <[email protected]>
32
 */
33
class DefaultArgsParser extends ArgvInput implements ArgsParser
34
{
35
    /**
36
     * Creates a new parser.
37
     */
38 307
    public function __construct()
39
    {
40
        // Hide the parent arguments from the public signature
41 307
        parent::__construct();
42 307
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 303
    public function parseArgs(RawArgs $args, ArgsFormat $format, $lenient = false)
48
    {
49 303
        $this->setTokens($args->getTokens());
50
51 303
        $formatAdapter = new ArgsFormatInputDefinition($format);
52
53
        try {
54 303
            $this->bind($formatAdapter);
55 13
        } catch (RuntimeException $e) {
56 13
            if (!$lenient) {
57 6
                throw new CannotParseArgsException($e->getMessage());
58
            }
59
        }
60
61
        // Prevent failing validation if not all command names are given
62 297
        $this->insertMissingCommandNames($formatAdapter, $lenient);
63
64
        try {
65 296
            $this->validate();
66 6
        } catch (RuntimeException $e) {
67 6
            if (!$lenient) {
68 3
                throw new CannotParseArgsException($e->getMessage());
69
            }
70
        }
71
72 293
        return $this->createArgs($format, $args);
73
    }
74
75
    /**
76
     * Creates the arguments from the current class state.
77
     *
78
     * @param ArgsFormat $format  The format.
79
     * @param RawArgs    $rawArgs The raw arguments.
80
     *
81
     * @return Args The created console arguments.
82
     */
83 293
    private function createArgs(ArgsFormat $format, RawArgs $rawArgs)
84
    {
85 293
        $args = new Args($format, $rawArgs);
86
87 293
        foreach ($this->arguments as $name => $value) {
88
            // Filter command names
89 290
            if ($format->hasArgument($name)) {
90 290
                $args->setArgument($name, $value);
91
            }
92
        }
93
94 293
        foreach ($this->options as $name => $value) {
95
            // Filter command options
96 235
            if ($format->hasOption($name)) {
97 235
                $args->setOption($name, $value);
98
            }
99
        }
100
101 293
        return $args;
102
    }
103
104 297
    private function insertMissingCommandNames(ArgsFormatInputDefinition $inputDefinition, $lenient = false)
105
    {
106
        // Start with the default values of the arguments.
107 297
        $inputArguments = $inputDefinition->getArguments();
108 297
        $fixedValues = array();
109 297
        $commandNames = $inputDefinition->getCommandNamesByArgumentName();
110
111
        // Flatten the actual arguments, in case they contain a multi-valued
112
        // argument.
113 297
        $actualValues = $this->flatten($this->arguments);
114
115
        // Reset all array pointers.
116 297
        reset($commandNames);
117 297
        reset($actualValues);
118 297
        reset($inputArguments);
119
120
        // Skip the command names. The resulting pointer is like this:
121
        //
122
        // actual: [ 0: remote, 1: origin, 2: foo/bar ]
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
123
        //                      ^
124
125 297
        $this->skipCommandNames($actualValues, $commandNames);
126
127
        // Copy the command names into the fixed array. The result is:
128
        //
129
        // fixed: [ cmd1: remote, cmd2: add ]
130
131 297
        $this->copyArgumentValues($commandNames, $inputArguments, $fixedValues, $lenient);
132
133
        // Copy the remaining actual values. The result is:
134
        //
135
        // fixed: [ cmd1: remote, cmd2: add, name: origin, target: foo/bar ]
136
137 297
        $this->copyArgumentValues($actualValues, $inputArguments, $fixedValues, $lenient);
138
139
        // Overwrite all current arguments with the fixed values
140 296
        foreach ($fixedValues as $name => $value) {
141 293
            $this->arguments[$name] = $value;
142
        }
143 296
    }
144
145 297
    private function flatten(array $arguments, array &$result = array())
146
    {
147 297
        foreach ($arguments as $value) {
148 238
            if (is_array($value)) {
149 3
                $this->flatten($value, $result);
150
            } else {
151 238
                $result[] = $value;
152
            }
153
        }
154
155 297
        return $result;
156
    }
157
158 297
    private function skipCommandNames(array &$arguments, array $commandNames)
159
    {
160 297
        reset($commandNames);
161
162 297
        while (null !== key($arguments) && null !== key($commandNames) && current($commandNames)->match(current($arguments))) {
163 227
            next($arguments);
164 227
            next($commandNames);
165
        }
166 297
    }
167
168 297
    private function copyArgumentValues(array &$actualValues, array &$inputArguments, array &$fixedValues, $lenient = false)
169
    {
170
        // The starting point are two arrays of arguments with the array
171
        // pointers set:
172
173
        // values: [ 0: remote, 1: origin, 2: foo/bar ]
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
174
        //                      ^
175
        // args:   [ cmd1: Argument, cmd2: Argument, name: Argument, target: Argument ]
176
        //                                                     ^
177
178
        // The fixed values may already contain values:
179
180
        // fixed: [ cmd1: remote, cmd2: add ]
181
182
        // The goal is to copy the actual values to the fixed array so that the
183
        // end result is:
184
185
        // fixed: [ cmd1: remote, cmd2: add, name: origin, target: foo/bar ]
186
187
        // Multi-valued arguments need special treatment. In this case, the
188
        // actual values are like this:
189
190
        // values: [ 0: remote, 1: one, 2: two, 3: three ]
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
191
        //                      ^
192
        // args:   [ cmd1: Argument, cmd2: Argument, multi: Argument ]
193
        //                                                     ^
194
195
        // The expected result is:
196
197
        // fixed: [ cmd1: remote, cmd2: add, multi: [ one, two, three ] ]
198
199 297
        while (null !== key($actualValues)) {
200 294
            if (null === key($inputArguments)) {
201 8
                if ($lenient) {
202 1
                    return;
203
                }
204
205 7
                throw new CannotParseArgsException('Too many arguments.');
206
            }
207
208
            /** @var InputArgument $argument */
209 294
            $argument = current($inputArguments);
210 294
            $name = $argument->getName();
211 294
            $value = current($actualValues);
212
213
            // Append the value to multi-valued arguments
214 294
            if ($argument->isArray()) {
215 3
                if (!isset($fixedValues[$name])) {
216 3
                    $fixedValues[$name] = array();
217
                }
218
219 3
                $fixedValues[$name][] = $value;
220
221
                // The multi-valued argument is the last one, so we don't
222
                // need to advance the array pointer anymore.
223
            } else {
224 294
                $fixedValues[$name] = $value;
225
226 294
                next($inputArguments);
227
            }
228
229 294
            next($actualValues);
230
        }
231 297
    }
232
}
233