Complex classes like ExtensionApiService 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 ExtensionApiService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
49 | class ExtensionApiService { |
||
|
|||
50 | |||
51 | /** |
||
52 | * @var \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terConnection |
||
53 | */ |
||
54 | public $terConnection; |
||
55 | |||
56 | /** |
||
57 | * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager |
||
58 | */ |
||
59 | protected $configurationManager; |
||
60 | |||
61 | /** |
||
62 | * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility $extensionList |
||
63 | */ |
||
64 | public $listUtility; |
||
65 | |||
66 | /** |
||
67 | * @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility |
||
68 | */ |
||
69 | protected $installUtility; |
||
70 | |||
71 | /** |
||
72 | * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository $repositoryRepository |
||
73 | */ |
||
74 | protected $repositoryRepository; |
||
75 | |||
76 | /** |
||
77 | * @var Helper $repositoryHelper |
||
78 | */ |
||
79 | protected $repositoryHelper; |
||
80 | |||
81 | /** |
||
82 | * @var ExtensionRepository $extensionRepository |
||
83 | */ |
||
84 | protected $extensionRepository; |
||
85 | |||
86 | /** |
||
87 | * @var ExtensionManagementService $extensionManagementService |
||
88 | */ |
||
89 | protected $extensionManagementService; |
||
90 | |||
91 | /** |
||
92 | * @var ObjectManagerInterface $objectManager |
||
93 | */ |
||
94 | protected $objectManager; |
||
95 | |||
96 | /** |
||
97 | * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility |
||
98 | */ |
||
99 | protected $fileHandlingUtility; |
||
100 | |||
101 | /** |
||
102 | * @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility |
||
103 | */ |
||
104 | protected $emConfUtility; |
||
105 | |||
106 | /** |
||
107 | * @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility |
||
108 | * |
||
109 | * @return void |
||
110 | */ |
||
111 | public function injectFileHandlingUtility(FileHandlingUtility $fileHandlingUtility) { |
||
114 | |||
115 | /** |
||
116 | * @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility |
||
117 | * |
||
118 | * @return void |
||
119 | */ |
||
120 | public function injectInstallUtility(InstallUtility $installUtility) { |
||
123 | |||
124 | /** |
||
125 | * @param RepositoryRepository $repositoryRepository |
||
126 | * |
||
127 | * @return void |
||
128 | */ |
||
129 | public function injectRepositoryRepository(RepositoryRepository $repositoryRepository) { |
||
132 | |||
133 | /** |
||
134 | * @param Helper $repositoryHelper |
||
135 | * |
||
136 | * @return void |
||
137 | */ |
||
138 | public function injectRepositoryHelper(Helper $repositoryHelper) { |
||
141 | |||
142 | /** |
||
143 | * @param ExtensionRepository $extensionRepository |
||
144 | * |
||
145 | * @return void |
||
146 | */ |
||
147 | public function injectExtensionRepository(ExtensionRepository $extensionRepository){ |
||
150 | |||
151 | /** |
||
152 | * @param ExtensionManagementService $extensionManagementService |
||
153 | * |
||
154 | * @return void |
||
155 | */ |
||
156 | public function injectExtensionManagementService(ExtensionManagementService $extensionManagementService) { |
||
159 | |||
160 | /** |
||
161 | * @param ObjectManagerInterface $objectManager |
||
162 | * |
||
163 | * @return void |
||
164 | */ |
||
165 | public function injectObjectManager(ObjectManagerInterface $objectManager) { |
||
168 | |||
169 | /** |
||
170 | * The constructor |
||
171 | */ |
||
172 | public function __construct() { |
||
175 | |||
176 | /** |
||
177 | * Get information about an extension. |
||
178 | * |
||
179 | * @param string $extensionKey extension key |
||
180 | * |
||
181 | * @throws InvalidArgumentException |
||
182 | * @return array |
||
183 | */ |
||
184 | 3 | public function getExtensionInformation($extensionKey) { |
|
185 | 3 | if (strlen($extensionKey) === 0) { |
|
186 | 1 | throw new InvalidArgumentException('No extension key given!'); |
|
187 | } |
||
188 | |||
189 | 2 | $this->checkExtensionExists($extensionKey); |
|
190 | |||
191 | 1 | $extensions = $this->listExtensions(); |
|
192 | |||
193 | $information = array( |
||
194 | 1 | 'em_conf' => $extensions[$extensionKey], |
|
195 | 1 | 'is_installed' => $this->installUtility->isLoaded($extensionKey) |
|
196 | 1 | ); |
|
197 | |||
198 | 1 | return $information; |
|
199 | } |
||
200 | |||
201 | /** |
||
202 | * Get array of installed extensions. |
||
203 | * |
||
204 | * @param string $type Local, System, Global or empty (for all) |
||
205 | * |
||
206 | * @throws InvalidArgumentException |
||
207 | * @return array |
||
208 | */ |
||
209 | 4 | public function listExtensions($type = '') { |
|
210 | 4 | $type = ucfirst(strtolower($type)); |
|
211 | 4 | if (!empty($type) && $type !== 'Local' && $type !== 'Global' && $type !== 'System') { |
|
212 | 1 | throw new InvalidArgumentException('Only "Local", "System", "Global" and "" (all) are supported as type'); |
|
213 | } |
||
214 | |||
215 | 3 | $this->initializeExtensionManagerObjects(); |
|
216 | |||
217 | // TODO: Make listUtlity local var |
||
218 | 3 | $extensions = $this->listUtility->getAvailableExtensions(); |
|
219 | |||
220 | 3 | $list = array(); |
|
221 | |||
222 | 3 | foreach ($extensions as $key => $extension) { |
|
223 | 3 | if ((!empty($type) && $type !== $extension['type']) |
|
224 | 3 | || (!$this->installUtility->isLoaded($extension['key'])) |
|
225 | 3 | ) { |
|
226 | 3 | continue; |
|
227 | } |
||
228 | |||
229 | // TODO: Make emConfUtility local var |
||
230 | 3 | $configuration = $this->emConfUtility->includeEmConf($extension); |
|
231 | 3 | if (!empty($configuration)) { |
|
232 | 3 | $list[$key] = $configuration; |
|
233 | 3 | } |
|
234 | 3 | } |
|
235 | 3 | ksort($list); |
|
236 | |||
237 | 3 | return $list; |
|
238 | } |
||
239 | |||
240 | /** |
||
241 | * Update the mirrors, using the scheduler task of EXT:em. |
||
242 | * |
||
243 | * @throws RuntimeException |
||
244 | * @return boolean |
||
245 | */ |
||
246 | 2 | public function updateMirrors() { |
|
247 | 2 | $result = FALSE; |
|
248 | 2 | $repositories = $this->repositoryRepository->findAll(); |
|
249 | |||
250 | // update all repositories |
||
251 | 2 | foreach ($repositories as $repository) { |
|
252 | 2 | $this->repositoryHelper->setRepository($repository); |
|
253 | 2 | $result = $this->repositoryHelper->updateExtList(); |
|
254 | 2 | unset($objRepository, $this->repositoryHelper); |
|
255 | 2 | } |
|
256 | |||
257 | 2 | return $result; |
|
258 | } |
||
259 | |||
260 | /** |
||
261 | * Install (load) an extension. |
||
262 | * |
||
263 | * @param string $extensionKey extension key |
||
264 | * |
||
265 | * @throws RuntimeException |
||
266 | * @throws InvalidArgumentException |
||
267 | * @return void |
||
268 | */ |
||
269 | 2 | public function installExtension($extensionKey) { |
|
270 | 2 | $this->checkExtensionExists($extensionKey); |
|
271 | |||
272 | 1 | $this->installUtility->install($extensionKey); |
|
273 | 1 | } |
|
274 | |||
275 | /** |
||
276 | * Uninstall (unload) an extension. |
||
277 | * |
||
278 | * @param string $extensionKey extension key |
||
279 | * |
||
280 | * @throws RuntimeException |
||
281 | * @throws InvalidArgumentException |
||
282 | * @return void |
||
283 | */ |
||
284 | 3 | public function uninstallExtension($extensionKey) { |
|
285 | 3 | if ($extensionKey === 'coreapi') { |
|
286 | 1 | throw new InvalidArgumentException('Extension "coreapi" cannot be uninstalled!'); |
|
287 | } |
||
288 | |||
289 | 2 | $this->checkExtensionExists($extensionKey); |
|
290 | 1 | $this->checkExtensionLoaded($extensionKey); |
|
291 | |||
292 | 1 | $this->installUtility->uninstall($extensionKey); |
|
293 | 1 | } |
|
294 | |||
295 | |||
296 | /** |
||
297 | * Configure an extension. |
||
298 | * |
||
299 | * @param string $extensionKey The extension key |
||
300 | * @param array $newExtensionConfiguration |
||
301 | * |
||
302 | * @throws RuntimeException |
||
303 | * @throws InvalidArgumentException |
||
304 | * @return void |
||
305 | */ |
||
306 | 8 | public function configureExtension($extensionKey, $newExtensionConfiguration = array()) { |
|
307 | 8 | $this->checkExtensionExists($extensionKey); |
|
308 | 7 | $this->checkExtensionLoaded($extensionKey); |
|
309 | |||
310 | // checks if conf array is empty |
||
311 | 6 | if (empty($newExtensionConfiguration)) { |
|
312 | 1 | throw new InvalidArgumentException(sprintf('No configuration provided for extension "%s"!', $extensionKey)); |
|
313 | } |
||
314 | |||
315 | // check if extension can be configured |
||
316 | 5 | $extAbsPath = $this->getExtensionPath($extensionKey); |
|
317 | 5 | $extConfTemplateFile = $extAbsPath . 'ext_conf_template.txt'; |
|
318 | 5 | if (!file_exists($extConfTemplateFile)) { |
|
319 | 1 | throw new InvalidArgumentException(sprintf('Extension "%s" has no configuration options!', $extensionKey)); |
|
320 | } |
||
321 | |||
322 | /** @var $configurationUtility \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility */ |
||
323 | 4 | $configurationUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ConfigurationUtility'); |
|
324 | // get existing configuration |
||
325 | 4 | $currentExtensionConfig = $configurationUtility->getCurrentConfiguration($extensionKey); |
|
326 | |||
327 | // check for unknown configuration settings |
||
328 | 4 | foreach ($newExtensionConfiguration as $key => $_) { |
|
329 | 4 | if (!isset($currentExtensionConfig[$key])) { |
|
330 | 1 | throw new InvalidArgumentException(sprintf('No configuration setting with name "%s" for extension "%s"!', $key, $extensionKey)); |
|
331 | } |
||
332 | 3 | } |
|
333 | |||
334 | // fill with missing values |
||
335 | 3 | $newExtensionConfiguration = $this->mergeNewExtensionConfiguratonWithCurrentConfiguration( |
|
336 | 3 | $newExtensionConfiguration, |
|
337 | $currentExtensionConfig |
||
338 | 3 | ); |
|
339 | |||
340 | // write configuration to typo3conf/LocalConfiguration.php |
||
341 | 3 | $configurationUtility->writeConfiguration($newExtensionConfiguration, $extensionKey); |
|
342 | 3 | } |
|
343 | |||
344 | /** |
||
345 | * Fetch an extension from TER. |
||
346 | * |
||
347 | * @param string $extensionKey The extension key |
||
348 | * @param string $location Where to import the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext |
||
349 | * @param bool $overwrite Overwrite the extension if it already exists |
||
350 | * @param int $mirror The mirror to fetch the extension from |
||
351 | * |
||
352 | * @throws \RuntimeException |
||
353 | * @throws \InvalidArgumentException |
||
354 | * @return array |
||
355 | */ |
||
356 | 13 | public function fetchExtension($extensionKey, $version = '', $location = 'Local', $overwrite = FALSE, $mirror = -1) { |
|
357 | 13 | if (!is_numeric($mirror)) { |
|
358 | 1 | throw new InvalidArgumentException('Option --mirror must be a number. Run the command extensionapi:listmirrors to get the list of all available repositories'); |
|
359 | } |
||
360 | |||
361 | 12 | if ($version === '') { |
|
362 | 6 | $extension = $this->extensionRepository->findHighestAvailableVersion($extensionKey); |
|
363 | 6 | if ($extension === NULL) { |
|
364 | 1 | throw new InvalidArgumentException(sprintf('Extension "%s" was not found on TER', $extensionKey)); |
|
365 | } |
||
366 | 5 | } else { |
|
367 | 6 | $extension = $this->extensionRepository->findOneByExtensionKeyAndVersion($extensionKey, $version); |
|
368 | 6 | if ($extension === NULL) { |
|
369 | 1 | throw new InvalidArgumentException(sprintf('Version %s of extension "%s" does not exist', $version, $extensionKey)); |
|
370 | } |
||
371 | } |
||
372 | |||
373 | 10 | if (!$overwrite) { |
|
374 | 10 | $comingExtPath = $this->fileHandlingUtility->getExtensionDir($extensionKey, $location); |
|
375 | 10 | if (@is_dir($comingExtPath)) { |
|
376 | 1 | throw new InvalidArgumentException(sprintf('Extension "%s" already exists at "%s"!', $extensionKey, $comingExtPath)); |
|
377 | } |
||
378 | 9 | } |
|
379 | |||
380 | 9 | $mirrors = $this->repositoryHelper->getMirrors(); |
|
381 | |||
382 | 9 | if ($mirrors === NULL) { |
|
383 | 1 | throw new RuntimeException('No mirrors found!'); |
|
384 | } |
||
385 | |||
386 | 8 | if ($mirror === -1) { |
|
387 | 6 | $mirrors->setSelect(); |
|
388 | 8 | } elseif ($mirror > 0 && $mirror <= count($mirrors->getMirrors())) { |
|
389 | 1 | $mirrors->setSelect($mirror); |
|
390 | 1 | } else { |
|
391 | 1 | throw new InvalidArgumentException(sprintf('Mirror "%s" does not exist', $mirror)); |
|
392 | } |
||
393 | |||
394 | /** |
||
395 | * @var \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility |
||
396 | */ |
||
397 | 7 | $downloadUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\DownloadUtility'); |
|
398 | 7 | $downloadUtility->setDownloadPath($location); |
|
399 | |||
400 | 3 | $this->extensionManagementService->downloadMainExtension($extension); |
|
401 | |||
402 | 3 | $return = array(); |
|
403 | 3 | $extensionDir = $this->fileHandlingUtility->getExtensionDir($extensionKey, $location); |
|
404 | 3 | if (is_dir($extensionDir)) { |
|
405 | 3 | $return['main']['extKey'] = $extension->getExtensionKey(); |
|
406 | 3 | $return['main']['version'] = $extension->getVersion(); |
|
407 | 3 | } else { |
|
408 | throw new RuntimeException( |
||
409 | sprintf('Extension "%s" version %s could not installed!', $extensionKey, $extension->getVersion()) |
||
410 | ); |
||
411 | } |
||
412 | |||
413 | 3 | return $return; |
|
414 | } |
||
415 | |||
416 | /** |
||
417 | * Lists the possible mirrors |
||
418 | * |
||
419 | * @return array |
||
420 | */ |
||
421 | 1 | public function listMirrors() { |
|
422 | /** @var $repositoryHelper Helper */ |
||
423 | 1 | $repositoryHelper = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\Repository\\Helper'); |
|
424 | 1 | $mirrors = $repositoryHelper->getMirrors(); |
|
425 | |||
426 | 1 | return $mirrors->getMirrors(); |
|
427 | } |
||
428 | |||
429 | /** |
||
430 | * Extracts and returns the file content of the given file |
||
431 | * |
||
432 | * @param string $file The file with file path |
||
433 | * |
||
434 | * @return array |
||
435 | */ |
||
436 | protected function getFileContentFromUrl($file) { |
||
439 | |||
440 | /** |
||
441 | * Imports extension from file. |
||
442 | * |
||
443 | * @param string $file Path to t3x file |
||
444 | * @param string $location Where to import the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext |
||
445 | * @param bool $overwrite Overwrite the extension if it already exists |
||
446 | * |
||
447 | * @throws \RuntimeException |
||
448 | * @throws \InvalidArgumentException |
||
449 | * @return array The extension data |
||
450 | */ |
||
451 | 8 | public function importExtension($file, $location = 'Local', $overwrite = FALSE) { |
|
452 | 8 | if (!is_file($file)) { |
|
453 | 1 | throw new InvalidArgumentException(sprintf('File "%s" does not exist!', $file)); |
|
454 | } |
||
455 | |||
456 | 7 | $this->checkInstallLocation($location); |
|
457 | |||
458 | 6 | $uploadExtensionFileController = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Controller\\UploadExtensionFileController'); |
|
459 | |||
460 | 6 | $filename = pathinfo($file, PATHINFO_BASENAME); |
|
461 | 6 | $return = $uploadExtensionFileController->extractExtensionFromFile($file, $filename, $overwrite, FALSE); |
|
462 | |||
463 | 2 | return $return; |
|
464 | } |
||
465 | |||
466 | /** |
||
467 | * Checks if the function exists in the system |
||
468 | * |
||
469 | * @param string $extensionKey The extension key |
||
470 | * |
||
471 | * @return void |
||
472 | * @throws \InvalidArgumentException |
||
473 | */ |
||
474 | protected function checkExtensionExists($extensionKey) { |
||
475 | if (!$this->installUtility->isAvailable($extensionKey)) { |
||
476 | throw new InvalidArgumentException(sprintf('Extension "%s" does not exist!', $extensionKey)); |
||
477 | } |
||
478 | } |
||
479 | |||
480 | /** |
||
481 | * Check if an extension is loaded. |
||
482 | * |
||
483 | * @param string $extensionKey The extension key |
||
484 | * |
||
485 | * @throws \InvalidArgumentException |
||
486 | * @return void |
||
487 | */ |
||
488 | protected function checkExtensionLoaded($extensionKey) { |
||
489 | if (!$this->installUtility->isLoaded($extensionKey)) { |
||
490 | throw new InvalidArgumentException(sprintf('Extension "%s" is not installed!', $extensionKey)); |
||
491 | } |
||
492 | } |
||
493 | |||
494 | /** |
||
495 | * Returns the absolute extension path. |
||
496 | * Wrapper around the static method. This makes the method mockable. |
||
497 | * |
||
498 | * @param string $extensionKey The extension key |
||
499 | * |
||
500 | * @return string |
||
501 | */ |
||
502 | protected function getExtensionPath($extensionKey) { |
||
505 | |||
506 | /** |
||
507 | * Add missing values from current configuration to the new configuration |
||
508 | * |
||
509 | * @param array $newExtensionConfiguration The new configuration which was provided as argument |
||
510 | * @param array $currentExtensionConfig The current configuration of the extension |
||
511 | * |
||
512 | * @return array The merged configuration |
||
513 | */ |
||
514 | protected function mergeNewExtensionConfiguratonWithCurrentConfiguration($newExtensionConfiguration, $currentExtensionConfig) { |
||
527 | |||
528 | /** |
||
529 | * Checks if the extension is able to install at the demanded location |
||
530 | * |
||
531 | * @param string $location The location |
||
532 | * @param array $allowedInstallTypes The allowed locations |
||
533 | * |
||
534 | * @return boolean |
||
535 | * @throws \InvalidArgumentException |
||
536 | */ |
||
537 | protected function checkInstallLocation($location) { |
||
538 | $allowedInstallTypes = Extension::returnAllowedInstallTypes(); |
||
539 | $location = ucfirst(strtolower($location)); |
||
540 | |||
541 | if (!in_array($location, $allowedInstallTypes)) { |
||
542 | if ($location === 'Global') { |
||
543 | throw new InvalidArgumentException('Global installation is not allowed!'); |
||
544 | } |
||
545 | if ($location === 'Local') { |
||
546 | throw new InvalidArgumentException('Local installation is not allowed!'); |
||
547 | } |
||
548 | if ($location === 'System') { |
||
549 | throw new InvalidArgumentException('System installation is not allowed!'); |
||
550 | } |
||
551 | throw new InvalidArgumentException(sprintf('Unknown location "%s"!', $location)); |
||
552 | } |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Initialize ExtensionManager Objects. |
||
557 | * |
||
558 | * @return void |
||
559 | */ |
||
560 | protected function initializeExtensionManagerObjects() { |
||
564 | |||
565 | /** |
||
566 | * Clear the caches. |
||
567 | * |
||
568 | * @return void |
||
569 | */ |
||
570 | protected function clearCaches() { |
||
571 | $cacheApiService = GeneralUtility::makeInstance('Etobi\\CoreAPI\\Service\\CacheApiService'); |
||
572 | $cacheApiService->initializeObject(); |
||
573 | $cacheApiService->clearAllCaches(); |
||
574 | } |
||
575 | } |
||
576 |
The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.
Some resources for further reading:
You can also find more detailed suggestions for refactoring in the “Code” section of your repository.