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 JsonEncoder 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 JsonEncoder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class JsonEncoder |
||
22 | { |
||
23 | /** |
||
24 | * Encode a value as JSON array. |
||
25 | */ |
||
26 | const JSON_ARRAY = 1; |
||
27 | |||
28 | /** |
||
29 | * Encode a value as JSON object. |
||
30 | */ |
||
31 | const JSON_OBJECT = 2; |
||
32 | |||
33 | /** |
||
34 | * Encode a value as JSON string. |
||
35 | */ |
||
36 | const JSON_STRING = 3; |
||
37 | |||
38 | /** |
||
39 | * Encode a value as JSON integer or float. |
||
40 | */ |
||
41 | const JSON_NUMBER = 4; |
||
42 | |||
43 | /** |
||
44 | * @var JsonValidator |
||
45 | */ |
||
46 | private $validator; |
||
47 | |||
48 | /** |
||
49 | * @var int |
||
50 | */ |
||
51 | private $arrayEncoding = self::JSON_ARRAY; |
||
52 | |||
53 | /** |
||
54 | * @var int |
||
55 | */ |
||
56 | private $numericEncoding = self::JSON_STRING; |
||
57 | |||
58 | /** |
||
59 | * @var bool |
||
60 | */ |
||
61 | private $gtLtEscaped = false; |
||
62 | |||
63 | /** |
||
64 | * @var bool |
||
65 | */ |
||
66 | private $ampersandEscaped = false; |
||
67 | |||
68 | /** |
||
69 | * @var bool |
||
70 | */ |
||
71 | private $singleQuoteEscaped = false; |
||
72 | |||
73 | /** |
||
74 | * @var bool |
||
75 | */ |
||
76 | private $doubleQuoteEscaped = false; |
||
77 | |||
78 | /** |
||
79 | * @var bool |
||
80 | */ |
||
81 | private $slashEscaped = true; |
||
82 | |||
83 | /** |
||
84 | * @var bool |
||
85 | */ |
||
86 | private $unicodeEscaped = true; |
||
87 | |||
88 | /** |
||
89 | * @var bool |
||
90 | */ |
||
91 | private $prettyPrinting = false; |
||
92 | |||
93 | /** |
||
94 | * @var bool |
||
95 | */ |
||
96 | private $terminatedWithLineFeed = false; |
||
97 | |||
98 | /** |
||
99 | * @var int |
||
100 | */ |
||
101 | private $maxDepth = 512; |
||
102 | |||
103 | /** |
||
104 | * Creates a new encoder. |
||
105 | * |
||
106 | * @param null|JsonValidator $validator |
||
107 | */ |
||
108 | 55 | public function __construct(JsonValidator $validator = null) |
|
112 | |||
113 | /** |
||
114 | * Encodes data as JSON. |
||
115 | * |
||
116 | * If a schema is passed, the value is validated against that schema before |
||
117 | * encoding. The schema may be passed as file path or as object returned |
||
118 | * from `JsonDecoder::decodeFile($schemaFile)`. |
||
119 | * |
||
120 | * You can adjust the decoding with the various setters in this class. |
||
121 | * |
||
122 | * @param mixed $data The data to encode. |
||
123 | * @param string|object $schema The schema file or object. |
||
|
|||
124 | * |
||
125 | * @return string The JSON string. |
||
126 | * |
||
127 | * @throws EncodingFailedException If the data could not be encoded. |
||
128 | * @throws ValidationFailedException If the data fails schema validation. |
||
129 | * @throws InvalidSchemaException If the schema is invalid. |
||
130 | */ |
||
131 | 47 | public function encode($data, $schema = null) |
|
217 | |||
218 | /** |
||
219 | * Encodes data into a JSON file. |
||
220 | * |
||
221 | * @param mixed $data The data to encode. |
||
222 | * @param string $path The path where the JSON file will be stored. |
||
223 | * @param string|object $schema The schema file or object. |
||
224 | * |
||
225 | * @throws EncodingFailedException If the data could not be encoded. |
||
226 | * @throws ValidationFailedException If the data fails schema validation. |
||
227 | * @throws InvalidSchemaException If the schema is invalid. |
||
228 | * |
||
229 | * @see encode |
||
230 | */ |
||
231 | 5 | public function encodeFile($data, $path, $schema = null) |
|
291 | |||
292 | /** |
||
293 | * Returns the encoding of non-associative arrays. |
||
294 | * |
||
295 | * @return int One of the constants {@link JSON_OBJECT} and {@link JSON_ARRAY}. |
||
296 | */ |
||
297 | public function getArrayEncoding() |
||
301 | |||
302 | /** |
||
303 | * Sets the encoding of non-associative arrays. |
||
304 | * |
||
305 | * By default, non-associative arrays are decoded as JSON arrays. |
||
306 | * |
||
307 | * @param int $encoding One of the constants {@link JSON_OBJECT} and {@link JSON_ARRAY}. |
||
308 | * |
||
309 | * @throws \InvalidArgumentException If the passed encoding is invalid. |
||
310 | */ |
||
311 | 5 | View Code Duplication | public function setArrayEncoding($encoding) |
323 | |||
324 | /** |
||
325 | * Returns the encoding of numeric strings. |
||
326 | * |
||
327 | * @return int One of the constants {@link JSON_STRING} and {@link JSON_NUMBER}. |
||
328 | */ |
||
329 | public function getNumericEncoding() |
||
333 | |||
334 | /** |
||
335 | * Sets the encoding of numeric strings. |
||
336 | * |
||
337 | * By default, non-associative arrays are decoded as JSON strings. |
||
338 | * |
||
339 | * @param int $encoding One of the constants {@link JSON_STRING} and {@link JSON_NUMBER}. |
||
340 | * |
||
341 | * @throws \InvalidArgumentException If the passed encoding is invalid. |
||
342 | */ |
||
343 | 6 | View Code Duplication | public function setNumericEncoding($encoding) |
355 | |||
356 | /** |
||
357 | * Returns whether ampersands (&) are escaped. |
||
358 | * |
||
359 | * If `true`, ampersands will be escaped as "\u0026". |
||
360 | * |
||
361 | * By default, ampersands are not escaped. |
||
362 | * |
||
363 | * @return bool Whether ampersands are escaped. |
||
364 | */ |
||
365 | public function isAmpersandEscaped() |
||
369 | |||
370 | /** |
||
371 | * Sets whether ampersands (&) should be escaped. |
||
372 | * |
||
373 | * If `true`, ampersands will be escaped as "\u0026". |
||
374 | * |
||
375 | * By default, ampersands are not escaped. |
||
376 | * |
||
377 | * @param bool $enabled Whether ampersands should be escaped. |
||
378 | */ |
||
379 | 2 | public function setEscapeAmpersand($enabled) |
|
383 | |||
384 | /** |
||
385 | * Returns whether double quotes (") are escaped. |
||
386 | * |
||
387 | * If `true`, double quotes will be escaped as "\u0022". |
||
388 | * |
||
389 | * By default, double quotes are not escaped. |
||
390 | * |
||
391 | * @return bool Whether double quotes are escaped. |
||
392 | */ |
||
393 | public function isDoubleQuoteEscaped() |
||
397 | |||
398 | /** |
||
399 | * Sets whether double quotes (") should be escaped. |
||
400 | * |
||
401 | * If `true`, double quotes will be escaped as "\u0022". |
||
402 | * |
||
403 | * By default, double quotes are not escaped. |
||
404 | * |
||
405 | * @param bool $enabled Whether double quotes should be escaped. |
||
406 | */ |
||
407 | 2 | public function setEscapeDoubleQuote($enabled) |
|
411 | |||
412 | /** |
||
413 | * Returns whether single quotes (') are escaped. |
||
414 | * |
||
415 | * If `true`, single quotes will be escaped as "\u0027". |
||
416 | * |
||
417 | * By default, single quotes are not escaped. |
||
418 | * |
||
419 | * @return bool Whether single quotes are escaped. |
||
420 | */ |
||
421 | public function isSingleQuoteEscaped() |
||
425 | |||
426 | /** |
||
427 | * Sets whether single quotes (") should be escaped. |
||
428 | * |
||
429 | * If `true`, single quotes will be escaped as "\u0027". |
||
430 | * |
||
431 | * By default, single quotes are not escaped. |
||
432 | * |
||
433 | * @param bool $enabled Whether single quotes should be escaped. |
||
434 | */ |
||
435 | 2 | public function setEscapeSingleQuote($enabled) |
|
439 | |||
440 | /** |
||
441 | * Returns whether forward slashes (/) are escaped. |
||
442 | * |
||
443 | * If `true`, forward slashes will be escaped as "\/". |
||
444 | * |
||
445 | * By default, forward slashes are not escaped. |
||
446 | * |
||
447 | * @return bool Whether forward slashes are escaped. |
||
448 | */ |
||
449 | public function isSlashEscaped() |
||
453 | |||
454 | /** |
||
455 | * Sets whether forward slashes (") should be escaped. |
||
456 | * |
||
457 | * If `true`, forward slashes will be escaped as "\/". |
||
458 | * |
||
459 | * By default, forward slashes are not escaped. |
||
460 | * |
||
461 | * @param bool $enabled Whether forward slashes should be escaped. |
||
462 | */ |
||
463 | 2 | public function setEscapeSlash($enabled) |
|
467 | |||
468 | /** |
||
469 | * Returns whether greater than/less than symbols (>, <) are escaped. |
||
470 | * |
||
471 | * If `true`, greater than will be escaped as "\u003E" and less than as |
||
472 | * "\u003C". |
||
473 | * |
||
474 | * By default, greater than/less than symbols are not escaped. |
||
475 | * |
||
476 | * @return bool Whether greater than/less than symbols are escaped. |
||
477 | */ |
||
478 | public function isGtLtEscaped() |
||
482 | |||
483 | /** |
||
484 | * Sets whether greater than/less than symbols (>, <) should be escaped. |
||
485 | * |
||
486 | * If `true`, greater than will be escaped as "\u003E" and less than as |
||
487 | * "\u003C". |
||
488 | * |
||
489 | * By default, greater than/less than symbols are not escaped. |
||
490 | * |
||
491 | * @param bool $enabled Whether greater than/less than should be escaped. |
||
492 | */ |
||
493 | 2 | public function setEscapeGtLt($enabled) |
|
497 | |||
498 | /** |
||
499 | * Returns whether unicode characters are escaped. |
||
500 | * |
||
501 | * If `true`, unicode characters will be escaped as hexadecimals strings. |
||
502 | * For example, "ü" will be escaped as "\u00fc". |
||
503 | * |
||
504 | * By default, unicode characters are escaped. |
||
505 | * |
||
506 | * @return bool Whether unicode characters are escaped. |
||
507 | */ |
||
508 | public function isUnicodeEscaped() |
||
512 | |||
513 | /** |
||
514 | * Sets whether unicode characters should be escaped. |
||
515 | * |
||
516 | * If `true`, unicode characters will be escaped as hexadecimals strings. |
||
517 | * For example, "ü" will be escaped as "\u00fc". |
||
518 | * |
||
519 | * By default, unicode characters are escaped. |
||
520 | * |
||
521 | * @param bool $enabled Whether unicode characters should be escaped. |
||
522 | */ |
||
523 | 2 | public function setEscapeUnicode($enabled) |
|
527 | |||
528 | /** |
||
529 | * Returns whether JSON strings are formatted for better readability. |
||
530 | * |
||
531 | * If `true`, line breaks will be added after object properties and array |
||
532 | * entries. Each new nesting level will be indented by four spaces. |
||
533 | * |
||
534 | * By default, pretty printing is not enabled. |
||
535 | * |
||
536 | * @return bool Whether JSON strings are formatted. |
||
537 | */ |
||
538 | public function isPrettyPrinting() |
||
542 | |||
543 | /** |
||
544 | * Sets whether JSON strings should be formatted for better readability. |
||
545 | * |
||
546 | * If `true`, line breaks will be added after object properties and array |
||
547 | * entries. Each new nesting level will be indented by four spaces. |
||
548 | * |
||
549 | * By default, pretty printing is not enabled. |
||
550 | * |
||
551 | * @param bool $prettyPrinting Whether JSON strings should be formatted. |
||
552 | */ |
||
553 | 2 | public function setPrettyPrinting($prettyPrinting) |
|
557 | |||
558 | /** |
||
559 | * Returns whether JSON strings are terminated with a line feed. |
||
560 | * |
||
561 | * By default, JSON strings are not terminated with a line feed. |
||
562 | * |
||
563 | * @return bool Whether JSON strings are terminated with a line feed. |
||
564 | */ |
||
565 | public function isTerminatedWithLineFeed() |
||
569 | |||
570 | /** |
||
571 | * Sets whether JSON strings should be terminated with a line feed. |
||
572 | * |
||
573 | * By default, JSON strings are not terminated with a line feed. |
||
574 | * |
||
575 | * @param bool $enabled Whether JSON strings should be terminated with a |
||
576 | * line feed. |
||
577 | */ |
||
578 | 2 | public function setTerminateWithLineFeed($enabled) |
|
582 | |||
583 | /** |
||
584 | * Returns the maximum recursion depth. |
||
585 | * |
||
586 | * A depth of zero means that objects are not allowed. A depth of one means |
||
587 | * only one level of objects or arrays is allowed. |
||
588 | * |
||
589 | * @return int The maximum recursion depth. |
||
590 | */ |
||
591 | public function getMaxDepth() |
||
595 | |||
596 | /** |
||
597 | * Sets the maximum recursion depth. |
||
598 | * |
||
599 | * If the depth is exceeded during encoding, an {@link EncodingFailedException} |
||
600 | * will be thrown. |
||
601 | * |
||
602 | * A depth of zero means that objects are not allowed. A depth of one means |
||
603 | * only one level of objects or arrays is allowed. |
||
604 | * |
||
605 | * @param int $maxDepth The maximum recursion depth. |
||
606 | * |
||
607 | * @throws \InvalidArgumentException If the depth is not an integer greater |
||
608 | * than or equal to zero. |
||
609 | */ |
||
610 | 4 | View Code Duplication | public function setMaxDepth($maxDepth) |
628 | } |
||
629 |
This check looks for
@param
annotations where the type inferred by our type inference engine differs from the declared type.It makes a suggestion as to what type it considers more descriptive.
Most often this is a case of a parameter that can be null in addition to its declared types.