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:
Complex classes like ExtensionApiCommandController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ExtensionApiCommandController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
39 | class ExtensionApiCommandController extends CommandController { |
||
40 | |||
41 | const MAXIMUM_LINE_LENGTH = 79; |
||
42 | |||
43 | /** |
||
44 | * @var \TYPO3\CMS\Core\Log\LogManager $logManager |
||
45 | */ |
||
46 | protected $logManager; |
||
47 | |||
48 | /** |
||
49 | * @var \TYPO3\CMS\Core\Log\Logger $logger |
||
50 | */ |
||
51 | protected $logger; |
||
52 | |||
53 | /** |
||
54 | * @param \TYPO3\CMS\Core\Log\LogManager $logManager |
||
55 | * |
||
56 | * @return void |
||
57 | */ |
||
58 | public function injectLogManager(\TYPO3\CMS\Core\Log\LogManager $logManager) { |
||
61 | |||
62 | /** |
||
63 | * Initialize the object |
||
64 | */ |
||
65 | public function initializeObject() { |
||
68 | |||
69 | /** |
||
70 | * @var \Etobi\CoreAPI\Service\ExtensionApiService |
||
71 | * @inject |
||
72 | */ |
||
73 | protected $extensionApiService; |
||
74 | |||
75 | /** |
||
76 | * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher |
||
77 | * @inject |
||
78 | */ |
||
79 | protected $signalSlotDispatcher; |
||
80 | |||
81 | /** |
||
82 | * Information about an extension. |
||
83 | * |
||
84 | * @param string $key The extension key |
||
85 | * |
||
86 | * @return void |
||
87 | */ |
||
88 | public function infoCommand($key) { |
||
|
|||
89 | $data = array(); |
||
90 | try { |
||
91 | $data = $this->extensionApiService->getExtensionInformation($key); |
||
92 | } catch (Exception $e) { |
||
93 | $message = $e->getMessage(); |
||
94 | $this->outputLine($message); |
||
95 | $this->logger->error($message); |
||
96 | $this->quit(1); |
||
97 | } |
||
98 | |||
99 | $this->outputLine(''); |
||
100 | $this->outputLine('EXTENSION "%s": %s %s', array(strtoupper($key), $data['em_conf']['version'], $data['em_conf']['state'])); |
||
101 | $this->outputLine(str_repeat('-', $this->output->getMaximumLineLength())); |
||
102 | |||
103 | $outputInformation = array(); |
||
104 | $outputInformation['is installed'] = ($data['is_installed'] ? 'yes' : 'no'); |
||
105 | if (!empty($data['em_conf'])) { |
||
106 | foreach ($data['em_conf'] as $emConfKey => $emConfValue) { |
||
107 | // Skip empty properties |
||
108 | if (empty($emConfValue)) { |
||
109 | continue; |
||
110 | } |
||
111 | // Skip properties which are already handled |
||
112 | if ($emConfKey === 'title' || $emConfKey === 'version' || $emConfKey === 'state') { |
||
113 | continue; |
||
114 | } |
||
115 | $outputInformation[$emConfKey] = $emConfValue; |
||
116 | } |
||
117 | } |
||
118 | |||
119 | foreach ($outputInformation as $outputKey => $outputValue) { |
||
120 | $description = ''; |
||
121 | if (is_array($outputValue)) { |
||
122 | foreach ($outputValue as $additionalKey => $additionalValue) { |
||
123 | if (is_array($additionalValue)) { |
||
124 | |||
125 | if (empty($additionalValue)) { |
||
126 | continue; |
||
127 | } |
||
128 | $description .= LF . str_repeat(' ', 28) . $additionalKey; |
||
129 | $description .= LF; |
||
130 | foreach ($additionalValue as $ak => $av) { |
||
131 | $description .= str_repeat(' ', 30) . $ak . ': ' . $av . LF; |
||
132 | } |
||
133 | } else { |
||
134 | $description .= LF . str_repeat(' ', 28) . $additionalKey . ': ' . $additionalValue; |
||
135 | } |
||
136 | } |
||
137 | } else { |
||
138 | $description = wordwrap($outputValue, $this->output->getMaximumLineLength() - 28, PHP_EOL . str_repeat(' ', 28), TRUE); |
||
139 | } |
||
140 | $this->outputLine('%-2s%-25s %s', array(' ', $outputKey, $description)); |
||
141 | } |
||
142 | |||
143 | $this->logger->info('extensionApi:info executes successfully.'); |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * List all installed extensions. |
||
148 | * |
||
149 | * @param string $type Extension type, can either be "Local", |
||
150 | * "System" or "Global". Leave it empty for all |
||
151 | * |
||
152 | * @return void |
||
153 | */ |
||
154 | public function listInstalledCommand($type = '') { |
||
155 | $type = ucfirst(strtolower($type)); |
||
156 | if (!empty($type) && $type !== 'Local' && $type !== 'Global' && $type !== 'System') { |
||
157 | // TODO: Throw a exception here? |
||
158 | $message = 'Only "Local", "System" and "Global" are supported as type (or nothing)'; |
||
159 | $this->outputLine($message); |
||
160 | $this->logger->error($message); |
||
161 | $this->quit(1); |
||
162 | } |
||
163 | |||
164 | $extensions = $this->extensionApiService->listExtensions($type); |
||
165 | |||
166 | foreach ($extensions as $key => $details) { |
||
167 | $title = $key . ' - ' . $details['version'] . '/' . $details['state']; |
||
168 | $description = $details['title']; |
||
169 | $description = wordwrap($description, $this->output->getMaximumLineLength() - 43, PHP_EOL . str_repeat(' ', 43), TRUE); |
||
170 | $this->outputLine('%-2s%-40s %s', array(' ', $title, $description)); |
||
171 | } |
||
172 | |||
173 | $this->outputLine('%-2s%-40s', array(' ', str_repeat('-', $this->output->getMaximumLineLength() - 3))); |
||
174 | $this->outputLine(' Total: ' . count($extensions) . ' extensions'); |
||
175 | $this->logger->info('extensionApi:listInstalled executed successfully'); |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Update list. |
||
180 | * |
||
181 | * @return void |
||
182 | */ |
||
183 | public function updateListCommand() { |
||
184 | $this->outputLine('This may take a while...'); |
||
185 | $result = $this->extensionApiService->updateMirrors(); |
||
186 | |||
187 | View Code Duplication | if ($result) { |
|
188 | $message = 'Extension list has been updated.'; |
||
189 | $this->outputLine($message); |
||
190 | $this->logger->info($message); |
||
191 | } else { |
||
192 | $message = 'Extension list already up-to-date.'; |
||
193 | $this->outputLine($message); |
||
194 | $this->logger->info($message); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * Install(activate) an extension. |
||
200 | * |
||
201 | * @param string $key The extension key |
||
202 | * |
||
203 | * @return void |
||
204 | */ |
||
205 | View Code Duplication | public function installCommand($key) { |
|
206 | try { |
||
207 | $this->emitPackagesMayHaveChangedSignal(); |
||
208 | $this->extensionApiService->installExtension($key); |
||
209 | } catch (Exception $e) { |
||
210 | $message = $e->getMessage(); |
||
211 | $this->outputLine($message); |
||
212 | $this->logger->error($message); |
||
213 | $this->quit(1); |
||
214 | } |
||
215 | |||
216 | $message = sprintf('Extension "%s" is now installed!', $key); |
||
217 | $this->outputLine($message); |
||
218 | $this->logger->info($message); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * UnInstall(deactivate) an extension. |
||
223 | * |
||
224 | * @param string $key The extension key |
||
225 | * |
||
226 | * @return void |
||
227 | */ |
||
228 | View Code Duplication | public function uninstallCommand($key) { |
|
229 | try { |
||
230 | $this->extensionApiService->uninstallExtension($key); |
||
231 | } catch (Exception $e) { |
||
232 | $message = $e->getMessage(); |
||
233 | $this->outputLine($message); |
||
234 | $this->logger->error($message); |
||
235 | $this->quit(1); |
||
236 | } |
||
237 | |||
238 | $message = sprintf('Extension "%s" is now uninstalled!', $key); |
||
239 | $this->outputLine($message); |
||
240 | $this->logger->info($message); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Configure an extension. |
||
245 | * This command enables you to configure an extension. |
||
246 | * |
||
247 | * <code> |
||
248 | * [1] Using a standard formatted ini-file |
||
249 | * ./cli_dispatch.phpsh extbase extensionapi:configure rtehtmlarea --configfile=C:\rteconf.txt |
||
250 | * |
||
251 | * [2] Adding configuration settings directly on the command line |
||
252 | * ./cli_dispatch.phpsh extbase extensionapi:configure rtehtmlarea --settings="enableImages=1;allowStyleAttribute=0" |
||
253 | * |
||
254 | * [3] A combination of [1] and [2] |
||
255 | * ./cli_dispatch.phpsh extbase extensionapi:configure rtehtmlarea --configfile=C:\rteconf.txt --settings="enableImages=1;allowStyleAttribute=0" |
||
256 | * </code> |
||
257 | * |
||
258 | * @param string $key The extension key |
||
259 | * @param string $configFile Path to file containing configuration settings. Must be formatted as a standard ini-file |
||
260 | * @param string $settings String containing configuration settings separated on the form "k1=v1;k2=v2;" |
||
261 | * |
||
262 | * @return void |
||
263 | */ |
||
264 | public function configureCommand($key, $configFile = '', $settings = '') { |
||
265 | try { |
||
266 | $conf = array(); |
||
267 | if (is_file($configFile)) { |
||
268 | $conf = parse_ini_file($configFile); |
||
269 | } |
||
270 | |||
271 | if (strlen($settings)) { |
||
272 | $arr = explode(';', $settings); |
||
273 | foreach ($arr as $v) { |
||
274 | if (strpos($v, '=') === FALSE) { |
||
275 | throw new InvalidArgumentException(sprintf('Ill-formed setting "%s"!', $v)); |
||
276 | } |
||
277 | $parts = GeneralUtility::trimExplode('=', $v, FALSE, 2); |
||
278 | if (!empty($parts[0])) { |
||
279 | $conf[$parts[0]] = $parts[1]; |
||
280 | } |
||
281 | } |
||
282 | } |
||
283 | |||
284 | if (empty($conf)) { |
||
285 | $this->response->setExitCode(1); |
||
286 | $message = sprintf('No configuration settings!', $key); |
||
287 | $this->logger->error($message); |
||
288 | throw new InvalidArgumentException($message); |
||
289 | } |
||
290 | |||
291 | $this->extensionApiService->configureExtension($key, $conf); |
||
292 | |||
293 | } catch (Exception $e) { |
||
294 | $message = $e->getMessage(); |
||
295 | $this->outputLine($message); |
||
296 | $this->logger->error($message); |
||
297 | $this->quit(1); |
||
298 | } |
||
299 | |||
300 | $message = sprintf('Extension "%s" has been configured!', $key); |
||
301 | $this->outputLine($message); |
||
302 | $this->logger->info($message); |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Fetch an extension from TER. |
||
307 | * |
||
308 | * @param string $key The extension key |
||
309 | * @param string $version The exact version of the extension, otherwise the latest will be picked |
||
310 | * @param string $location Where to put the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext |
||
311 | * @param bool $overwrite Overwrite the extension if already exists |
||
312 | * @param int $mirror Mirror to fetch the extension from. Run extensionapi:listmirrors to get the list of all available repositories, otherwise a random mirror will be selected |
||
313 | * |
||
314 | * @return void |
||
315 | */ |
||
316 | public function fetchCommand($key, $version = '', $location = 'Local', $overwrite = FALSE, $mirror = -1) { |
||
317 | try { |
||
318 | $data = $this->extensionApiService->fetchExtension($key, $version, $location, $overwrite, $mirror); |
||
319 | $message = sprintf('Extension "%s" version %s has been fetched from repository! Dependencies were not resolved.', $data['main']['extKey'], $data['main']['version']); |
||
320 | $this->outputLine($message); |
||
321 | $this->logger->info($message); |
||
322 | } catch (Exception $e) { |
||
323 | $message = $e->getMessage(); |
||
324 | $this->outputLine($message); |
||
325 | $this->logger->error($message); |
||
326 | $this->quit(1); |
||
327 | } |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Lists the possible mirrors |
||
332 | * |
||
333 | * @return void |
||
334 | */ |
||
335 | public function listMirrorsCommand() { |
||
336 | try { |
||
337 | $mirrors = $this->extensionApiService->listMirrors(); |
||
338 | $key = 0; |
||
339 | foreach ($mirrors as $mirror) { |
||
340 | $this->outputLine($key . ' = ' . $mirror['title'] . ' ' . $mirror['host']); |
||
341 | ++$key; |
||
342 | } |
||
343 | } catch (Exception $e) { |
||
344 | $message = $e->getMessage(); |
||
345 | $this->outputLine($message); |
||
346 | $this->logger->error($message); |
||
347 | $this->quit(1); |
||
348 | } |
||
349 | $this->logger->info('extensionApi:listMirrors executed successfully.'); |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * Import extension from file. |
||
354 | * |
||
355 | * @param string $file Path to t3x file |
||
356 | * @param string $location Where to import the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext |
||
357 | * @param boolean $overwrite Overwrite the extension if already exists |
||
358 | * |
||
359 | * @return void |
||
360 | */ |
||
361 | View Code Duplication | public function importCommand($file, $location = 'Local', $overwrite = FALSE) { |
|
362 | try { |
||
363 | $data = $this->extensionApiService->importExtension($file, $location, $overwrite); |
||
364 | $message = sprintf('Extension "%s" has been imported!', $data['extKey']); |
||
365 | $this->outputLine($message); |
||
366 | $this->logger->info($message); |
||
367 | } catch (Exception $e) { |
||
368 | $this->outputLine($e->getMessage()); |
||
369 | $this->logger->error($e->getMessage()); |
||
370 | $this->quit(1); |
||
371 | } |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * Emits packages may have changed signal |
||
376 | * |
||
377 | * @return \Etobi\CoreAPI\Service\ExtensionApiService object |
||
378 | */ |
||
379 | protected function emitPackagesMayHaveChangedSignal() { |
||
382 | } |
||
383 |
A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.
You can also find more information in the “Code” section of your repository.