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 StringPrimitive 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 StringPrimitive, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class StringPrimitive implements PrimitiveInterface, StringableInterface |
||
13 | { |
||
14 | const PAD_RIGHT = STR_PAD_RIGHT; |
||
15 | const PAD_LEFT = STR_PAD_LEFT; |
||
16 | const PAD_BOTH = STR_PAD_BOTH; |
||
17 | const PREG_NO_FLAGS = 0; |
||
18 | const PREG_SPLIT_NO_EMPTY = PREG_SPLIT_NO_EMPTY; |
||
19 | const PREG_SPLIT_DELIM_CAPTURE = PREG_SPLIT_DELIM_CAPTURE; |
||
20 | const PREG_SPLIT_OFFSET_CAPTURE = PREG_SPLIT_OFFSET_CAPTURE; |
||
21 | const PREG_OFFSET_CAPTURE = PREG_OFFSET_CAPTURE; |
||
22 | |||
23 | private $value; |
||
24 | |||
25 | public function __construct(string $value) |
||
34 | |||
35 | /** |
||
36 | * {@inheritdoc} |
||
37 | */ |
||
38 | public function toPrimitive() |
||
42 | |||
43 | /** |
||
44 | * {@inheritdoc} |
||
45 | */ |
||
46 | public function __toString(): string |
||
50 | |||
51 | /** |
||
52 | * Split the string into a collection of ones |
||
53 | * |
||
54 | * @param string $delimiter |
||
55 | * |
||
56 | * @return TypedCollectionInterface |
||
57 | */ |
||
58 | View Code Duplication | public function split(string $delimiter = null): TypedCollectionInterface |
|
73 | |||
74 | /** |
||
75 | * Returns a collection of the string splitted by the given chunk size |
||
76 | * |
||
77 | * @param int $size |
||
78 | * |
||
79 | * @return TypedCollectionInterface |
||
80 | */ |
||
81 | View Code Duplication | public function chunk(int $size = 1): TypedCollectionInterface |
|
91 | |||
92 | /** |
||
93 | * Returns the position of the first occurence of the string |
||
94 | * |
||
95 | * @param string $needle |
||
96 | * @param int $offset |
||
97 | * |
||
98 | * @throws SubstringException If the string is not found |
||
99 | * |
||
100 | * @return int |
||
101 | */ |
||
102 | View Code Duplication | public function pos(string $needle, int $offset = 0): int |
|
115 | |||
116 | /** |
||
117 | * Replace all occurences of the search string with the replacement one |
||
118 | * |
||
119 | * @param string $search |
||
120 | * @param string $replacement |
||
121 | * |
||
122 | * @return self |
||
123 | */ |
||
124 | public function replace(string $search, string $replacement): self |
||
132 | |||
133 | /** |
||
134 | * Returns the string following the given delimiter |
||
135 | * |
||
136 | * @param string $delimiter |
||
137 | * |
||
138 | * @throws SubstringException If the string is not found |
||
139 | * |
||
140 | * @return self |
||
141 | */ |
||
142 | View Code Duplication | public function str(string $delimiter): self |
|
155 | |||
156 | /** |
||
157 | * Return the string in upper case |
||
158 | * |
||
159 | * @return self |
||
160 | */ |
||
161 | public function toUpper(): self |
||
165 | |||
166 | /** |
||
167 | * Return the string in lower case |
||
168 | * |
||
169 | * @return self |
||
170 | */ |
||
171 | public function toLower(): self |
||
175 | |||
176 | /** |
||
177 | * Return the string length |
||
178 | * |
||
179 | * @return int |
||
180 | */ |
||
181 | public function length(): int |
||
185 | |||
186 | /** |
||
187 | * Reverse the string |
||
188 | * |
||
189 | * @return self |
||
190 | */ |
||
191 | public function reverse(): self |
||
195 | |||
196 | /** |
||
197 | * Pad the string |
||
198 | * |
||
199 | * @param int $length |
||
200 | * @param string $character |
||
201 | * @param int $direction |
||
202 | * |
||
203 | * @return self |
||
204 | */ |
||
205 | public function pad(int $length, string $character = ' ', int $direction = self::PAD_RIGHT): self |
||
214 | |||
215 | /** |
||
216 | * Pad to the right |
||
217 | * |
||
218 | * @param int $length |
||
219 | * @param string $character |
||
220 | * |
||
221 | * @return self |
||
222 | */ |
||
223 | public function rightPad(int $length, string $character = ' '): self |
||
227 | |||
228 | /** |
||
229 | * Pad to the left |
||
230 | * |
||
231 | * @param int $length |
||
232 | * @param string $character |
||
233 | * |
||
234 | * @return self |
||
235 | */ |
||
236 | public function leftPad(int $length, string $character = ' '): self |
||
240 | |||
241 | /** |
||
242 | * Pad both sides |
||
243 | * |
||
244 | * @param int $length |
||
245 | * @param string $character |
||
246 | * |
||
247 | * @return self |
||
248 | */ |
||
249 | public function uniPad(int $length, string $character = ' '): self |
||
253 | |||
254 | /** |
||
255 | * Find length of initial segment not matching mask |
||
256 | * |
||
257 | * @param string $mask |
||
258 | * @param int $start |
||
259 | * @param int $length |
||
260 | * |
||
261 | * @return int |
||
262 | */ |
||
263 | View Code Duplication | public function cspn(string $mask, int $start = 0, int $length = null): int |
|
278 | |||
279 | /** |
||
280 | * Repeat the string n times |
||
281 | * |
||
282 | * @param int $repeat |
||
283 | * |
||
284 | * @return self |
||
285 | */ |
||
286 | public function repeat(int $repeat): self |
||
290 | |||
291 | /** |
||
292 | * Shuffle the string |
||
293 | * |
||
294 | * @return self |
||
295 | */ |
||
296 | public function shuffle(): self |
||
300 | |||
301 | /** |
||
302 | * Strip slashes |
||
303 | * |
||
304 | * @return self |
||
305 | */ |
||
306 | public function stripSlashes(): self |
||
310 | |||
311 | /** |
||
312 | * Strip C-like slashes |
||
313 | * |
||
314 | * @return self |
||
315 | */ |
||
316 | public function stripCSlashes(): self |
||
320 | |||
321 | /** |
||
322 | * Return the word count |
||
323 | * |
||
324 | * @param string $charlist |
||
325 | * |
||
326 | * @return int |
||
327 | */ |
||
328 | public function wordCount(string $charlist = ''): int |
||
336 | |||
337 | /** |
||
338 | * Return the collection of words |
||
339 | * |
||
340 | * @param string $charlist |
||
341 | * |
||
342 | * @return TypedCollectionInterface |
||
343 | */ |
||
344 | public function words(string $charlist = ''): TypedCollectionInterface |
||
357 | |||
358 | /** |
||
359 | * Split the string using a regular expression |
||
360 | * |
||
361 | * @param string $regex |
||
362 | * @param int $limit |
||
363 | * @param int $flags |
||
364 | * |
||
365 | * @return TypedCollectionInterface |
||
366 | */ |
||
367 | View Code Duplication | public function pregSplit(string $regex, int $limit = -1, int $flags = self::PREG_NO_FLAGS): TypedCollectionInterface |
|
380 | |||
381 | /** |
||
382 | * Check if the string match the given regular expression |
||
383 | * |
||
384 | * @param string $regex |
||
385 | * @param int $offset |
||
386 | * |
||
387 | * @throws Exception If the regex failed |
||
388 | * |
||
389 | * @return bool |
||
390 | */ |
||
391 | View Code Duplication | public function match(string $regex, int $offset = 0): bool |
|
402 | |||
403 | /** |
||
404 | * Return a collection of the elements matching the regex |
||
405 | * |
||
406 | * @param string $regex |
||
407 | * @param int $offset |
||
408 | * @param int $flags |
||
409 | * |
||
410 | * @throws Exception If the regex failed |
||
411 | * |
||
412 | * @return TypedCollectionInterface |
||
413 | */ |
||
414 | public function getMatches(string $regex, int $offset = 0, int $flags = self::PREG_NO_FLAGS): TypedCollectionInterface |
||
435 | |||
436 | /** |
||
437 | * Replace part of the string by using a regular expression |
||
438 | * |
||
439 | * @param string $regex |
||
440 | * @param string $replacement |
||
441 | * @param int $limit |
||
442 | * |
||
443 | * @throws Exception If the regex failed |
||
444 | * |
||
445 | * @return self |
||
446 | */ |
||
447 | View Code Duplication | public function pregReplace(string $regex, string $replacement, int $limit = -1): self |
|
462 | |||
463 | /** |
||
464 | * Return part of the string |
||
465 | * |
||
466 | * @param int $start |
||
467 | * @param int $length |
||
468 | * |
||
469 | * @return self |
||
470 | */ |
||
471 | View Code Duplication | public function substring(int $start, int $length = null): self |
|
481 | |||
482 | /** |
||
483 | * Return a formatted string |
||
484 | * |
||
485 | * @return self |
||
486 | */ |
||
487 | public function sprintf(): self |
||
495 | |||
496 | /** |
||
497 | * Return the string with the first letter as uppercase |
||
498 | * |
||
499 | * @return self |
||
500 | */ |
||
501 | public function ucfirst(): self |
||
505 | |||
506 | /** |
||
507 | * Return the string with the first letter as lowercase |
||
508 | * |
||
509 | * @return self |
||
510 | */ |
||
511 | public function lcfirst(): self |
||
515 | |||
516 | /** |
||
517 | * Return a CamelCase representation of the string |
||
518 | * |
||
519 | * @return self |
||
520 | */ |
||
521 | public function camelize(): self |
||
532 | |||
533 | /** |
||
534 | * Append a string at the end of the current one |
||
535 | * |
||
536 | * @param string $string |
||
537 | * |
||
538 | * @return self |
||
539 | */ |
||
540 | public function append(string $string): self |
||
544 | |||
545 | /** |
||
546 | * Prepend a string at the beginning of the current one |
||
547 | * |
||
548 | * @param string $string |
||
549 | * |
||
550 | * @return self |
||
551 | */ |
||
552 | public function prepend(string $string): self |
||
556 | |||
557 | /** |
||
558 | * Check if the 2 strings are equal |
||
559 | * |
||
560 | * @param self $string |
||
561 | * |
||
562 | * @return bool |
||
563 | */ |
||
564 | public function equals(self $string): bool |
||
568 | |||
569 | /** |
||
570 | * Trim the string |
||
571 | * |
||
572 | * @param string $mask |
||
573 | * |
||
574 | * @return self |
||
575 | */ |
||
576 | public function trim(string $mask = null): self |
||
580 | |||
581 | /** |
||
582 | * Trim the right side of the string |
||
583 | * |
||
584 | * @param string $mask |
||
585 | * |
||
586 | * @return self |
||
587 | */ |
||
588 | public function rightTrim(string $mask = null): self |
||
592 | |||
593 | /** |
||
594 | * Trim the left side of the string |
||
595 | * |
||
596 | * @param string $mask |
||
597 | * |
||
598 | * @return self |
||
599 | */ |
||
600 | public function leftTrim(string $mask = null): self |
||
604 | } |
||
605 |
If you suppress an error, we recommend checking for the error condition explicitly: