ExtensionApiCommandController   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 344
Duplicated Lines 14.53 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 8
Bugs 1 Features 4
Metric Value
wmc 45
c 8
b 1
f 4
lcom 2
cbo 1
dl 50
loc 344
rs 8.3673

12 Methods

Rating   Name   Duplication   Size   Complexity  
A injectLogManager() 0 3 1
A initializeObject() 0 3 1
A emitPackagesMayHaveChangedSignal() 0 3 1
C infoCommand() 0 57 15
B listInstalledCommand() 0 23 6
A updateListCommand() 9 14 2
A installCommand() 15 15 2
A uninstallCommand() 14 14 2
C configureCommand() 0 40 8
A fetchCommand() 0 13 2
A listMirrorsCommand() 0 16 3
A importCommand() 12 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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
2
namespace Etobi\CoreAPI\Command;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2012 Georg Ringer <[email protected]>
8
 *  (c) 2014 Stefano Kowalke <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 2 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
use Exception;
28
use InvalidArgumentException;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Extbase\Mvc\Controller\CommandController;
31
32
/**
33
 * Extension API Command Controller
34
 *
35
 * @author Georg Ringer <[email protected]>
36
 * @author Stefano Kowalke <[email protected]>
37
 * @package Etobi\CoreAPI\Service\SiteApiService
38
 */
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) {
59
		$this->logManager = $logManager;
60
	}
61
62
	/**
63
	 * Initialize the object
64
	 */
65
	public function initializeObject() {
66
		$this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__);
67
	}
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) {
0 ignored issues
show
Complexity introduced by
This operation has 800 execution paths which exceeds the configured maximum of 200.

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.

Loading history...
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) {
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...
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) {
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...
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) {
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...
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) {
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...
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() {
380
		$this->signalSlotDispatcher->dispatch('PackageManagement', 'packagesMayHaveChanged');
381
	}
382
}
383