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 TinyMCEConfig 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 TinyMCEConfig, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class TinyMCEConfig extends HTMLEditorConfig |
||
20 | { |
||
21 | /** |
||
22 | * @config |
||
23 | * @var array |
||
24 | */ |
||
25 | private static $tinymce_lang = [ |
||
26 | 'ar_EG' => 'ar', |
||
27 | 'ca_AD' => 'ca', |
||
28 | 'ca_ES' => 'ca', |
||
29 | 'cs_CZ' => 'cs', |
||
30 | 'cy_GB' => 'cy', |
||
31 | 'da_DK' => 'da', |
||
32 | 'da_GL' => 'da', |
||
33 | 'de_AT' => 'de', |
||
34 | 'de_BE' => 'de', |
||
35 | 'de_CH' => 'de', |
||
36 | 'de_DE' => 'de', |
||
37 | 'de_LI' => 'de', |
||
38 | 'de_LU' => 'de', |
||
39 | 'de_BR' => 'de', |
||
40 | 'de_US' => 'de', |
||
41 | 'el_CY' => 'el', |
||
42 | 'el_GR' => 'el', |
||
43 | 'es_AR' => 'es', |
||
44 | 'es_BO' => 'es', |
||
45 | 'es_CL' => 'es', |
||
46 | 'es_CO' => 'es', |
||
47 | 'es_CR' => 'es', |
||
48 | 'es_CU' => 'es', |
||
49 | 'es_DO' => 'es', |
||
50 | 'es_EC' => 'es', |
||
51 | 'es_ES' => 'es', |
||
52 | 'es_GQ' => 'es', |
||
53 | 'es_GT' => 'es', |
||
54 | 'es_HN' => 'es', |
||
55 | 'es_MX' => 'es', |
||
56 | 'es_NI' => 'es', |
||
57 | 'es_PA' => 'es', |
||
58 | 'es_PE' => 'es', |
||
59 | 'es_PH' => 'es', |
||
60 | 'es_PR' => 'es', |
||
61 | 'es_PY' => 'es', |
||
62 | 'es_SV' => 'es', |
||
63 | 'es_UY' => 'es', |
||
64 | 'es_VE' => 'es', |
||
65 | 'es_AD' => 'es', |
||
66 | 'es_BZ' => 'es', |
||
67 | 'es_US' => 'es', |
||
68 | 'fa_AF' => 'fa', |
||
69 | 'fa_IR' => 'fa', |
||
70 | 'fa_PK' => 'fa', |
||
71 | 'fi_FI' => 'fi', |
||
72 | 'fi_SE' => 'fi', |
||
73 | 'fr_BE' => 'fr', |
||
74 | 'fr_BF' => 'fr', |
||
75 | 'fr_BI' => 'fr', |
||
76 | 'fr_BJ' => 'fr', |
||
77 | 'fr_CA' => 'fr_ca', |
||
78 | 'fr_CF' => 'fr', |
||
79 | 'fr_CG' => 'fr', |
||
80 | 'fr_CH' => 'fr', |
||
81 | 'fr_CI' => 'fr', |
||
82 | 'fr_CM' => 'fr', |
||
83 | 'fr_DJ' => 'fr', |
||
84 | 'fr_DZ' => 'fr', |
||
85 | 'fr_FR' => 'fr', |
||
86 | 'fr_GA' => 'fr', |
||
87 | 'fr_GF' => 'fr', |
||
88 | 'fr_GN' => 'fr', |
||
89 | 'fr_GP' => 'fr', |
||
90 | 'fr_HT' => 'fr', |
||
91 | 'fr_KM' => 'fr', |
||
92 | 'fr_LU' => 'fr', |
||
93 | 'fr_MA' => 'fr', |
||
94 | 'fr_MC' => 'fr', |
||
95 | 'fr_MG' => 'fr', |
||
96 | 'fr_ML' => 'fr', |
||
97 | 'fr_MQ' => 'fr', |
||
98 | 'fr_MU' => 'fr', |
||
99 | 'fr_NC' => 'fr', |
||
100 | 'fr_NE' => 'fr', |
||
101 | 'fr_PF' => 'fr', |
||
102 | 'fr_PM' => 'fr', |
||
103 | 'fr_RE' => 'fr', |
||
104 | 'fr_RW' => 'fr', |
||
105 | 'fr_SC' => 'fr', |
||
106 | 'fr_SN' => 'fr', |
||
107 | 'fr_SY' => 'fr', |
||
108 | 'fr_TD' => 'fr', |
||
109 | 'fr_TG' => 'fr', |
||
110 | 'fr_TN' => 'fr', |
||
111 | 'fr_VU' => 'fr', |
||
112 | 'fr_WF' => 'fr', |
||
113 | 'fr_YT' => 'fr', |
||
114 | 'fr_GB' => 'fr', |
||
115 | 'fr_US' => 'fr', |
||
116 | 'he_IL' => 'he', |
||
117 | 'hu_HU' => 'hu', |
||
118 | 'hu_AT' => 'hu', |
||
119 | 'hu_RO' => 'hu', |
||
120 | 'hu_RS' => 'hu', |
||
121 | 'is_IS' => 'is', |
||
122 | 'it_CH' => 'it', |
||
123 | 'it_IT' => 'it', |
||
124 | 'it_SM' => 'it', |
||
125 | 'it_FR' => 'it', |
||
126 | 'it_HR' => 'it', |
||
127 | 'it_US' => 'it', |
||
128 | 'it_VA' => 'it', |
||
129 | 'ja_JP' => 'ja', |
||
130 | 'ko_KP' => 'ko', |
||
131 | 'ko_KR' => 'ko', |
||
132 | 'ko_CN' => 'ko', |
||
133 | 'mi_NZ' => 'mi_NZ', |
||
134 | 'nb_NO' => 'nb', |
||
135 | 'nb_SJ' => 'nb', |
||
136 | 'nl_AN' => 'nl', |
||
137 | 'nl_AW' => 'nl', |
||
138 | 'nl_BE' => 'nl', |
||
139 | 'nl_NL' => 'nl', |
||
140 | 'nl_SR' => 'nl', |
||
141 | 'nn_NO' => 'nn', |
||
142 | 'pl_PL' => 'pl', |
||
143 | 'pl_UA' => 'pl', |
||
144 | 'pt_AO' => 'pt', |
||
145 | 'pt_BR' => 'pt', |
||
146 | 'pt_CV' => 'pt', |
||
147 | 'pt_GW' => 'pt', |
||
148 | 'pt_MZ' => 'pt', |
||
149 | 'pt_PT' => 'pt', |
||
150 | 'pt_ST' => 'pt', |
||
151 | 'pt_TL' => 'pt', |
||
152 | 'ro_MD' => 'ro', |
||
153 | 'ro_RO' => 'ro', |
||
154 | 'ro_RS' => 'ro', |
||
155 | 'ru_BY' => 'ru', |
||
156 | 'ru_KG' => 'ru', |
||
157 | 'ru_KZ' => 'ru', |
||
158 | 'ru_RU' => 'ru', |
||
159 | 'ru_SJ' => 'ru', |
||
160 | 'ru_UA' => 'ru', |
||
161 | 'si_LK' => 'si', |
||
162 | 'sk_SK' => 'sk', |
||
163 | 'sk_RS' => 'sk', |
||
164 | 'sq_AL' => 'sq', |
||
165 | 'sr_BA' => 'sr', |
||
166 | 'sr_ME' => 'sr', |
||
167 | 'sr_RS' => 'sr', |
||
168 | 'sv_FI' => 'sv', |
||
169 | 'sv_SE' => 'sv', |
||
170 | 'tr_CY' => 'tr', |
||
171 | 'tr_TR' => 'tr', |
||
172 | 'tr_DE' => 'tr', |
||
173 | 'tr_MK' => 'tr', |
||
174 | 'uk_UA' => 'uk', |
||
175 | 'vi_VN' => 'vi', |
||
176 | 'vi_US' => 'vi', |
||
177 | 'zh_CN' => 'zh-cn', |
||
178 | 'zh_HK' => 'zh-cn', |
||
179 | 'zh_MO' => 'zh-cn', |
||
180 | 'zh_SG' => 'zh-cn', |
||
181 | 'zh_TW' => 'zh-tw', |
||
182 | 'zh_ID' => 'zh-cn', |
||
183 | 'zh_MY' => 'zh-cn', |
||
184 | 'zh_TH' => 'zh-cn', |
||
185 | 'zh_US' => 'zn-cn', |
||
186 | ]; |
||
187 | |||
188 | /** |
||
189 | * Location of module relative to BASE_DIR. This must contain the following dirs |
||
190 | * - plugins |
||
191 | * - themes |
||
192 | * - skins |
||
193 | * |
||
194 | * If left blank defaults to [admin dir]/tinyme |
||
195 | * |
||
196 | * @config |
||
197 | * @var string |
||
198 | */ |
||
199 | private static $base_dir = null; |
||
200 | |||
201 | /** |
||
202 | * TinyMCE JS settings |
||
203 | * |
||
204 | * @link https://www.tinymce.com/docs/configure/ |
||
205 | * |
||
206 | * @var array |
||
207 | */ |
||
208 | protected $settings = array( |
||
209 | 'fix_list_elements' => true, // https://www.tinymce.com/docs/configure/content-filtering/#fix_list_elements |
||
210 | 'friendly_name' => '(Please set a friendly name for this config)', |
||
211 | 'priority' => 0, // used for Per-member config override |
||
212 | 'browser_spellcheck' => true, |
||
213 | 'body_class' => 'typography', |
||
214 | 'elementpath' => false, // https://www.tinymce.com/docs/configure/editor-appearance/#elementpath |
||
215 | 'relative_urls' => true, |
||
216 | 'remove_script_host' => true, |
||
217 | 'convert_urls' => false, // Prevent site-root images being rewritten to base relative |
||
218 | 'menubar' => false, |
||
219 | 'language' => 'en', |
||
220 | ); |
||
221 | |||
222 | /** |
||
223 | * Holder list of enabled plugins |
||
224 | * |
||
225 | * @var array |
||
226 | */ |
||
227 | protected $plugins = array( |
||
228 | 'table' => null, |
||
229 | 'emoticons' => null, |
||
230 | 'paste' => null, |
||
231 | 'code' => null, |
||
232 | 'link' => null, |
||
233 | 'importcss' => null, |
||
234 | ); |
||
235 | |||
236 | /** |
||
237 | * Theme name |
||
238 | * |
||
239 | * @var string |
||
240 | */ |
||
241 | protected $theme = 'modern'; |
||
242 | |||
243 | /** |
||
244 | * Get the theme |
||
245 | * |
||
246 | * @return string |
||
247 | */ |
||
248 | public function getTheme() |
||
252 | |||
253 | /** |
||
254 | * Set the theme name |
||
255 | * |
||
256 | * @param string $theme |
||
257 | * @return $this |
||
258 | */ |
||
259 | public function setTheme($theme) |
||
264 | |||
265 | /** |
||
266 | * Holder list of buttons, organised by line. This array is 1-based indexed array |
||
267 | * |
||
268 | * {@link https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols} |
||
269 | * |
||
270 | * @var array |
||
271 | */ |
||
272 | protected $buttons = array( |
||
273 | 1 => array( |
||
274 | 'bold', 'italic', 'underline', 'removeformat', '|', |
||
275 | 'alignleft', 'aligncenter', 'alignright', 'alignjustify', '|', |
||
276 | 'bullist', 'numlist', 'outdent', 'indent', |
||
277 | ), |
||
278 | 2 => array( |
||
279 | 'formatselect', '|', |
||
280 | 'paste', 'pastetext', '|', |
||
281 | 'table', 'sslink', 'unlink', '|', |
||
282 | 'code' |
||
283 | ), |
||
284 | 3 => array() |
||
285 | ); |
||
286 | |||
287 | public function getOption($key) |
||
294 | |||
295 | public function setOption($key, $value) |
||
300 | |||
301 | public function setOptions($options) |
||
308 | |||
309 | /** |
||
310 | * Get all settings |
||
311 | * |
||
312 | * @return array |
||
313 | */ |
||
314 | protected function getSettings() |
||
318 | |||
319 | public function getAttributes() |
||
326 | |||
327 | /** |
||
328 | * Enable one or several plugins. Will maintain unique list if already |
||
329 | * enabled plugin is re-passed. If passed in as a map of plugin-name to path, |
||
330 | * the plugin will be loaded by tinymce.PluginManager.load() instead of through tinyMCE.init(). |
||
331 | * Keep in mind that these externals plugins require a dash-prefix in their name. |
||
332 | * |
||
333 | * @see http://wiki.moxiecode.com/index.php/TinyMCE:API/tinymce.PluginManager/load |
||
334 | * |
||
335 | * If passing in a non-associative array, the plugin name should be located in the standard tinymce |
||
336 | * plugins folder. |
||
337 | * |
||
338 | * If passing in an associative array, the key of each item should be the plugin name. |
||
339 | * The value of each item is one of: |
||
340 | * - null - Will be treated as a stardard plugin in the standard location |
||
341 | * - relative path - Will be treated as a relative url |
||
342 | * - absolute url - Some url to an external plugin |
||
343 | * |
||
344 | * @param string|array $plugin,... a string, or several strings, or a single array of strings - The plugins to enable |
||
|
|||
345 | * @return $this |
||
346 | */ |
||
347 | public function enablePlugins($plugin) |
||
365 | |||
366 | /** |
||
367 | * Enable one or several plugins. Will properly handle being passed a plugin that is already disabled |
||
368 | * @param string $plugin,... a string, or several strings, or a single array of strings - The plugins to enable |
||
369 | * @return $this |
||
370 | */ |
||
371 | public function disablePlugins($plugin) |
||
382 | |||
383 | /** |
||
384 | * Gets the list of all enabled plugins as an associative array. |
||
385 | * Array keys are the plugin names, and values are potentially the plugin location |
||
386 | * |
||
387 | * @return array |
||
388 | */ |
||
389 | public function getPlugins() |
||
393 | |||
394 | /** |
||
395 | * Get list of plugins without custom locations, which is the set of |
||
396 | * plugins which can be loaded via the standard plugin path, and could |
||
397 | * potentially be minified |
||
398 | * |
||
399 | * @return array |
||
400 | */ |
||
401 | public function getInternalPlugins() |
||
412 | |||
413 | /** |
||
414 | * Get all button rows, skipping empty rows |
||
415 | * |
||
416 | * @return array |
||
417 | */ |
||
418 | public function getButtons() |
||
422 | |||
423 | /** |
||
424 | * Totally re-set the buttons on a given line |
||
425 | * |
||
426 | * @param int $line The line number to redefine, from 1 to 3 |
||
427 | * @param string $buttons,... A string or several strings, or a single array of strings. |
||
428 | * The button names to assign to this line. |
||
429 | * @return $this |
||
430 | */ |
||
431 | public function setButtonsForLine($line, $buttons) |
||
440 | |||
441 | /** |
||
442 | * Add buttons to the end of a line |
||
443 | * @param int $line The line number to redefine, from 1 to 3 |
||
444 | * @param string $buttons,... A string or several strings, or a single array of strings. |
||
445 | * The button names to add to this line |
||
446 | * @return $this |
||
447 | */ |
||
448 | public function addButtonsToLine($line, $buttons) |
||
462 | |||
463 | /** |
||
464 | * Internal function for adding and removing buttons related to another button |
||
465 | * @param string $name The name of the button to modify |
||
466 | * @param int $offset The offset relative to that button to perform an array_splice at. |
||
467 | * 0 for before $name, 1 for after. |
||
468 | * @param int $del The number of buttons to remove at the position given by index(string) + offset |
||
469 | * @param mixed $add An array or single item to insert at the position given by index(string) + offset, |
||
470 | * or null for no insertion |
||
471 | * @return bool True if $name matched a button, false otherwise |
||
472 | */ |
||
473 | protected function modifyButtons($name, $offset, $del = 0, $add = null) |
||
487 | |||
488 | /** |
||
489 | * Insert buttons before the first occurance of another button |
||
490 | * @param string $before the name of the button to insert other buttons before |
||
491 | * @param string $buttons,... a string, or several strings, or a single array of strings. |
||
492 | * The button names to insert before that button |
||
493 | * @return bool True if insertion occured, false if it did not (because the given button name was not found) |
||
494 | */ |
||
495 | View Code Duplication | public function insertButtonsBefore($before, $buttons) |
|
506 | |||
507 | /** |
||
508 | * Insert buttons after the first occurance of another button |
||
509 | * @param string $after the name of the button to insert other buttons before |
||
510 | * @param string $buttons,... a string, or several strings, or a single array of strings. |
||
511 | * The button names to insert after that button |
||
512 | * @return bool True if insertion occured, false if it did not (because the given button name was not found) |
||
513 | */ |
||
514 | View Code Duplication | public function insertButtonsAfter($after, $buttons) |
|
525 | |||
526 | /** |
||
527 | * Remove the first occurance of buttons |
||
528 | * @param string $buttons,... one or more strings - the name of the buttons to remove |
||
529 | */ |
||
530 | View Code Duplication | public function removeButtons($buttons) |
|
542 | |||
543 | /** |
||
544 | * Generate the JavaScript that will set TinyMCE's configuration: |
||
545 | * - Parse all configurations into JSON objects to be used in JavaScript |
||
546 | * - Includes TinyMCE and configurations using the {@link Requirements} system |
||
547 | * |
||
548 | * @return array |
||
549 | */ |
||
550 | protected function getConfig() |
||
610 | |||
611 | /** |
||
612 | * Get location of all editor.css files |
||
613 | * |
||
614 | * @return array |
||
615 | */ |
||
616 | protected function getEditorCSS() |
||
631 | |||
632 | /** |
||
633 | * Generate gzipped TinyMCE configuration including plugins and languages. |
||
634 | * This ends up "pre-loading" TinyMCE bundled with the required plugins |
||
635 | * so that multiple HTTP requests on the client don't need to be made. |
||
636 | * |
||
637 | * @return string |
||
638 | */ |
||
639 | public function getScriptURL() |
||
665 | |||
666 | public function init() |
||
671 | |||
672 | |||
673 | /** |
||
674 | * Get the current tinyMCE language |
||
675 | * |
||
676 | * @return string Language |
||
677 | */ |
||
678 | public static function get_tinymce_lang() |
||
687 | |||
688 | /** |
||
689 | * @return string|false |
||
690 | */ |
||
691 | public function getAdminPath() |
||
699 | |||
700 | /** |
||
701 | * @return string|false |
||
702 | */ |
||
703 | public function getTinyMCEPath() |
||
720 | |||
721 | /** |
||
722 | * @return \SilverStripe\Core\Manifest\Module |
||
723 | */ |
||
724 | protected function getAdminModule() |
||
728 | } |
||
729 |
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.
Consider the following example. The parameter
$ireland
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was changed, but the annotation was not.