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 Str 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 Str, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
48 | class Str implements ArrayAccess |
||
49 | { |
||
50 | /** |
||
51 | * @var string |
||
52 | */ |
||
53 | protected $string; |
||
54 | |||
55 | /** |
||
56 | * @param string $string |
||
57 | */ |
||
58 | public function __construct($string = '') |
||
72 | |||
73 | /** |
||
74 | * @return string |
||
75 | */ |
||
76 | public function __toString() |
||
80 | |||
81 | /** |
||
82 | * Get the string between the given start and end. |
||
83 | * |
||
84 | * @param $start |
||
85 | * @param $end |
||
86 | * |
||
87 | * @return \Spatie\String\Str |
||
88 | */ |
||
89 | public function between($start, $end) |
||
117 | |||
118 | /** |
||
119 | * Convert the string to uppercase. |
||
120 | * |
||
121 | * @return \Spatie\String\Str |
||
122 | */ |
||
123 | public function toUpper() |
||
127 | |||
128 | /** |
||
129 | * Convert the string to lowercase. |
||
130 | * |
||
131 | * @return \Spatie\String\Str |
||
132 | */ |
||
133 | public function toLower() |
||
137 | |||
138 | /** |
||
139 | * Shortens a string in a pretty way. It will clean it by trimming |
||
140 | * it, remove all double spaces and html. If the string is then still |
||
141 | * longer than the specified $length it will be shortened. The end |
||
142 | * of the string is always a full word concatinated with the |
||
143 | * specified moreTextIndicator. |
||
144 | * |
||
145 | * @param int $length |
||
146 | * @param string $moreTextIndicator |
||
147 | * |
||
148 | * @return \Spatie\String\Str |
||
149 | */ |
||
150 | public function tease($length = 200, $moreTextIndicator = '...') |
||
167 | |||
168 | /** |
||
169 | * Sanitize the string for teasing. |
||
170 | * |
||
171 | * @param $string |
||
172 | * |
||
173 | * @return string |
||
174 | */ |
||
175 | private function sanitizeForTease($string) |
||
187 | |||
188 | /** |
||
189 | * Replace the first occurrence of a string. |
||
190 | * |
||
191 | * @param $search |
||
192 | * @param $replace |
||
193 | * |
||
194 | * @return \Spatie\String\Str |
||
195 | */ |
||
196 | View Code Duplication | public function replaceFirst($search, $replace) |
|
212 | |||
213 | /** |
||
214 | * Replace the last occurrence of a string. |
||
215 | * |
||
216 | * @param $search |
||
217 | * @param $replace |
||
218 | * |
||
219 | * @return \Spatie\String\Str |
||
220 | */ |
||
221 | View Code Duplication | public function replaceLast($search, $replace) |
|
237 | |||
238 | /** |
||
239 | * Prefix a string. |
||
240 | * |
||
241 | * @param $string |
||
242 | * |
||
243 | * @return \Spatie\String\Str |
||
244 | */ |
||
245 | public function prefix($string) |
||
249 | |||
250 | /** |
||
251 | * Suffix a string. |
||
252 | * |
||
253 | * @param $string |
||
254 | * |
||
255 | * @return \Spatie\String\Str |
||
256 | */ |
||
257 | public function suffix($string) |
||
261 | |||
262 | /** |
||
263 | * Concatenate a string. |
||
264 | * |
||
265 | * @param $string |
||
266 | * |
||
267 | * @return \Spatie\String\Str |
||
268 | */ |
||
269 | public function concat($string) |
||
273 | |||
274 | /** |
||
275 | * Get the possessive version of a string. |
||
276 | * |
||
277 | * @return \Spatie\String\Str |
||
278 | */ |
||
279 | public function possessive() |
||
293 | |||
294 | /** |
||
295 | * Get a segment from a string based on a delimiter. |
||
296 | * Returns an empty string when the offset doesn't exist. |
||
297 | * Use a negative index to start counting from the last element. |
||
298 | * |
||
299 | * @param string $delimiter |
||
300 | * @param int $index |
||
301 | * |
||
302 | * @return \Spatie\String\Str |
||
303 | */ |
||
304 | public function segment($delimiter, $index) |
||
317 | |||
318 | /** |
||
319 | * Get the first segment from a string based on a delimiter. |
||
320 | * |
||
321 | * @param string $delimiter |
||
322 | * |
||
323 | * @return \Spatie\String\Str |
||
324 | */ |
||
325 | public function firstSegment($delimiter) |
||
329 | |||
330 | /** |
||
331 | * Get the last segment from a string based on a delimiter. |
||
332 | * |
||
333 | * @param string $delimiter |
||
334 | * |
||
335 | * @return \Spatie\String\Str |
||
336 | */ |
||
337 | public function lastSegment($delimiter) |
||
341 | |||
342 | /** |
||
343 | * Pop (remove) the last segment of a string based on a delimiter. |
||
344 | * |
||
345 | * @param string $delimiter |
||
346 | * |
||
347 | * @return \Spatie\String\Str |
||
348 | */ |
||
349 | public function pop($delimiter) |
||
353 | |||
354 | /** |
||
355 | * Strip whitespace (or other characters) from the beginning and end of a string. |
||
356 | * |
||
357 | * @param string $characterMask |
||
358 | * |
||
359 | * @return \Spatie\String\Str |
||
360 | */ |
||
361 | public function trim($characterMask = " \t\n\r\0\x0B") |
||
365 | |||
366 | /** |
||
367 | * Alias for find. |
||
368 | * |
||
369 | * @param array|string $needle |
||
370 | * @param bool $caseSensitive |
||
371 | * @param bool $absolute |
||
372 | * |
||
373 | * @return bool |
||
374 | */ |
||
375 | public function contains($needle, $caseSensitive = false, $absolute = false) |
||
379 | |||
380 | /** |
||
381 | * Unknown methods calls will be handled by various integrations. |
||
382 | * |
||
383 | * @param $method |
||
384 | * @param $args |
||
385 | * |
||
386 | * @throws UnknownFunctionException |
||
387 | * |
||
388 | * @return mixed|\Spatie\String\Str |
||
389 | */ |
||
390 | public function __call($method, $args) |
||
400 | |||
401 | /** |
||
402 | * Whether a offset exists. |
||
403 | * |
||
404 | * @link http://php.net/manual/en/arrayaccess.offsetexists.php |
||
405 | * |
||
406 | * @param mixed $offset An offset to check for. |
||
407 | * |
||
408 | * @return bool true on success or false on failure. |
||
409 | * The return value will be casted to boolean if non-boolean was returned. |
||
410 | */ |
||
411 | public function offsetExists($offset) |
||
415 | |||
416 | /** |
||
417 | * Offset to retrieve. |
||
418 | * |
||
419 | * @link http://php.net/manual/en/arrayaccess.offsetget.php |
||
420 | * |
||
421 | * @param mixed $offset The offset to retrieve. |
||
422 | * |
||
423 | * @return mixed Can return all value types. |
||
424 | */ |
||
425 | public function offsetGet($offset) |
||
431 | |||
432 | /** |
||
433 | * Offset to set. |
||
434 | * |
||
435 | * @link http://php.net/manual/en/arrayaccess.offsetset.php |
||
436 | * |
||
437 | * @param mixed $offset The offset to assign the value to. |
||
438 | * @param mixed $value The value to set. |
||
439 | */ |
||
440 | public function offsetSet($offset, $value) |
||
444 | |||
445 | /** |
||
446 | * Offset to unset. |
||
447 | * |
||
448 | * @link http://php.net/manual/en/arrayaccess.offsetunset.php |
||
449 | * |
||
450 | * @param mixed $offset The offset to unset. |
||
451 | * |
||
452 | * @throws UnsetOffsetException |
||
453 | */ |
||
454 | public function offsetUnset($offset) |
||
458 | } |
||
459 |
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.