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 OC_App 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 OC_App, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
63 | class OC_App { |
||
|
|||
64 | static private $appVersion = []; |
||
65 | static private $adminForms = array(); |
||
66 | static private $personalForms = array(); |
||
67 | static private $appInfo = array(); |
||
68 | static private $appTypes = array(); |
||
69 | static private $loadedApps = array(); |
||
70 | static private $altLogin = array(); |
||
71 | static private $alreadyRegistered = []; |
||
72 | const officialApp = 200; |
||
73 | |||
74 | /** |
||
75 | * clean the appId |
||
76 | * |
||
77 | * @param string|boolean $app AppId that needs to be cleaned |
||
78 | * @return string |
||
79 | */ |
||
80 | public static function cleanAppId($app) { |
||
83 | |||
84 | /** |
||
85 | * Check if an app is loaded |
||
86 | * |
||
87 | * @param string $app |
||
88 | * @return bool |
||
89 | */ |
||
90 | public static function isAppLoaded($app) { |
||
93 | |||
94 | /** |
||
95 | * loads all apps |
||
96 | * |
||
97 | * @param string[] | string | null $types |
||
98 | * @return bool |
||
99 | * |
||
100 | * This function walks through the ownCloud directory and loads all apps |
||
101 | * it can find. A directory contains an app if the file /appinfo/info.xml |
||
102 | * exists. |
||
103 | * |
||
104 | * if $types is set, only apps of those types will be loaded |
||
105 | */ |
||
106 | public static function loadApps($types = null) { |
||
132 | |||
133 | /** |
||
134 | * load a single app |
||
135 | * |
||
136 | * @param string $app |
||
137 | */ |
||
138 | public static function loadApp($app) { |
||
139 | self::$loadedApps[] = $app; |
||
140 | $appPath = self::getAppPath($app); |
||
141 | if($appPath === false) { |
||
142 | return; |
||
143 | } |
||
144 | |||
145 | // in case someone calls loadApp() directly |
||
146 | self::registerAutoloading($app, $appPath); |
||
147 | |||
148 | if (is_file($appPath . '/appinfo/app.php')) { |
||
149 | \OC::$server->getEventLogger()->start('load_app_' . $app, 'Load app: ' . $app); |
||
150 | self::requireAppFile($app); |
||
151 | if (self::isType($app, array('authentication'))) { |
||
152 | // since authentication apps affect the "is app enabled for group" check, |
||
153 | // the enabled apps cache needs to be cleared to make sure that the |
||
154 | // next time getEnableApps() is called it will also include apps that were |
||
155 | // enabled for groups |
||
156 | self::$enabledAppsCache = array(); |
||
157 | } |
||
158 | \OC::$server->getEventLogger()->end('load_app_' . $app); |
||
159 | } |
||
160 | |||
161 | $info = self::getAppInfo($app); |
||
162 | View Code Duplication | if (!empty($info['activity']['filters'])) { |
|
163 | foreach ($info['activity']['filters'] as $filter) { |
||
164 | \OC::$server->getActivityManager()->registerFilter($filter); |
||
165 | } |
||
166 | } |
||
167 | View Code Duplication | if (!empty($info['activity']['settings'])) { |
|
168 | foreach ($info['activity']['settings'] as $setting) { |
||
169 | \OC::$server->getActivityManager()->registerSetting($setting); |
||
170 | } |
||
171 | } |
||
172 | View Code Duplication | if (!empty($info['activity']['providers'])) { |
|
173 | foreach ($info['activity']['providers'] as $provider) { |
||
174 | \OC::$server->getActivityManager()->registerProvider($provider); |
||
175 | } |
||
176 | } |
||
177 | if (!empty($info['collaboration']['plugins'])) { |
||
178 | // deal with one or many plugin entries |
||
179 | $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ? |
||
180 | [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin']; |
||
181 | foreach ($plugins as $plugin) { |
||
182 | if($plugin['@attributes']['type'] === 'collaborator-search') { |
||
183 | $pluginInfo = [ |
||
184 | 'shareType' => $plugin['@attributes']['share-type'], |
||
185 | 'class' => $plugin['@value'], |
||
186 | ]; |
||
187 | \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo); |
||
188 | } |
||
189 | } |
||
190 | } |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * @internal |
||
195 | * @param string $app |
||
196 | * @param string $path |
||
197 | */ |
||
198 | public static function registerAutoloading($app, $path) { |
||
199 | $key = $app . '-' . $path; |
||
200 | if(isset(self::$alreadyRegistered[$key])) { |
||
201 | return; |
||
202 | } |
||
203 | self::$alreadyRegistered[$key] = true; |
||
204 | // Register on PSR-4 composer autoloader |
||
205 | $appNamespace = \OC\AppFramework\App::buildAppNamespace($app); |
||
206 | \OC::$server->registerNamespace($app, $appNamespace); |
||
207 | \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true); |
||
208 | if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) { |
||
209 | \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true); |
||
210 | } |
||
211 | |||
212 | // Register on legacy autoloader |
||
213 | \OC::$loader->addValidRoot($path); |
||
214 | } |
||
215 | |||
216 | /** |
||
217 | * Load app.php from the given app |
||
218 | * |
||
219 | * @param string $app app name |
||
220 | */ |
||
221 | private static function requireAppFile($app) { |
||
222 | try { |
||
223 | // encapsulated here to avoid variable scope conflicts |
||
224 | require_once $app . '/appinfo/app.php'; |
||
225 | } catch (Error $ex) { |
||
226 | \OC::$server->getLogger()->logException($ex); |
||
227 | $blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps(); |
||
228 | if (!in_array($app, $blacklist)) { |
||
229 | self::disable($app); |
||
230 | } |
||
231 | } |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * check if an app is of a specific type |
||
236 | * |
||
237 | * @param string $app |
||
238 | * @param string|array $types |
||
239 | * @return bool |
||
240 | */ |
||
241 | public static function isType($app, $types) { |
||
242 | if (is_string($types)) { |
||
243 | $types = array($types); |
||
244 | } |
||
245 | $appTypes = self::getAppTypes($app); |
||
246 | foreach ($types as $type) { |
||
247 | if (array_search($type, $appTypes) !== false) { |
||
248 | return true; |
||
249 | } |
||
250 | } |
||
251 | return false; |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * get the types of an app |
||
256 | * |
||
257 | * @param string $app |
||
258 | * @return array |
||
259 | */ |
||
260 | private static function getAppTypes($app) { |
||
261 | //load the cache |
||
262 | if (count(self::$appTypes) == 0) { |
||
263 | self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); |
||
264 | } |
||
265 | |||
266 | if (isset(self::$appTypes[$app])) { |
||
267 | return explode(',', self::$appTypes[$app]); |
||
268 | } else { |
||
269 | return array(); |
||
270 | } |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * read app types from info.xml and cache them in the database |
||
275 | */ |
||
276 | public static function setAppTypes($app) { |
||
277 | $appData = self::getAppInfo($app); |
||
278 | if(!is_array($appData)) { |
||
279 | return; |
||
280 | } |
||
281 | |||
282 | if (isset($appData['types'])) { |
||
283 | $appTypes = implode(',', $appData['types']); |
||
284 | } else { |
||
285 | $appTypes = ''; |
||
286 | $appData['types'] = []; |
||
287 | } |
||
288 | |||
289 | \OC::$server->getAppConfig()->setValue($app, 'types', $appTypes); |
||
290 | |||
291 | if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) { |
||
292 | $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes'); |
||
293 | if ($enabled !== 'yes' && $enabled !== 'no') { |
||
294 | \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes'); |
||
295 | } |
||
296 | } |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * get all enabled apps |
||
301 | */ |
||
302 | protected static $enabledAppsCache = array(); |
||
303 | |||
304 | /** |
||
305 | * Returns apps enabled for the current user. |
||
306 | * |
||
307 | * @param bool $forceRefresh whether to refresh the cache |
||
308 | * @param bool $all whether to return apps for all users, not only the |
||
309 | * currently logged in one |
||
310 | * @return string[] |
||
311 | */ |
||
312 | public static function getEnabledApps($forceRefresh = false, $all = false) { |
||
313 | if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { |
||
314 | return array(); |
||
315 | } |
||
316 | // in incognito mode or when logged out, $user will be false, |
||
317 | // which is also the case during an upgrade |
||
318 | $appManager = \OC::$server->getAppManager(); |
||
319 | if ($all) { |
||
320 | $user = null; |
||
321 | } else { |
||
322 | $user = \OC::$server->getUserSession()->getUser(); |
||
323 | } |
||
324 | |||
325 | if (is_null($user)) { |
||
326 | $apps = $appManager->getInstalledApps(); |
||
327 | } else { |
||
328 | $apps = $appManager->getEnabledAppsForUser($user); |
||
329 | } |
||
330 | $apps = array_filter($apps, function ($app) { |
||
331 | return $app !== 'files';//we add this manually |
||
332 | }); |
||
333 | sort($apps); |
||
334 | array_unshift($apps, 'files'); |
||
335 | return $apps; |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * checks whether or not an app is enabled |
||
340 | * |
||
341 | * @param string $app app |
||
342 | * @return bool |
||
343 | * |
||
344 | * This function checks whether or not an app is enabled. |
||
345 | */ |
||
346 | public static function isEnabled($app) { |
||
347 | return \OC::$server->getAppManager()->isEnabledForUser($app); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * enables an app |
||
352 | * |
||
353 | * @param string $appId |
||
354 | * @param array $groups (optional) when set, only these groups will have access to the app |
||
355 | * @throws \Exception |
||
356 | * @return void |
||
357 | * |
||
358 | * This function set an app as enabled in appconfig. |
||
359 | */ |
||
360 | public function enable($appId, |
||
361 | $groups = null) { |
||
362 | self::$enabledAppsCache = []; // flush |
||
363 | |||
364 | // Check if app is already downloaded |
||
365 | $installer = new Installer( |
||
366 | \OC::$server->getAppFetcher(), |
||
367 | \OC::$server->getHTTPClientService(), |
||
368 | \OC::$server->getTempManager(), |
||
369 | \OC::$server->getLogger(), |
||
370 | \OC::$server->getConfig() |
||
371 | ); |
||
372 | $isDownloaded = $installer->isDownloaded($appId); |
||
373 | |||
374 | if(!$isDownloaded) { |
||
375 | $installer->downloadApp($appId); |
||
376 | } |
||
377 | |||
378 | $installer->installApp($appId); |
||
379 | |||
380 | $appManager = \OC::$server->getAppManager(); |
||
381 | if (!is_null($groups)) { |
||
382 | $groupManager = \OC::$server->getGroupManager(); |
||
383 | $groupsList = []; |
||
384 | foreach ($groups as $group) { |
||
385 | $groupItem = $groupManager->get($group); |
||
386 | if ($groupItem instanceof \OCP\IGroup) { |
||
387 | $groupsList[] = $groupManager->get($group); |
||
388 | } |
||
389 | } |
||
390 | $appManager->enableAppForGroups($appId, $groupsList); |
||
391 | } else { |
||
392 | $appManager->enableApp($appId); |
||
393 | } |
||
394 | } |
||
395 | |||
396 | /** |
||
397 | * @param string $app |
||
398 | * @return bool |
||
399 | */ |
||
400 | public static function removeApp($app) { |
||
414 | |||
415 | /** |
||
416 | * This function set an app as disabled in appconfig. |
||
417 | * |
||
418 | * @param string $app app |
||
419 | * @throws Exception |
||
420 | */ |
||
421 | public static function disable($app) { |
||
438 | |||
439 | // This is private as well. It simply works, so don't ask for more details |
||
440 | private static function proceedNavigation($list) { |
||
463 | |||
464 | /** |
||
465 | * Get the path where to install apps |
||
466 | * |
||
467 | * @return string|false |
||
468 | */ |
||
469 | public static function getInstallPath() { |
||
483 | |||
484 | |||
485 | /** |
||
486 | * search for an app in all app-directories |
||
487 | * |
||
488 | * @param string $appId |
||
489 | * @return false|string |
||
490 | */ |
||
491 | public static function findAppInDirectories($appId) { |
||
531 | |||
532 | /** |
||
533 | * Get the directory for the given app. |
||
534 | * If the app is defined in multiple directories, the first one is taken. (false if not found) |
||
535 | * |
||
536 | * @param string $appId |
||
537 | * @return string|false |
||
538 | */ |
||
539 | public static function getAppPath($appId) { |
||
549 | |||
550 | /** |
||
551 | * Get the path for the given app on the access |
||
552 | * If the app is defined in multiple directories, the first one is taken. (false if not found) |
||
553 | * |
||
554 | * @param string $appId |
||
555 | * @return string|false |
||
556 | */ |
||
557 | public static function getAppWebPath($appId) { |
||
563 | |||
564 | /** |
||
565 | * get the last version of the app from appinfo/info.xml |
||
566 | * |
||
567 | * @param string $appId |
||
568 | * @param bool $useCache |
||
569 | * @return string |
||
570 | */ |
||
571 | public static function getAppVersion($appId, $useCache = true) { |
||
580 | |||
581 | /** |
||
582 | * get app's version based on it's path |
||
583 | * |
||
584 | * @param string $path |
||
585 | * @return string |
||
586 | */ |
||
587 | public static function getAppVersionByPath($path) { |
||
592 | |||
593 | |||
594 | /** |
||
595 | * Read all app metadata from the info.xml file |
||
596 | * |
||
597 | * @param string $appId id of the app or the path of the info.xml file |
||
598 | * @param bool $path |
||
599 | * @param string $lang |
||
600 | * @return array|null |
||
601 | * @note all data is read from info.xml, not just pre-defined fields |
||
602 | */ |
||
603 | public static function getAppInfo($appId, $path = false, $lang = null) { |
||
636 | |||
637 | /** |
||
638 | * Returns the navigation |
||
639 | * |
||
640 | * @return array |
||
641 | * |
||
642 | * This function returns an array containing all entries added. The |
||
643 | * entries are sorted by the key 'order' ascending. Additional to the keys |
||
644 | * given for each app the following keys exist: |
||
645 | * - active: boolean, signals if the user is on this navigation entry |
||
646 | */ |
||
647 | public static function getNavigation() { |
||
651 | |||
652 | /** |
||
653 | * Returns the Settings Navigation |
||
654 | * |
||
655 | * @return string[] |
||
656 | * |
||
657 | * This function returns an array containing all settings pages added. The |
||
658 | * entries are sorted by the key 'order' ascending. |
||
659 | */ |
||
660 | public static function getSettingsNavigation() { |
||
664 | |||
665 | /** |
||
666 | * get the id of loaded app |
||
667 | * |
||
668 | * @return string |
||
669 | */ |
||
670 | public static function getCurrentApp() { |
||
687 | |||
688 | /** |
||
689 | * @param string $type |
||
690 | * @return array |
||
691 | */ |
||
692 | public static function getForms($type) { |
||
709 | |||
710 | /** |
||
711 | * register an admin form to be shown |
||
712 | * |
||
713 | * @param string $app |
||
714 | * @param string $page |
||
715 | */ |
||
716 | public static function registerAdmin($app, $page) { |
||
719 | |||
720 | /** |
||
721 | * register a personal form to be shown |
||
722 | * @param string $app |
||
723 | * @param string $page |
||
724 | */ |
||
725 | public static function registerPersonal($app, $page) { |
||
728 | |||
729 | /** |
||
730 | * @param array $entry |
||
731 | */ |
||
732 | public static function registerLogIn(array $entry) { |
||
735 | |||
736 | /** |
||
737 | * @return array |
||
738 | */ |
||
739 | public static function getAlternativeLogIns() { |
||
742 | |||
743 | /** |
||
744 | * get a list of all apps in the apps folder |
||
745 | * |
||
746 | * @return array an array of app names (string IDs) |
||
747 | * @todo: change the name of this method to getInstalledApps, which is more accurate |
||
748 | */ |
||
749 | public static function getAllApps() { |
||
750 | |||
751 | $apps = array(); |
||
773 | |||
774 | /** |
||
775 | * List all apps, this is used in apps.php |
||
776 | * |
||
777 | * @return array |
||
778 | */ |
||
779 | public function listAllApps() { |
||
859 | |||
860 | /** |
||
861 | * Returns the internal app ID or false |
||
862 | * @param string $ocsID |
||
863 | * @return string|false |
||
864 | */ |
||
865 | public static function getInternalAppIdByOcs($ocsID) { |
||
874 | |||
875 | public static function shouldUpgrade($app) { |
||
886 | |||
887 | /** |
||
888 | * Adjust the number of version parts of $version1 to match |
||
889 | * the number of version parts of $version2. |
||
890 | * |
||
891 | * @param string $version1 version to adjust |
||
892 | * @param string $version2 version to take the number of parts from |
||
893 | * @return string shortened $version1 |
||
894 | */ |
||
895 | private static function adjustVersionParts($version1, $version2) { |
||
908 | |||
909 | /** |
||
910 | * Check whether the current ownCloud version matches the given |
||
911 | * application's version requirements. |
||
912 | * |
||
913 | * The comparison is made based on the number of parts that the |
||
914 | * app info version has. For example for ownCloud 6.0.3 if the |
||
915 | * app info version is expecting version 6.0, the comparison is |
||
916 | * made on the first two parts of the ownCloud version. |
||
917 | * This means that it's possible to specify "requiremin" => 6 |
||
918 | * and "requiremax" => 6 and it will still match ownCloud 6.0.3. |
||
919 | * |
||
920 | * @param string $ocVersion ownCloud version to check against |
||
921 | * @param array $appInfo app info (from xml) |
||
922 | * |
||
923 | * @return boolean true if compatible, otherwise false |
||
924 | */ |
||
925 | public static function isAppCompatible($ocVersion, $appInfo) { |
||
965 | |||
966 | /** |
||
967 | * get the installed version of all apps |
||
968 | */ |
||
969 | public static function getAppVersions() { |
||
978 | |||
979 | /** |
||
980 | * @param string $app |
||
981 | * @param \OCP\IConfig $config |
||
982 | * @param \OCP\IL10N $l |
||
983 | * @return bool |
||
984 | * |
||
985 | * @throws Exception if app is not compatible with this version of ownCloud |
||
986 | * @throws Exception if no app-name was specified |
||
987 | */ |
||
988 | public function installApp($app, |
||
1036 | |||
1037 | /** |
||
1038 | * update the database for the app and call the update script |
||
1039 | * |
||
1040 | * @param string $appId |
||
1041 | * @return bool |
||
1042 | */ |
||
1043 | public static function updateApp($appId) { |
||
1098 | |||
1099 | /** |
||
1100 | * @param string $appId |
||
1101 | * @param string[] $steps |
||
1102 | * @throws \OC\NeedsUpdateException |
||
1103 | */ |
||
1104 | public static function executeRepairSteps($appId, array $steps) { |
||
1126 | |||
1127 | public static function setupBackgroundJobs(array $jobs) { |
||
1133 | |||
1134 | /** |
||
1135 | * @param string $appId |
||
1136 | * @param string[] $steps |
||
1137 | */ |
||
1138 | private static function setupLiveMigrations($appId, array $steps) { |
||
1146 | |||
1147 | /** |
||
1148 | * @param string $appId |
||
1149 | * @return \OC\Files\View|false |
||
1150 | */ |
||
1151 | public static function getStorage($appId) { |
||
1168 | |||
1169 | protected static function findBestL10NOption($options, $lang) { |
||
1213 | |||
1214 | /** |
||
1215 | * parses the app data array and enhanced the 'description' value |
||
1216 | * |
||
1217 | * @param array $data the app data |
||
1218 | * @param string $lang |
||
1219 | * @return array improved app data |
||
1220 | */ |
||
1221 | public static function parseAppInfo(array $data, $lang = null) { |
||
1239 | |||
1240 | /** |
||
1241 | * @param \OCP\IConfig $config |
||
1242 | * @param \OCP\IL10N $l |
||
1243 | * @param array $info |
||
1244 | * @throws \Exception |
||
1245 | */ |
||
1246 | public static function checkAppDependencies($config, $l, $info) { |
||
1258 | } |
||
1259 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.