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 PluginController 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 PluginController, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 58 | class PluginController extends AbstractController | ||
| 59 | { | ||
| 60 | /** | ||
| 61 |      * @Inject("orm.em") | ||
| 62 | * @var EntityManager | ||
| 63 | */ | ||
| 64 | protected $entityManager; | ||
| 65 | |||
| 66 | /** | ||
| 67 |      * @Inject("monolog") | ||
| 68 | * @var Logger | ||
| 69 | */ | ||
| 70 | protected $logger; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * @Inject(PluginEventHandlerRepository::class) | ||
| 74 | * @var PluginEventHandlerRepository | ||
| 75 | */ | ||
| 76 | protected $pluginEventHandlerRepository; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * @Inject(PluginService::class) | ||
| 80 | * @var PluginService | ||
| 81 | */ | ||
| 82 | protected $pluginService; | ||
| 83 | |||
| 84 | /** | ||
| 85 |      * @Inject("config") | ||
| 86 | * @var array | ||
| 87 | */ | ||
| 88 | protected $appConfig; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * @Inject(BaseInfo::class) | ||
| 92 | * @var BaseInfo | ||
| 93 | */ | ||
| 94 | protected $BaseInfo; | ||
| 95 | |||
| 96 | /** | ||
| 97 |      * @Inject("form.factory") | ||
| 98 | * @var FormFactory | ||
| 99 | */ | ||
| 100 | protected $formFactory; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * @Inject(PluginRepository::class) | ||
| 104 | * @var PluginRepository | ||
| 105 | */ | ||
| 106 | protected $pluginRepository; | ||
| 107 | |||
| 108 | |||
| 109 | /** | ||
| 110 | * インストール済プラグイン画面 | ||
| 111 | * | ||
| 112 |      * @Route("/{_admin}/store/plugin", name="admin_store_plugin") | ||
| 113 |      * @Template("Store/plugin.twig") | ||
| 114 | */ | ||
| 115 | public function index(Application $app, Request $request) | ||
| 116 |     { | ||
| 117 | $pluginForms = array(); | ||
| 118 | $configPages = array(); | ||
| 119 | |||
| 120 |         $Plugins = $this->pluginRepository->findBy(array(), array('code' => 'ASC')); | ||
| 121 | |||
| 122 | // ファイル設置プラグインの取得. | ||
| 123 | $unregisterdPlugins = $this->getUnregisteredPlugins($Plugins, $app); | ||
| 124 | $unregisterdPluginsConfigPages = array(); | ||
| 125 |         foreach ($unregisterdPlugins as $unregisterdPlugin) { | ||
| 126 |             try { | ||
| 127 | $code = $unregisterdPlugin['code']; | ||
| 128 | // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか) | ||
| 129 |                 $unregisterdPluginsConfigPages[$code] = $app->url('plugin_'.$code.'_config'); | ||
| 130 |             } catch (RouteNotFoundException $e) { | ||
| 131 | // プラグインで設定画面のルートが定義されていない場合は無視 | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | $officialPlugins = array(); | ||
| 136 | $unofficialPlugins = array(); | ||
| 137 | |||
| 138 |         foreach ($Plugins as $Plugin) { | ||
| 139 | $form = $this->formFactory | ||
| 140 | ->createNamedBuilder( | ||
| 141 | 'form'.$Plugin->getId(), | ||
| 142 | PluginManagementType::class, | ||
| 143 | null, | ||
| 144 | array( | ||
| 145 | 'plugin_id' => $Plugin->getId(), | ||
| 146 | ) | ||
| 147 | ) | ||
| 148 | ->getForm(); | ||
| 149 | $pluginForms[$Plugin->getId()] = $form->createView(); | ||
| 150 | |||
| 151 |             try { | ||
| 152 | // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか) | ||
| 153 |                 $configPages[$Plugin->getCode()] = $app->url('plugin_'.$Plugin->getCode().'_config'); | ||
| 154 |             } catch (\Exception $e) { | ||
| 155 | // プラグインで設定画面のルートが定義されていない場合は無視 | ||
| 156 | } | ||
| 157 | |||
| 158 |             if ($Plugin->getSource() == 0) { | ||
| 159 | // 商品IDが設定されていない場合、非公式プラグイン | ||
| 160 | $unofficialPlugins[] = $Plugin; | ||
| 161 |             } else { | ||
| 162 | $officialPlugins[] = $Plugin; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | // Todo: Need new authentication mechanism | ||
| 167 | // オーナーズストアからダウンロード可能プラグイン情報を取得 | ||
| 168 | $authKey = $this->BaseInfo->getAuthenticationKey(); | ||
| 169 | //        if (!is_null($authKey)) { | ||
| 170 | |||
| 171 | // オーナーズストア通信 | ||
| 172 | $url = $this->appConfig['owners_store_url'].'?method=list'; | ||
| 173 | list($json, $info) = $this->getRequestApi($request, $authKey, $url, $app); | ||
| 174 | |||
| 175 | $officialPluginsDetail = []; | ||
| 176 |         if ($json) { | ||
| 177 | // 接続成功時 | ||
| 178 | $data = json_decode($json, true); | ||
| 179 |             if (isset($data['success'])) { | ||
| 180 | $success = $data['success']; | ||
| 181 |                 if ($success == '1') { | ||
| 182 |                     foreach ($data['item'] as $item) { | ||
| 183 |                         foreach ($officialPlugins as $key => $plugin) { | ||
| 184 |                             if ($plugin->getSource() == $item['product_id']) { | ||
| 185 | $officialPluginsDetail[$key] = $item; | ||
| 186 | $officialPluginsDetail[$key]['update_status'] = 0; | ||
| 187 |                                 if ($plugin->getVersion() != $item['version']) { | ||
| 188 | $officialPluginsDetail[$key]['update_status'] = 1; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | return [ | ||
| 198 | 'plugin_forms' => $pluginForms, | ||
| 199 | 'officialPlugins' => $officialPlugins, | ||
| 200 | 'unofficialPlugins' => $unofficialPlugins, | ||
| 201 | 'configPages' => $configPages, | ||
| 202 | 'unregisterdPlugins' => $unregisterdPlugins, | ||
| 203 | 'unregisterdPluginsConfigPages' => $unregisterdPluginsConfigPages, | ||
| 204 | 'officialPluginsDetail' => $officialPluginsDetail, | ||
| 205 | ]; | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * インストール済プラグインからのアップデート | ||
| 210 | * | ||
| 211 |      * @Method("POST") | ||
| 212 |      * @Route("/{_admin}/store/plugin/{id}/update", requirements={"id" = "\d+"}, name="admin_store_plugin_update") | ||
| 213 | */ | ||
| 214 | public function update(Application $app, Request $request, Plugin $Plugin) | ||
| 215 |     { | ||
| 216 | $form = $this->formFactory | ||
| 217 | ->createNamedBuilder( | ||
| 218 | 'form'.$Plugin->getId(), | ||
| 219 | PluginManagementType::class, | ||
| 220 | null, | ||
| 221 | array( | ||
| 222 | 'plugin_id' => null, // placeHolder | ||
| 223 | ) | ||
| 224 | ) | ||
| 225 | ->getForm(); | ||
| 226 | |||
| 227 | $message = ''; | ||
| 228 | |||
| 229 |         if ('POST' === $request->getMethod()) { | ||
| 230 | $form->handleRequest($request); | ||
| 231 | |||
| 232 |             if ($form->isValid()) { | ||
| 233 | |||
| 234 | $tmpDir = null; | ||
| 235 |                 try { | ||
| 236 | |||
| 237 | $formFile = $form['plugin_archive']->getData(); | ||
| 238 | |||
| 239 | $tmpDir = $this->pluginService->createTempDir(); | ||
| 240 | $tmpFile = sha1(Str::random(32)).'.'.$formFile->getClientOriginalExtension(); | ||
| 241 | |||
| 242 | $formFile->move($tmpDir, $tmpFile); | ||
| 243 | $this->pluginService->update($Plugin, $tmpDir.'/'.$tmpFile); | ||
| 244 | |||
| 245 | $fs = new Filesystem(); | ||
| 246 | $fs->remove($tmpDir); | ||
| 247 | |||
| 248 |                     $app->addSuccess('admin.plugin.update.complete', 'admin'); | ||
| 249 | |||
| 250 |                     return $app->redirect($app->url('admin_store_plugin')); | ||
| 251 | |||
| 252 |                 } catch (PluginException $e) { | ||
| 253 |                     if (!empty($tmpDir) && file_exists($tmpDir)) { | ||
| 254 | $fs = new Filesystem(); | ||
| 255 | $fs->remove($tmpDir); | ||
| 256 | } | ||
| 257 | $message = $e->getMessage(); | ||
| 258 | } | ||
| 259 |             } else { | ||
| 260 | $errors = $form->getErrors(true); | ||
| 261 |                 foreach ($errors as $error) { | ||
| 262 | $message = $error->getMessage(); | ||
| 263 | } | ||
| 264 | |||
| 265 | } | ||
| 266 | |||
| 267 | } | ||
| 268 | |||
| 269 | $app->addError($message, 'admin'); | ||
| 270 | |||
| 271 |         return $app->redirect($app->url('admin_store_plugin')); | ||
| 272 | } | ||
| 273 | |||
| 274 | |||
| 275 | /** | ||
| 276 | * 対象のプラグインを有効にします。 | ||
| 277 | * | ||
| 278 |      * @Method("PUT") | ||
| 279 |      * @Route("/{_admin}/store/plugin/{id}/enable", requirements={"id" = "\d+"}, name="admin_store_plugin_enable") | ||
| 280 | */ | ||
| 281 | View Code Duplication | public function enable(Application $app, Plugin $Plugin) | |
| 282 |     { | ||
| 283 | $this->isTokenValid($app); | ||
| 284 | |||
| 285 |         if ($Plugin->getEnable() == Constant::ENABLED) { | ||
| 286 |             $app->addError('admin.plugin.already.enable', 'admin'); | ||
| 287 |         } else { | ||
| 288 | $this->pluginService->enable($Plugin); | ||
| 289 |             $app->addSuccess('admin.plugin.enable.complete', 'admin'); | ||
| 290 | } | ||
| 291 | |||
| 292 |         return $app->redirect($app->url('admin_store_plugin')); | ||
| 293 | } | ||
| 294 | |||
| 295 | /** | ||
| 296 | * 対象のプラグインを無効にします。 | ||
| 297 | * | ||
| 298 |      * @Method("PUT") | ||
| 299 |      * @Route("/{_admin}/store/plugin/{id}/disable", requirements={"id" = "\d+"}, name="admin_store_plugin_disable") | ||
| 300 | */ | ||
| 301 | View Code Duplication | public function disable(Application $app, Plugin $Plugin) | |
| 302 |     { | ||
| 303 | $this->isTokenValid($app); | ||
| 304 | |||
| 305 |         if ($Plugin->getEnable() == Constant::ENABLED) { | ||
| 306 | $this->pluginService->disable($Plugin); | ||
| 307 |             $app->addSuccess('admin.plugin.disable.complete', 'admin'); | ||
| 308 |         } else { | ||
| 309 |             $app->addError('admin.plugin.already.disable', 'admin'); | ||
| 310 | } | ||
| 311 | |||
| 312 |         return $app->redirect($app->url('admin_store_plugin')); | ||
| 313 | } | ||
| 314 | |||
| 315 | |||
| 316 | /** | ||
| 317 | * 対象のプラグインを削除します。 | ||
| 318 | * | ||
| 319 |      * @Method("DELETE") | ||
| 320 |      * @Route("/{_admin}/store/plugin/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_uninstall") | ||
| 321 | */ | ||
| 322 | public function uninstall(Application $app, Plugin $Plugin) | ||
| 323 |     { | ||
| 324 | $this->isTokenValid($app); | ||
| 325 | |||
| 326 | $this->pluginService->uninstall($Plugin); | ||
| 327 | |||
| 328 |         $app->addSuccess('admin.plugin.uninstall.complete', 'admin'); | ||
| 329 | |||
| 330 |         return $app->redirect($app->url('admin_store_plugin')); | ||
| 331 | } | ||
| 332 | |||
| 333 | /** | ||
| 334 |      * @Route("/{_admin}/store/plugin/handler", name="admin_store_plugin_handler") | ||
| 335 |      * @Template("Store/plugin_handler.twig") | ||
| 336 | */ | ||
| 337 | public function handler(Application $app) | ||
| 338 |     { | ||
| 339 | $handlers = $this->pluginEventHandlerRepository->getHandlers(); | ||
| 340 | |||
| 341 | // 一次元配列からイベント毎の二次元配列に変換する | ||
| 342 | $HandlersPerEvent = array(); | ||
| 343 |         foreach ($handlers as $handler) { | ||
| 344 | $HandlersPerEvent[$handler->getEvent()][$handler->getHandlerType()][] = $handler; | ||
| 345 | } | ||
| 346 | |||
| 347 | return [ | ||
| 348 | 'handlersPerEvent' => $HandlersPerEvent, | ||
| 349 | ]; | ||
| 350 | } | ||
| 351 | |||
| 352 | /** | ||
| 353 |      * @Route("/{_admin}/store/plugin/handler_up/{id}", requirements={"id" = "\d+"}, name="admin_store_plugin_handler_up") | ||
| 354 | */ | ||
| 355 | View Code Duplication | public function handler_up(Application $app, PluginEventHandler $Handler) | |
| 362 | |||
| 363 | /** | ||
| 364 |      * @Route("/{_admin}/store/plugin/handler_down/{id}", requirements={"id" = "\d+"}, name="admin_store_plugin_handler_down") | ||
| 365 | */ | ||
| 366 | View Code Duplication | public function handler_down(Application $app, PluginEventHandler $Handler) | |
| 367 |     { | ||
| 368 | $repo = $this->pluginEventHandlerRepository; | ||
| 373 | |||
| 374 | /** | ||
| 375 | * プラグインファイルアップロード画面 | ||
| 376 | * | ||
| 377 |      * @Route("/{_admin}/store/plugin/install", name="admin_store_plugin_install") | ||
| 378 |      * @Template("Store/plugin_install.twig") | ||
| 379 | */ | ||
| 380 | public function install(Application $app, Request $request) | ||
| 440 | |||
| 441 | /** | ||
| 442 | * オーナーズストアプラグインインストール画面 | ||
| 443 | * | ||
| 444 |      * @Route("/{_admin}/store/plugin/owners_install", name="admin_store_plugin_owners_install") | ||
| 445 |      * @Template("Store/plugin_owners_install.twig") | ||
| 446 | */ | ||
| 447 | public function ownersInstall(Application $app, Request $request) | ||
| 448 |     { | ||
| 449 | // オーナーズストアからダウンロード可能プラグイン情報を取得 | ||
| 450 | $authKey = $this->BaseInfo->getAuthenticationKey(); | ||
| 451 | $authResult = true; | ||
| 452 | $success = 0; | ||
| 453 | $items = array(); | ||
| 454 | $promotionItems = array(); | ||
| 455 | $message = ''; | ||
| 456 |         if (!is_null($authKey)) { | ||
| 457 | |||
| 458 | // オーナーズストア通信 | ||
| 459 | $url = $this->appConfig['owners_store_url'].'?method=list'; | ||
| 460 | list($json, $info) = $this->getRequestApi($request, $authKey, $url, $app); | ||
| 461 | |||
| 462 |             if ($json === false) { | ||
| 463 | // 接続失敗時 | ||
| 464 | $success = 0; | ||
| 465 | |||
| 466 | $message = $this->getResponseErrorMessage($info); | ||
| 467 | |||
| 468 |             } else { | ||
| 469 | // 接続成功時 | ||
| 470 | |||
| 471 | $data = json_decode($json, true); | ||
| 472 | |||
| 473 |                 if (isset($data['success'])) { | ||
| 474 | $success = $data['success']; | ||
| 475 |                     if ($success == '1') { | ||
| 476 | $items = array(); | ||
| 477 | |||
| 478 | // 既にインストールされているかどうか確認 | ||
| 479 | $Plugins = $this->pluginRepository->findAll(); | ||
| 480 | $status = false; | ||
| 481 | // update_status 1 : 未インストール、2 : インストール済、 3 : 更新あり、4 : 有料購入 | ||
| 482 |                         foreach ($data['item'] as $item) { | ||
| 483 |                             foreach ($Plugins as $plugin) { | ||
| 484 |                                 if ($plugin->getSource() == $item['product_id']) { | ||
| 485 |                                     if ($plugin->getVersion() == $item['version']) { | ||
| 486 | // バージョンが同じ | ||
| 487 | $item['update_status'] = 2; | ||
| 488 |                                     } else { | ||
| 489 | // バージョンが異なる | ||
| 490 | $item['update_status'] = 3; | ||
| 491 | } | ||
| 492 | $items[] = $item; | ||
| 493 | $status = true; | ||
| 494 | break; | ||
| 495 | } | ||
| 496 | } | ||
| 497 |                             if (!$status) { | ||
| 498 | // 未インストール | ||
| 499 | $item['update_status'] = 1; | ||
| 500 | $items[] = $item; | ||
| 501 | } | ||
| 502 | $status = false; | ||
| 503 | } | ||
| 504 | |||
| 505 | // EC-CUBEのバージョンチェック | ||
| 506 | // 参照渡しをして値を追加 | ||
| 507 |                         foreach ($items as &$item) { | ||
| 508 |                             if (in_array(Constant::VERSION, $item['eccube_version'])) { | ||
| 509 | // 対象バージョン | ||
| 510 | $item['version_check'] = 1; | ||
| 511 |                             } else { | ||
| 512 | // 未対象バージョン | ||
| 513 | $item['version_check'] = 0; | ||
| 514 | } | ||
| 515 |                             if ($item['price'] != '0' && $item['purchased'] == '0') { | ||
| 516 | // 有料商品で未購入 | ||
| 517 | $item['update_status'] = 4; | ||
| 518 | } | ||
| 519 | } | ||
| 520 | unset($item); | ||
| 521 | |||
| 522 | // promotionアイテム | ||
| 523 | $i = 0; | ||
| 524 | View Code Duplication |                         foreach ($items as $item) { | |
| 525 |                             if ($item['promotion'] == 1) { | ||
| 526 | $promotionItems[] = $item; | ||
| 527 | unset($items[$i]); | ||
| 528 | } | ||
| 529 | $i++; | ||
| 530 | } | ||
| 531 | |||
| 532 |                     } else { | ||
| 533 | $message = $data['error_code'].' : '.$data['error_message']; | ||
| 534 | } | ||
| 535 |                 } else { | ||
| 536 | $success = 0; | ||
| 537 | $message = "EC-CUBEオーナーズストアにエラーが発生しています。"; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 |         } else { | ||
| 542 | $authResult = false; | ||
| 543 | } | ||
| 544 | |||
| 545 | return [ | ||
| 546 | 'authResult' => $authResult, | ||
| 547 | 'success' => $success, | ||
| 548 | 'items' => $items, | ||
| 549 | 'promotionItems' => $promotionItems, | ||
| 550 | 'message' => $message, | ||
| 551 | ]; | ||
| 552 | } | ||
| 553 | |||
| 554 | /** | ||
| 555 | * オーナーズブラグインインストール、アップデート | ||
| 556 | * | ||
| 557 |      * @Route("/{_admin}/store/plugin/upgrade/{action}/{id}/{version}", requirements={"id" = "\d+"}, name="admin_store_plugin_upgrade") | ||
| 558 | */ | ||
| 559 | public function upgrade(Application $app, Request $request, $action, $id, $version) | ||
| 651 | |||
| 652 | /** | ||
| 653 | * 認証キー設定画面 | ||
| 654 | * | ||
| 655 |      * @Route("/{_admin}/store/plugin/authentication_setting", name="admin_store_authentication_setting") | ||
| 656 |      * @Template("Store/authentication_setting.twig") | ||
| 657 | */ | ||
| 658 | public function authenticationSetting(Application $app, Request $request) | ||
| 688 | |||
| 689 | |||
| 690 | /** | ||
| 691 | * APIリクエスト処理 | ||
| 692 | * | ||
| 693 | * @param Request $request | ||
| 694 | * @param $authKey | ||
| 695 | * @param string $url | ||
| 696 | * @param Application $app | ||
| 697 | * @return array | ||
| 698 | */ | ||
| 699 | private function getRequestApi(Request $request, $authKey, $url, $app) | ||
| 729 | |||
| 730 | /** | ||
| 731 | * レスポンスのチェック | ||
| 732 | * | ||
| 733 | * @param $info | ||
| 734 | * @return string | ||
| 735 | */ | ||
| 736 | View Code Duplication | private function getResponseErrorMessage($info) | |
| 750 | |||
| 751 | |||
| 752 | /** | ||
| 753 | * フォルダ設置のみのプラグインを取得する. | ||
| 754 | * | ||
| 755 | * @param array $plugins | ||
| 756 | * @param Application $app | ||
| 757 | * @return array | ||
| 758 | */ | ||
| 759 | protected function getUnregisteredPlugins(array $plugins, \Eccube\Application $app) | ||
| 795 | } | ||
| 796 |