Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
30 | class DefaultResolver implements CommandResolver |
||
31 | { |
||
32 | /** |
||
33 | * {@inheritdoc} |
||
34 | */ |
||
35 | 217 | public function resolveCommand(RawArgs $args, Application $application) |
|
61 | |||
62 | /** |
||
63 | * @param RawArgs $args |
||
64 | * @param CommandCollection $namedCommands |
||
65 | * @param string[] $argumentsToTest |
||
66 | * @param string[] $optionsToTest |
||
67 | * |
||
68 | * @return ResolveResult |
||
69 | */ |
||
70 | 217 | private function processArguments(RawArgs $args, CommandCollection $namedCommands, array $argumentsToTest, array $optionsToTest) |
|
71 | { |
||
72 | 217 | $currentCommand = null; |
|
73 | |||
74 | // Parse the arguments for command names until we fail to find a |
||
75 | // matching command |
||
76 | 217 | View Code Duplication | foreach ($argumentsToTest as $name) { |
77 | 200 | if (!$namedCommands->contains($name)) { |
|
78 | 16 | break; |
|
79 | } |
||
80 | |||
81 | 199 | $nextCommand = $namedCommands->get($name); |
|
82 | |||
83 | 199 | if ($nextCommand->getConfig() instanceof OptionCommandConfig) { |
|
84 | break; |
||
85 | } |
||
86 | |||
87 | 199 | $currentCommand = $nextCommand; |
|
88 | 199 | $namedCommands = $currentCommand->getNamedSubCommands(); |
|
89 | } |
||
90 | |||
91 | 217 | if (!$currentCommand) { |
|
92 | 18 | return null; |
|
93 | } |
||
94 | |||
95 | 199 | return $this->processOptions($args, $currentCommand, $optionsToTest); |
|
96 | } |
||
97 | |||
98 | /** |
||
99 | * @param RawArgs $args |
||
100 | * @param Command $currentCommand |
||
101 | * @param string[] $optionsToTest |
||
102 | * |
||
103 | * @return ResolveResult |
||
104 | */ |
||
105 | 199 | private function processOptions(RawArgs $args, Command $currentCommand, array $optionsToTest) |
|
106 | { |
||
107 | 199 | View Code Duplication | foreach ($optionsToTest as $option) { |
108 | 142 | $commands = $currentCommand->getNamedSubCommands(); |
|
109 | |||
110 | 142 | if (!$commands->contains($option)) { |
|
111 | 131 | continue; |
|
112 | } |
||
113 | |||
114 | 45 | $nextCommand = $commands->get($option); |
|
115 | |||
116 | 45 | if (!$nextCommand->getConfig() instanceof OptionCommandConfig) { |
|
117 | break; |
||
118 | } |
||
119 | |||
120 | 45 | $currentCommand = $nextCommand; |
|
121 | } |
||
122 | |||
123 | 199 | return $this->processDefaultSubCommands($args, $currentCommand); |
|
124 | } |
||
125 | |||
126 | /** |
||
127 | * @param RawArgs $args |
||
128 | * @param Command $currentCommand |
||
129 | * |
||
130 | * @return ResolveResult |
||
131 | */ |
||
132 | 199 | private function processDefaultSubCommands(RawArgs $args, Command $currentCommand) |
|
141 | |||
142 | /** |
||
143 | * @param RawArgs $args |
||
144 | * @param CommandCollection $defaultCommands |
||
145 | * |
||
146 | * @return ResolveResult |
||
147 | */ |
||
148 | 216 | private function processDefaultCommands(RawArgs $args, CommandCollection $defaultCommands) |
|
149 | { |
||
150 | 216 | $firstResult = null; |
|
151 | |||
152 | 216 | foreach ($defaultCommands as $defaultCommand) { |
|
153 | 59 | $resolvedCommand = new ResolveResult($defaultCommand, $args); |
|
154 | |||
155 | 59 | if ($resolvedCommand->isParsable()) { |
|
156 | 58 | return $resolvedCommand; |
|
157 | } |
||
158 | |||
159 | 7 | if (!$firstResult) { |
|
160 | 7 | $firstResult = $resolvedCommand; |
|
161 | } |
||
162 | } |
||
163 | |||
164 | // Return the first default command if one was found |
||
165 | 158 | return $firstResult; |
|
166 | } |
||
167 | |||
168 | 217 | private function getArgumentsToTest(array &$tokens) |
|
169 | { |
||
170 | 217 | $argumentsToTest = array(); |
|
171 | |||
172 | 217 | for (; null !== key($tokens); next($tokens)) { |
|
173 | 214 | $token = current($tokens); |
|
174 | |||
175 | // "--" stops argument parsing |
||
176 | 214 | if ('--' === $token) { |
|
177 | 3 | break; |
|
178 | } |
||
179 | |||
180 | // Stop argument parsing when we reach the first option. |
||
181 | |||
182 | // Command names must be passed before any option. The reason |
||
183 | // is that we cannot determine whether an argument after an |
||
184 | // option is the value of that option or an argument by itself |
||
185 | // without getting the input definition of the corresponding |
||
186 | // command first. |
||
187 | |||
188 | // For example, in the command "server -f add" we don't know |
||
189 | // whether "add" is the value of the "-f" option or an argument. |
||
190 | // Hence we stop argument parsing after "-f" and assume that |
||
191 | // "server" (or "server -f") is the command to execute. |
||
192 | 214 | if (isset($token[0]) && '-' === $token[0]) { |
|
193 | 173 | break; |
|
194 | } |
||
195 | |||
196 | 200 | $argumentsToTest[] = $token; |
|
197 | } |
||
198 | |||
199 | 217 | return $argumentsToTest; |
|
200 | } |
||
201 | |||
202 | 217 | private function getOptionsToTest(array &$tokens) |
|
203 | { |
||
204 | 217 | $optionsToTest = array(); |
|
205 | |||
206 | 217 | for (; null !== key($tokens); next($tokens)) { |
|
207 | 176 | $token = current($tokens); |
|
208 | |||
209 | // "--" stops option parsing |
||
210 | 176 | if ('--' === $token) { |
|
211 | 3 | break; |
|
212 | } |
||
213 | |||
214 | 173 | if (isset($token[0]) && '-' === $token[0]) { |
|
215 | 173 | if ('--' === substr($token, 0, 2) && strlen($token) > 2) { |
|
216 | 98 | $optionsToTest[] = substr($token, 2); |
|
217 | 97 | } elseif (2 === strlen($token)) { |
|
218 | 75 | $optionsToTest[] = substr($token, 1); |
|
219 | } |
||
220 | |||
221 | 173 | continue; |
|
222 | } |
||
223 | } |
||
224 | |||
225 | 217 | return $optionsToTest; |
|
226 | } |
||
227 | |||
228 | 216 | private function createResolvedCommand(ResolveResult $result) |
|
236 | } |
||
237 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.