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 |