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 |
||
60 | class PluginController extends AbstractController |
||
61 | { |
||
62 | /** |
||
63 | * @Inject("orm.em") |
||
64 | * @var EntityManager |
||
65 | */ |
||
66 | protected $entityManager; |
||
67 | |||
68 | /** |
||
69 | * @Inject("monolog") |
||
70 | * @var Logger |
||
71 | */ |
||
72 | protected $logger; |
||
73 | |||
74 | /** |
||
75 | * @Inject(PluginEventHandlerRepository::class) |
||
76 | * @var PluginEventHandlerRepository |
||
77 | */ |
||
78 | protected $pluginEventHandlerRepository; |
||
79 | |||
80 | /** |
||
81 | * @Inject(PluginService::class) |
||
82 | * @var PluginService |
||
83 | */ |
||
84 | protected $pluginService; |
||
85 | |||
86 | /** |
||
87 | * @Inject("config") |
||
88 | * @var array |
||
89 | */ |
||
90 | protected $appConfig; |
||
91 | |||
92 | /** |
||
93 | * @Inject(BaseInfo::class) |
||
94 | * @var BaseInfo |
||
95 | */ |
||
96 | protected $BaseInfo; |
||
97 | |||
98 | /** |
||
99 | * @Inject("form.factory") |
||
100 | * @var FormFactory |
||
101 | */ |
||
102 | protected $formFactory; |
||
103 | |||
104 | /** |
||
105 | * @Inject(PluginRepository::class) |
||
106 | * @var PluginRepository |
||
107 | */ |
||
108 | protected $pluginRepository; |
||
109 | |||
110 | |||
111 | /** |
||
112 | * インストール済プラグイン画面 |
||
113 | * |
||
114 | * @Route("/{_admin}/store/plugin", name="admin_store_plugin") |
||
115 | * @Template("Store/plugin.twig") |
||
116 | */ |
||
117 | public function index(Application $app, Request $request) |
||
118 | { |
||
119 | $pluginForms = array(); |
||
120 | $configPages = array(); |
||
121 | $Plugins = $this->pluginRepository->findBy(array(), array('code' => 'ASC')); |
||
122 | |||
123 | // ファイル設置プラグインの取得. |
||
124 | $unregisterdPlugins = $this->getUnregisteredPlugins($Plugins, $app); |
||
125 | $unregisterdPluginsConfigPages = array(); |
||
126 | foreach ($unregisterdPlugins as $unregisterdPlugin) { |
||
127 | try { |
||
128 | $code = $unregisterdPlugin['code']; |
||
129 | // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか) |
||
130 | $unregisterdPluginsConfigPages[$code] = $app->url('plugin_'.$code.'_config'); |
||
131 | } catch (RouteNotFoundException $e) { |
||
132 | // プラグインで設定画面のルートが定義されていない場合は無視 |
||
133 | } |
||
134 | } |
||
135 | |||
136 | $officialPlugins = array(); |
||
137 | $unofficialPlugins = array(); |
||
138 | |||
139 | foreach ($Plugins as $Plugin) { |
||
140 | $form = $this->formFactory |
||
141 | ->createNamedBuilder( |
||
142 | 'form'.$Plugin->getId(), |
||
143 | PluginManagementType::class, |
||
144 | null, |
||
145 | array( |
||
146 | 'plugin_id' => $Plugin->getId(), |
||
147 | ) |
||
148 | ) |
||
149 | ->getForm(); |
||
150 | $pluginForms[$Plugin->getId()] = $form->createView(); |
||
151 | |||
152 | try { |
||
153 | // プラグイン用設定画面があれば表示(プラグイン用のサービスプロバイダーに定義されているか) |
||
154 | $configPages[$Plugin->getCode()] = $app->url('plugin_'.$Plugin->getCode().'_config'); |
||
155 | } catch (\Exception $e) { |
||
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 | // オーナーズストア通信 |
||
170 | $url = $this->appConfig['package_repo_url'].'/search/packages.json'; |
||
171 | list($json, $info) = $this->getRequestApi($request, $authKey, $url, $app); |
||
172 | |||
173 | $officialPluginsDetail = []; |
||
174 | if ($json) { |
||
175 | // 接続成功時 |
||
176 | $data = json_decode($json, true); |
||
177 | if (isset($data['success']) && $data['success']) { |
||
178 | foreach ($data['item'] as $item) { |
||
179 | foreach ($officialPlugins as $key => $plugin) { |
||
180 | if ($plugin->getSource() == $item['product_id']) { |
||
181 | $officialPluginsDetail[$key] = $item; |
||
182 | $officialPluginsDetail[$key]['update_status'] = 0; |
||
183 | if ($this->pluginService->isUpdate($plugin->getVersion(), $item['version'])) { |
||
184 | $officialPluginsDetail[$key]['update_status'] = 1; |
||
185 | } |
||
186 | } |
||
187 | } |
||
188 | } |
||
189 | } |
||
190 | } |
||
191 | |||
192 | return [ |
||
193 | 'plugin_forms' => $pluginForms, |
||
194 | 'officialPlugins' => $officialPlugins, |
||
195 | 'unofficialPlugins' => $unofficialPlugins, |
||
196 | 'configPages' => $configPages, |
||
197 | 'unregisterdPlugins' => $unregisterdPlugins, |
||
198 | 'unregisterdPluginsConfigPages' => $unregisterdPluginsConfigPages, |
||
199 | 'officialPluginsDetail' => $officialPluginsDetail, |
||
200 | ]; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * インストール済プラグインからのアップデート |
||
205 | * |
||
206 | * @Method("POST") |
||
207 | * @Route("/{_admin}/store/plugin/{id}/update", requirements={"id" = "\d+"}, name="admin_store_plugin_update") |
||
208 | * @param Application $app |
||
209 | * @param Request $request |
||
210 | * @param Plugin $Plugin |
||
211 | * @return RedirectResponse |
||
212 | */ |
||
213 | public function update(Application $app, Request $request, Plugin $Plugin) |
||
214 | { |
||
215 | $form = $this->formFactory |
||
216 | ->createNamedBuilder( |
||
217 | 'form'.$Plugin->getId(), |
||
218 | PluginManagementType::class, |
||
219 | null, |
||
220 | array( |
||
221 | 'plugin_id' => null, // placeHolder |
||
222 | ) |
||
223 | ) |
||
224 | ->getForm(); |
||
225 | |||
226 | $message = ''; |
||
227 | $form->handleRequest($request); |
||
228 | if ($form->isSubmitted() && $form->isValid()) { |
||
229 | $tmpDir = null; |
||
230 | try { |
||
231 | $formFile = $form['plugin_archive']->getData(); |
||
232 | $tmpDir = $this->pluginService->createTempDir(); |
||
233 | $tmpFile = sha1(StringUtil::random(32)).'.'.$formFile->getClientOriginalExtension(); |
||
234 | $formFile->move($tmpDir, $tmpFile); |
||
235 | $this->pluginService->update($Plugin, $tmpDir.'/'.$tmpFile); |
||
236 | $fs = new Filesystem(); |
||
237 | $fs->remove($tmpDir); |
||
238 | $app->addSuccess('admin.plugin.update.complete', 'admin'); |
||
239 | |||
240 | return $app->redirect($app->url('admin_store_plugin')); |
||
241 | } catch (PluginException $e) { |
||
242 | if (!empty($tmpDir) && file_exists($tmpDir)) { |
||
243 | $fs = new Filesystem(); |
||
244 | $fs->remove($tmpDir); |
||
245 | } |
||
246 | $message = $e->getMessage(); |
||
247 | } catch (\Exception $er) { |
||
248 | // Catch composer install error | Other error |
||
249 | if (!empty($tmpDir) && file_exists($tmpDir)) { |
||
250 | $fs = new Filesystem(); |
||
251 | $fs->remove($tmpDir); |
||
252 | } |
||
253 | $this->logger->error("plugin install failed.", array('original-message' => $er->getMessage())); |
||
254 | $message = 'admin.plugin.install.fail'; |
||
255 | } |
||
256 | } else { |
||
257 | $errors = $form->getErrors(true); |
||
258 | foreach ($errors as $error) { |
||
259 | $message = $error->getMessage(); |
||
260 | } |
||
261 | } |
||
262 | |||
263 | $app->addError($message, 'admin'); |
||
264 | |||
265 | return $app->redirect($app->url('admin_store_plugin')); |
||
266 | } |
||
267 | |||
268 | |||
269 | /** |
||
270 | * 対象のプラグインを有効にします。 |
||
271 | * |
||
272 | * @Method("PUT") |
||
273 | * @Route("/{_admin}/store/plugin/{id}/enable", requirements={"id" = "\d+"}, name="admin_store_plugin_enable") |
||
274 | * @param Application $app |
||
275 | * @param Plugin $Plugin |
||
276 | * @return RedirectResponse |
||
277 | */ |
||
278 | View Code Duplication | public function enable(Application $app, Plugin $Plugin) |
|
279 | { |
||
280 | $this->isTokenValid($app); |
||
281 | |||
282 | if ($Plugin->isEnabled()) { |
||
283 | $app->addError('admin.plugin.already.enable', 'admin'); |
||
284 | } else { |
||
285 | $requires = $this->pluginService->findRequirePluginNeedEnable($Plugin->getCode()); |
||
286 | if (!empty($requires)) { |
||
287 | $DependPlugin = $this->pluginRepository->findOneBy(['code' => $requires[0]]); |
||
288 | $dependName = $requires[0]; |
||
289 | if ($DependPlugin) { |
||
290 | $dependName = $DependPlugin->getName(); |
||
291 | } |
||
292 | $message = $app->trans('admin.plugin.enable.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]); |
||
293 | $app->addError($message, 'admin'); |
||
294 | |||
295 | return $app->redirect($app->url('admin_store_plugin')); |
||
296 | } |
||
297 | $this->pluginService->enable($Plugin); |
||
298 | $app->addSuccess('admin.plugin.enable.complete', 'admin'); |
||
299 | } |
||
300 | |||
301 | return $app->redirect($app->url('admin_store_plugin')); |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * 対象のプラグインを無効にします。 |
||
306 | * |
||
307 | * @Method("PUT") |
||
308 | * @Route("/{_admin}/store/plugin/{id}/disable", requirements={"id" = "\d+"}, name="admin_store_plugin_disable") |
||
309 | * @param Application $app |
||
310 | * @param Plugin $Plugin |
||
311 | * @return RedirectResponse |
||
312 | */ |
||
313 | View Code Duplication | public function disable(Application $app, Plugin $Plugin) |
|
314 | { |
||
315 | $this->isTokenValid($app); |
||
316 | |||
317 | if ($Plugin->isEnabled()) { |
||
318 | $dependents = $this->pluginService->findDependentPluginNeedDisable($Plugin->getCode()); |
||
319 | if (!empty($dependents)) { |
||
320 | $dependName = $dependents[0]; |
||
321 | $DependPlugin = $this->pluginRepository->findOneBy(['code' => $dependents[0]]); |
||
322 | if ($DependPlugin) { |
||
323 | $dependName = $DependPlugin->getName(); |
||
324 | } |
||
325 | $message = $app->trans('admin.plugin.disable.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]); |
||
326 | $app->addError($message, 'admin'); |
||
327 | |||
328 | return $app->redirect($app->url('admin_store_plugin')); |
||
329 | } |
||
330 | |||
331 | $this->pluginService->disable($Plugin); |
||
332 | $app->addSuccess('admin.plugin.disable.complete', 'admin'); |
||
333 | } else { |
||
334 | $app->addError('admin.plugin.already.disable', 'admin'); |
||
335 | } |
||
336 | |||
337 | return $app->redirect($app->url('admin_store_plugin')); |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * 対象のプラグインを削除します。 |
||
342 | * |
||
343 | * @Method("DELETE") |
||
344 | * @Route("/{_admin}/store/plugin/{id}/uninstall", requirements={"id" = "\d+"}, name="admin_store_plugin_uninstall") |
||
345 | * @param Application $app |
||
346 | * @param Plugin $Plugin |
||
347 | * @return RedirectResponse |
||
348 | */ |
||
349 | public function uninstall(Application $app, Plugin $Plugin) |
||
350 | { |
||
351 | $this->isTokenValid($app); |
||
352 | |||
353 | if ($Plugin->isEnabled()) { |
||
354 | $app->addError('admin.plugin.uninstall.error.not_disable', 'admin'); |
||
355 | |||
356 | return $app->redirect($app->url('admin_store_plugin')); |
||
357 | } |
||
358 | |||
359 | // Check other plugin depend on it |
||
360 | $pluginCode = $Plugin->getCode(); |
||
361 | $otherDepend = $this->pluginService->findDependentPlugin($pluginCode); |
||
362 | if (!empty($otherDepend)) { |
||
363 | $DependPlugin = $this->pluginRepository->findOneBy(['code' => $otherDepend[0]]); |
||
364 | $dependName = $otherDepend[0]; |
||
365 | if ($DependPlugin) { |
||
366 | $dependName = $DependPlugin->getName(); |
||
367 | } |
||
368 | $message = $app->trans('admin.plugin.uninstall.depend', ['%name%' => $Plugin->getName(), '%depend_name%' => $dependName]); |
||
369 | $app->addError($message, 'admin'); |
||
370 | |||
371 | return $app->redirect($app->url('admin_store_plugin')); |
||
372 | } |
||
373 | |||
374 | $this->pluginService->uninstall($Plugin); |
||
375 | $app->addSuccess('admin.plugin.uninstall.complete', 'admin'); |
||
376 | |||
377 | return $app->redirect($app->url('admin_store_plugin')); |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * @Route("/{_admin}/store/plugin/handler", name="admin_store_plugin_handler") |
||
382 | * @Template("Store/plugin_handler.twig") |
||
383 | */ |
||
384 | public function handler(Application $app) |
||
398 | |||
399 | /** |
||
400 | * @Route("/{_admin}/store/plugin/handler_up/{id}", requirements={"id" = "\d+"}, name="admin_store_plugin_handler_up") |
||
401 | */ |
||
402 | View Code Duplication | public function handler_up(Application $app, PluginEventHandler $Handler) |
|
409 | |||
410 | /** |
||
411 | * @Route("/{_admin}/store/plugin/handler_down/{id}", requirements={"id" = "\d+"}, name="admin_store_plugin_handler_down") |
||
412 | */ |
||
413 | View Code Duplication | public function handler_down(Application $app, PluginEventHandler $Handler) |
|
420 | |||
421 | /** |
||
422 | * プラグインファイルアップロード画面 |
||
423 | * |
||
424 | * @Route("/{_admin}/store/plugin/install", name="admin_store_plugin_install") |
||
425 | * @Template("Store/plugin_install.twig") |
||
426 | * @param Application $app |
||
427 | * @param Request $request |
||
428 | * @return array|RedirectResponse |
||
429 | */ |
||
430 | public function install(Application $app, Request $request) |
||
482 | |||
483 | /** |
||
484 | * 認証キー設定画面 |
||
485 | * |
||
486 | * @Route("/{_admin}/store/plugin/authentication_setting", name="admin_store_authentication_setting") |
||
487 | * @Template("Store/authentication_setting.twig") |
||
488 | */ |
||
489 | public function authenticationSetting(Application $app, Request $request) |
||
519 | |||
520 | |||
521 | /** |
||
522 | * APIリクエスト処理 |
||
523 | * |
||
524 | * @param Request $request |
||
525 | * @param $authKey |
||
526 | * @param string $url |
||
527 | * @param Application $app |
||
528 | * @return array |
||
529 | */ |
||
530 | private function getRequestApi(Request $request, $authKey, $url, $app) |
||
560 | |||
561 | /** |
||
562 | * レスポンスのチェック |
||
563 | * |
||
564 | * @param $info |
||
565 | * @return string |
||
566 | */ |
||
567 | View Code Duplication | private function getResponseErrorMessage($info) |
|
581 | |||
582 | |||
583 | /** |
||
584 | * フォルダ設置のみのプラグインを取得する. |
||
585 | * |
||
586 | * @param array $plugins |
||
587 | * @param Application $app |
||
588 | * @return array |
||
589 | */ |
||
590 | protected function getUnregisteredPlugins(array $plugins, \Eccube\Application $app) |
||
626 | } |
||
627 |