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 CultureInfo 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 CultureInfo, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | class CultureInfo |
||
52 | { |
||
53 | /** |
||
54 | * The ICU data array, shared by all instances of this class. |
||
55 | * @var array |
||
56 | */ |
||
57 | protected static $data = []; |
||
58 | |||
59 | /** |
||
60 | * The current culture. |
||
61 | * @var string |
||
62 | */ |
||
63 | protected $culture; |
||
64 | |||
65 | /** |
||
66 | * A list of resource bundles keys |
||
67 | * @var array |
||
68 | */ |
||
69 | protected static $bundleNames = [ |
||
70 | 'Core' => null, |
||
71 | 'Currencies' => 'ICUDATA-curr', |
||
72 | 'Languages' => 'ICUDATA-lang', |
||
73 | 'Countries' => 'ICUDATA-region', |
||
74 | 'zoneStrings' => 'ICUDATA-zone', |
||
75 | ]; |
||
76 | |||
77 | /** |
||
78 | * A list of properties that are accessable/writable. |
||
79 | * @var array |
||
80 | */ |
||
81 | protected $properties = []; |
||
82 | |||
83 | /** |
||
84 | * Culture type, all. |
||
85 | * @see getCultures() |
||
86 | * @var int |
||
87 | */ |
||
88 | const ALL = 0; |
||
89 | |||
90 | /** |
||
91 | * Culture type, neutral. |
||
92 | * @see getCultures() |
||
93 | * @var int |
||
94 | */ |
||
95 | const NEUTRAL = 1; |
||
96 | |||
97 | /** |
||
98 | * Culture type, specific. |
||
99 | * @see getCultures() |
||
100 | * @var int |
||
101 | */ |
||
102 | const SPECIFIC = 2; |
||
103 | |||
104 | /** |
||
105 | * Display the culture name. |
||
106 | * @return string the culture name. |
||
107 | * @see getName() |
||
108 | */ |
||
109 | public function __toString() |
||
113 | |||
114 | /** |
||
115 | * Allow functions that begins with 'set' to be called directly |
||
116 | * as an attribute/property to retrieve the value. |
||
117 | * @param mixed $name |
||
118 | * @return mixed |
||
119 | */ |
||
120 | View Code Duplication | public function __get($name) |
|
129 | |||
130 | /** |
||
131 | * Allow functions that begins with 'set' to be called directly |
||
132 | * as an attribute/property to set the value. |
||
133 | * @param mixed $name |
||
134 | * @param mixed $value |
||
135 | */ |
||
136 | View Code Duplication | public function __set($name, $value) |
|
145 | |||
146 | |||
147 | /** |
||
148 | * Initializes a new instance of the CultureInfo class based on the |
||
149 | * culture specified by name. E.g. <code>new CultureInfo('en_AU');</cdoe> |
||
150 | * The culture indentifier must be of the form |
||
151 | * "language_(country/region/variant)". |
||
152 | * @param string $culture a culture name, e.g. "en_AU". |
||
153 | * @return return new CultureInfo. |
||
154 | */ |
||
155 | public function __construct($culture = 'en') |
||
165 | |||
166 | /** |
||
167 | * Gets the CultureInfo that for this culture string |
||
168 | * @param mixed $culture |
||
169 | * @return CultureInfo invariant culture info is "en". |
||
170 | */ |
||
171 | public static function getInstance($culture) |
||
178 | |||
179 | /** |
||
180 | * Determine if a given culture is valid. Simply checks that the |
||
181 | * culture data exists. |
||
182 | * @param string $culture a culture |
||
183 | * @return bool true if valid, false otherwise. |
||
184 | */ |
||
185 | public static function validCulture($culture) |
||
189 | |||
190 | /** |
||
191 | * Set the culture for the current instance. The culture indentifier |
||
192 | * must be of the form "<language>_(country/region)". |
||
193 | * @param string $culture culture identifier, e.g. "fr_FR_EURO". |
||
194 | */ |
||
195 | protected function setCulture($culture) |
||
205 | |||
206 | /** |
||
207 | * Load the ICU culture data for the specific culture identifier. |
||
208 | * @param string $culture the culture identifier. |
||
209 | */ |
||
210 | protected function loadCultureData($key) |
||
224 | |||
225 | /** |
||
226 | * Find the specific ICU data information from the data. |
||
227 | * The path to the specific ICU data is separated with a slash "/". |
||
228 | * E.g. To find the default calendar used by the culture, the path |
||
229 | * "calendar/default" will return the corresponding default calendar. |
||
230 | * @param string $path the data you want to find. |
||
231 | * @param string $key bundle name. |
||
232 | * @return mixed the specific ICU data. |
||
233 | */ |
||
234 | public function findInfo($path='/', $key = null) |
||
257 | |||
258 | /** |
||
259 | * Search the array for a specific value using a path separated using |
||
260 | * slash "/" separated path. e.g to find $info['hello']['world'], |
||
261 | * the path "hello/world" will return the corresponding value. |
||
262 | * @param array $info the array for search |
||
263 | * @param string $path slash "/" separated array path. |
||
264 | * @return mixed the value array using the path |
||
265 | */ |
||
266 | private function searchResources($resource, $path='/') |
||
277 | |||
278 | /** |
||
279 | * Gets the culture name in the format |
||
280 | * "<languagecode2>_(country/regioncode2)". |
||
281 | * @return string culture name. |
||
282 | */ |
||
283 | public function getName() |
||
287 | |||
288 | /** |
||
289 | * Gets the default calendar used by the culture, e.g. "gregorian". |
||
290 | * @return string the default calendar. |
||
291 | */ |
||
292 | public function getCalendar() |
||
296 | |||
297 | /** |
||
298 | * Gets the culture name in the language that the culture is set |
||
299 | * to display. Returns <code>array('Language','Country');</code> |
||
300 | * 'Country' is omitted if the culture is neutral. |
||
301 | * @return array array with language and country as elements, localized. |
||
302 | */ |
||
303 | public function getNativeName() |
||
314 | |||
315 | /** |
||
316 | * Gets the culture name in English. |
||
317 | * Returns <code>array('Language','Country');</code> |
||
318 | * 'Country' is omitted if the culture is neutral. |
||
319 | * @return string language (country), it may locale code string if english name does not exist. |
||
320 | */ |
||
321 | public function getEnglishName() |
||
338 | |||
339 | /** |
||
340 | * Gets the CultureInfo that is culture-independent (invariant). |
||
341 | * Any changes to the invariant culture affects all other |
||
342 | * instances of the invariant culture. |
||
343 | * The invariant culture is assumed to be "en"; |
||
344 | * @return CultureInfo invariant culture info is "en". |
||
345 | */ |
||
346 | public static function getInvariantCulture() |
||
354 | |||
355 | /** |
||
356 | * Gets a value indicating whether the current CultureInfo |
||
357 | * represents a neutral culture. Returns true if the culture |
||
358 | * only contains two characters. |
||
359 | * @return bool true if culture is neutral, false otherwise. |
||
360 | */ |
||
361 | public function getIsNeutralCulture() |
||
365 | |||
366 | /** |
||
367 | * Gets the list of supported cultures filtered by the specified |
||
368 | * culture type. This is an EXPENSIVE function, it needs to traverse |
||
369 | * a list of ICU files in the data directory. |
||
370 | * This function can be called statically. |
||
371 | * @param int $type culture type, CultureInfo::ALL, CultureInfo::NEUTRAL |
||
372 | * or CultureInfo::SPECIFIC. |
||
373 | * @return array list of culture information available. |
||
374 | */ |
||
375 | public static function getCultures($type=CultureInfo::ALL) |
||
399 | |||
400 | /** |
||
401 | * Simplify a single element array into its own value. |
||
402 | * E.g. <code>array(0 => array('hello'), 1 => 'world');</code> |
||
403 | * becomes <code>array(0 => 'hello', 1 => 'world');</code> |
||
404 | * @param array $array with single elements arrays |
||
405 | * @return array simplified array. |
||
406 | */ |
||
407 | protected function simplify($obj) |
||
431 | |||
432 | /** |
||
433 | * Get a list of countries in the language of the localized version. |
||
434 | * @return array a list of localized country names. |
||
435 | */ |
||
436 | public function getCountries() |
||
440 | |||
441 | /** |
||
442 | * Get a list of currencies in the language of the localized version. |
||
443 | * @return array a list of localized currencies. |
||
444 | */ |
||
445 | public function getCurrencies() |
||
456 | |||
457 | /** |
||
458 | * Get a list of languages in the language of the localized version. |
||
459 | * @return array list of localized language names. |
||
460 | */ |
||
461 | public function getLanguages() |
||
465 | |||
466 | /** |
||
467 | * Get a list of scripts in the language of the localized version. |
||
468 | * @return array list of localized script names. |
||
469 | */ |
||
470 | public function getScripts() |
||
474 | |||
475 | /** |
||
476 | * Get a list of timezones in the language of the localized version. |
||
477 | * @return array list of localized timezones. |
||
478 | */ |
||
479 | public function getTimeZones() |
||
500 | } |
||
501 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.