Total Complexity | 56 |
Total Lines | 547 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like ModulesManagementProcessor 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.
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 ModulesManagementProcessor, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
47 | class ModulesManagementProcessor extends Injectable |
||
48 | { |
||
49 | /** |
||
50 | * Processes module management requests. |
||
51 | * |
||
52 | * @param array $request The request data. |
||
53 | * |
||
54 | * @return PBXApiResult An object containing the result of the API call. |
||
55 | * |
||
56 | */ |
||
57 | public static function callBack(array $request): PBXApiResult |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Installs a new additional extension module from an early uploaded zip archive. |
||
112 | * |
||
113 | * @param string $filePath The path to the module file. |
||
114 | * |
||
115 | * @return PBXApiResult An object containing the result of the API call. |
||
116 | */ |
||
117 | public static function installModule(string $filePath): PBXApiResult |
||
118 | { |
||
119 | $res = new PBXApiResult(); |
||
120 | $res->processor = __METHOD__; |
||
121 | $resModuleMetadata = self::getMetadataFromModuleFile($filePath); |
||
122 | if (!$resModuleMetadata->success) { |
||
123 | return $resModuleMetadata; |
||
124 | } |
||
125 | |||
126 | $moduleUniqueID = $resModuleMetadata->data['uniqid']; |
||
127 | // Disable the module if it's enabled |
||
128 | if (PbxExtensionUtils::isEnabled($moduleUniqueID)) { |
||
129 | $res = self::disableModule($moduleUniqueID); |
||
130 | if (!$res->success) { |
||
131 | return $res; |
||
132 | } |
||
133 | } |
||
134 | |||
135 | $currentModuleDir = PbxExtensionUtils::getModuleDir($moduleUniqueID); |
||
136 | $needBackup = is_dir($currentModuleDir); |
||
137 | |||
138 | if ($needBackup) { |
||
139 | self::uninstallModule($moduleUniqueID, true); |
||
140 | } |
||
141 | |||
142 | // Start the background process to install the module |
||
143 | $temp_dir = dirname($filePath); |
||
144 | |||
145 | // Create a progress file to track the installation progress |
||
146 | file_put_contents($temp_dir . '/installation_progress', '0'); |
||
147 | |||
148 | // Create an error file to store any installation errors |
||
149 | file_put_contents($temp_dir . '/installation_error', ''); |
||
150 | |||
151 | $install_settings = [ |
||
152 | 'filePath' => $filePath, |
||
153 | 'currentModuleDir' => $currentModuleDir, |
||
154 | 'uniqid' => $moduleUniqueID, |
||
155 | ]; |
||
156 | |||
157 | // Save the installation settings to a JSON file |
||
158 | $settings_file = "{$temp_dir}/install_settings.json"; |
||
159 | file_put_contents( |
||
160 | $settings_file, |
||
161 | json_encode($install_settings, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) |
||
162 | ); |
||
163 | $phpPath = Util::which('php'); |
||
164 | $workerFilesMergerPath = Util::getFilePathByClassName(WorkerModuleInstaller::class); |
||
165 | |||
166 | // Execute the background process to install the module |
||
167 | Processes::mwExecBg("{$phpPath} -f {$workerFilesMergerPath} start '{$settings_file}'"); |
||
168 | $res->data['filePath'] = $filePath; |
||
169 | $res->success = true; |
||
170 | |||
171 | return $res; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Uninstall extension module |
||
176 | * |
||
177 | * @param string $moduleUniqueID The unique ID of the module to uninstall. |
||
178 | * @param bool $keepSettings Indicates whether to keep the module settings. |
||
179 | * |
||
180 | * @return PBXApiResult An object containing the result of the API call. |
||
181 | */ |
||
182 | public static function uninstallModule(string $moduleUniqueID, bool $keepSettings): PBXApiResult |
||
183 | { |
||
184 | $res = new PBXApiResult(); |
||
185 | $res->processor = __METHOD__; |
||
186 | $currentModuleDir = PbxExtensionUtils::getModuleDir($moduleUniqueID); |
||
187 | |||
188 | // Kill all module processes |
||
189 | if (is_dir("{$currentModuleDir}/bin")) { |
||
190 | $busyboxPath = Util::which('busybox'); |
||
191 | $killPath = Util::which('kill'); |
||
192 | $lsofPath = Util::which('lsof'); |
||
193 | $grepPath = Util::which('grep'); |
||
194 | $awkPath = Util::which('awk'); |
||
195 | $uniqPath = Util::which('uniq'); |
||
196 | |||
197 | // Execute the command to kill all processes related to the module |
||
198 | Processes::mwExec( |
||
199 | "{$busyboxPath} {$killPath} -9 $({$lsofPath} {$currentModuleDir}/bin/* | {$busyboxPath} {$grepPath} -v COMMAND | {$busyboxPath} {$awkPath} '{ print $2}' | {$busyboxPath} {$uniqPath})" |
||
200 | ); |
||
201 | } |
||
202 | |||
203 | // Uninstall module with keep settings and backup db |
||
204 | $moduleClass = "\\Modules\\{$moduleUniqueID}\\Setup\\PbxExtensionSetup"; |
||
205 | |||
206 | try { |
||
207 | if (class_exists($moduleClass) |
||
208 | && method_exists($moduleClass, 'uninstallModule')) { |
||
209 | // Instantiate the module setup class and call the uninstallModule method |
||
210 | $setup = new $moduleClass($moduleUniqueID); |
||
211 | } else { |
||
212 | |||
213 | // Use a fallback class to uninstall the module from the database if it doesn't exist on disk |
||
214 | $moduleClass = PbxExtensionSetupFailure::class; |
||
215 | $setup = new $moduleClass($moduleUniqueID); |
||
216 | } |
||
217 | $setup->uninstallModule($keepSettings); |
||
218 | } finally { |
||
219 | if (is_dir($currentModuleDir)) { |
||
220 | // If the module directory still exists, force uninstallation |
||
221 | $rmPath = Util::which('rm'); |
||
222 | |||
223 | // Remove the module directory recursively |
||
224 | Processes::mwExec("{$rmPath} -rf {$currentModuleDir}"); |
||
225 | |||
226 | // Use the fallback class to unregister the module from the database |
||
227 | $moduleClass = PbxExtensionSetupFailure::class; |
||
228 | $setup = new $moduleClass($moduleUniqueID); |
||
229 | $setup->unregisterModule(); |
||
230 | } |
||
231 | } |
||
232 | $res->success = true; |
||
233 | |||
234 | return $res; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Enables extension module. |
||
239 | * |
||
240 | * @param string $moduleUniqueID |
||
241 | * |
||
242 | * @return PBXApiResult An object containing the result of the API call. |
||
243 | */ |
||
244 | private static function enableModule(string $moduleUniqueID): PBXApiResult |
||
245 | { |
||
246 | $res = new PBXApiResult(); |
||
247 | $res->processor = __METHOD__; |
||
248 | $moduleStateProcessor = new PbxExtensionState($moduleUniqueID); |
||
249 | if ($moduleStateProcessor->enableModule() === false) { |
||
250 | $res->success = false; |
||
251 | $res->messages = $moduleStateProcessor->getMessages(); |
||
252 | } else { |
||
253 | PBXConfModulesProvider::recreateModulesProvider(); |
||
254 | $res->data = $moduleStateProcessor->getMessages(); |
||
255 | $res->success = true; |
||
256 | } |
||
257 | |||
258 | return $res; |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Disables extension module. |
||
263 | * |
||
264 | * @param string $moduleUniqueID |
||
265 | * |
||
266 | * @return PBXApiResult An object containing the result of the API call. |
||
267 | */ |
||
268 | private static function disableModule(string $moduleUniqueID): PBXApiResult |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Checks the status of a module installation by the provided zip file path. |
||
287 | * |
||
288 | * @param string $filePath The path of the module installation file. |
||
289 | * |
||
290 | * @return PBXApiResult An object containing the result of the API call. |
||
291 | */ |
||
292 | public static function statusOfModuleInstallation(string $filePath): PBXApiResult |
||
293 | { |
||
294 | $res = new PBXApiResult(); |
||
295 | $res->processor = __METHOD__; |
||
296 | $di = Di::getDefault(); |
||
297 | if ($di === null) { |
||
298 | $res->messages[] = 'Dependency injector does not initialized'; |
||
299 | |||
300 | return $res; |
||
301 | } |
||
302 | $temp_dir = dirname($filePath); |
||
303 | $progress_file = $temp_dir . '/installation_progress'; |
||
304 | $error_file = $temp_dir . '/installation_error'; |
||
305 | if (!file_exists($error_file) || !file_exists($progress_file)) { |
||
306 | $res->success = false; |
||
307 | $res->data['i_status'] = 'PROGRESS_FILE_NOT_FOUND'; |
||
308 | $res->data['i_status_progress'] = '0'; |
||
309 | } elseif (file_get_contents($error_file) !== '') { |
||
310 | $res->success = false; |
||
311 | $res->data['i_status'] = 'INSTALLATION_ERROR'; |
||
312 | $res->data['i_status_progress'] = '0'; |
||
313 | $res->messages[] = file_get_contents($error_file); |
||
314 | } elseif ('100' === file_get_contents($progress_file)) { |
||
315 | $res->success = true; |
||
316 | $res->data['i_status_progress'] = '100'; |
||
317 | $res->data['i_status'] = 'INSTALLATION_COMPLETE'; |
||
318 | } else { |
||
319 | $res->success = true; |
||
320 | $res->data['i_status'] = 'INSTALLATION_IN_PROGRESS'; |
||
321 | $res->data['i_status_progress'] = file_get_contents($progress_file); |
||
322 | } |
||
323 | |||
324 | return $res; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Starts the module download in a separate background process. |
||
329 | * |
||
330 | * @param string $module The module name. |
||
331 | * @param string $url The download URL of the module. |
||
332 | * @param string $md5 The MD5 hash of the module file. |
||
333 | * |
||
334 | * @return PBXApiResult An object containing the result of the API call. |
||
335 | */ |
||
336 | public static function moduleStartDownload(string $module, string $url, string $md5): PBXApiResult |
||
337 | { |
||
338 | $res = new PBXApiResult(); |
||
339 | $res->processor = __METHOD__; |
||
340 | $di = Di::getDefault(); |
||
341 | if ($di !== null) { |
||
342 | $tempDir = $di->getShared(ConfigProvider::SERVICE_NAME)->path('www.uploadDir'); |
||
343 | } else { |
||
344 | $tempDir = '/tmp'; |
||
345 | } |
||
346 | |||
347 | $moduleDirTmp = "{$tempDir}/{$module}"; |
||
348 | Util::mwMkdir($moduleDirTmp); |
||
349 | |||
350 | $download_settings = [ |
||
351 | 'res_file' => "$moduleDirTmp/modulefile.zip", |
||
352 | 'url' => $url, |
||
353 | 'module' => $module, |
||
354 | 'md5' => $md5, |
||
355 | 'action' => 'moduleInstall', |
||
356 | ]; |
||
357 | if (file_exists("$moduleDirTmp/error")) { |
||
358 | unlink("$moduleDirTmp/error"); |
||
359 | } |
||
360 | if (file_exists("$moduleDirTmp/installed")) { |
||
361 | unlink("$moduleDirTmp/installed"); |
||
362 | } |
||
363 | file_put_contents("$moduleDirTmp/progress", '0'); |
||
364 | file_put_contents( |
||
365 | "$moduleDirTmp/download_settings.json", |
||
366 | json_encode($download_settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) |
||
367 | ); |
||
368 | $workerDownloaderPath = Util::getFilePathByClassName(WorkerDownloader::class); |
||
369 | $phpPath = Util::which('php'); |
||
370 | Processes::mwExecBg("{$phpPath} -f {$workerDownloaderPath} start {$moduleDirTmp}/download_settings.json"); |
||
371 | |||
372 | $res->data['uniqid'] = $module; |
||
373 | $res->data['d_status'] = 'DOWNLOAD_IN_PROGRESS'; |
||
374 | $res->success = true; |
||
375 | |||
376 | return $res; |
||
377 | } |
||
378 | |||
379 | /** |
||
380 | * Returns the download status of a module. |
||
381 | * |
||
382 | * @param string $moduleUniqueID The unique ID of the module. |
||
383 | * |
||
384 | * @return PBXApiResult An object containing the result of the API call. |
||
385 | */ |
||
386 | public static function moduleDownloadStatus(string $moduleUniqueID): PBXApiResult |
||
387 | { |
||
388 | clearstatcache(); |
||
389 | $res = new PBXApiResult(); |
||
390 | $res->processor = __METHOD__; |
||
391 | $di = Di::getDefault(); |
||
392 | if ($di !== null) { |
||
393 | $tempDir = $di->getShared(ConfigProvider::SERVICE_NAME)->path('www.uploadDir'); |
||
394 | } else { |
||
395 | $tempDir = '/tmp'; |
||
396 | } |
||
397 | $moduleDirTmp = $tempDir . '/' . $moduleUniqueID; |
||
398 | $progress_file = $moduleDirTmp . '/progress'; |
||
399 | $error = ''; |
||
400 | if (file_exists($moduleDirTmp . '/error')) { |
||
401 | $error = trim(file_get_contents($moduleDirTmp . '/error')); |
||
402 | } |
||
403 | |||
404 | // Wait until download process started |
||
405 | $d_pid = Processes::getPidOfProcess("{$moduleDirTmp}/download_settings.json"); |
||
406 | if (empty($d_pid)) { |
||
407 | usleep(500000); |
||
408 | } |
||
409 | |||
410 | if (!file_exists($progress_file)) { |
||
411 | $res->data['d_status_progress'] = '0'; |
||
412 | $res->data['d_status'] = 'NOT_FOUND'; |
||
413 | $res->success = false; |
||
414 | } elseif ('' !== $error) { |
||
415 | $res->data['d_status'] = 'DOWNLOAD_ERROR'; |
||
416 | $res->data['d_status_progress'] = file_get_contents($progress_file); |
||
417 | $res->data['d_error'] = $error; |
||
418 | $res->messages[] = file_get_contents($moduleDirTmp . '/error'); |
||
419 | $res->success = false; |
||
420 | } elseif ('100' === file_get_contents($progress_file)) { |
||
421 | $res->data['d_status_progress'] = '100'; |
||
422 | $res->data['d_status'] = 'DOWNLOAD_COMPLETE'; |
||
423 | $res->data['filePath'] = "$moduleDirTmp/modulefile.zip"; |
||
424 | $res->success = true; |
||
425 | } else { |
||
426 | $res->data['d_status_progress'] = file_get_contents($progress_file); |
||
427 | $d_pid = Processes::getPidOfProcess($moduleDirTmp . '/download_settings.json'); |
||
428 | if (empty($d_pid)) { |
||
429 | $res->data['d_status'] = 'DOWNLOAD_ERROR'; |
||
430 | if (file_exists($moduleDirTmp . '/error')) { |
||
431 | $res->messages[] = file_get_contents($moduleDirTmp . '/error'); |
||
432 | } else { |
||
433 | $res->messages[] = "Download process interrupted at {$res->data['d_status_progress']}%"; |
||
434 | } |
||
435 | $res->success = false; |
||
436 | } else { |
||
437 | $res->data['d_status'] = 'DOWNLOAD_IN_PROGRESS'; |
||
438 | $res->success = true; |
||
439 | } |
||
440 | } |
||
441 | |||
442 | return $res; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Unpacks a module ZIP file and retrieves metadata information from the JSON config inside. |
||
447 | * |
||
448 | * @param string $filePath The file path of the module. |
||
449 | * |
||
450 | * @return PBXApiResult An object containing the result of the API call. |
||
451 | */ |
||
452 | public static function getMetadataFromModuleFile(string $filePath): PBXApiResult |
||
453 | { |
||
454 | $res = new PBXApiResult(); |
||
455 | $res->processor = __METHOD__; |
||
456 | |||
457 | if (file_exists($filePath)) { |
||
458 | $sevenZaPath = Util::which('7za'); |
||
459 | $grepPath = Util::which('grep'); |
||
460 | $echoPath = Util::which('echo'); |
||
461 | $awkPath = Util::which('awk'); |
||
462 | $cmd = 'f="' . $filePath . '"; p=`' . $sevenZaPath . ' l $f | ' . $grepPath . ' module.json`;if [ "$?" == "0" ]; then ' . $sevenZaPath . ' -so e -y -r $f `' . $echoPath . ' $p | ' . $awkPath . ' -F" " \'{print $6}\'`; fi'; |
||
463 | |||
464 | Processes::mwExec($cmd, $out); |
||
465 | $settings = json_decode(implode("\n", $out), true); |
||
466 | |||
467 | $moduleUniqueID = $settings['moduleUniqueID'] ?? null; |
||
468 | if (!$moduleUniqueID) { |
||
469 | $res->messages[] = 'The" moduleUniqueID " in the module file is not described.the json or file does not exist.'; |
||
470 | |||
471 | return $res; |
||
472 | } |
||
473 | $res->success = true; |
||
474 | $res->data = [ |
||
475 | 'filePath' => $filePath, |
||
476 | 'uniqid' => $moduleUniqueID, |
||
477 | ]; |
||
478 | } |
||
479 | |||
480 | return $res; |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Retrieves available modules on MIKO repository. |
||
485 | * |
||
486 | * @return PBXApiResult |
||
487 | */ |
||
488 | public static function getAvailableModules(): PBXApiResult |
||
544 | } |
||
545 | |||
546 | /** |
||
547 | * Retrieves the installation link for a module. |
||
548 | * |
||
549 | * @param string $moduleReleaseId The module release unique id retrieved on getAvailableModules |
||
550 | * |
||
551 | * @return PBXApiResult |
||
552 | */ |
||
553 | public static function getModuleLink(string $moduleReleaseId): PBXApiResult |
||
594 | } |
||
595 | |||
596 | } |