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 Service 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 Service, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Comodojo\Dispatcher\Service; |
||
31 | abstract class Service { |
||
32 | |||
33 | //###### Things a service should define ######// |
||
34 | |||
35 | /** |
||
36 | * The content type that service will return. |
||
37 | * Default content is "text/plain"; each service can override, |
||
38 | * both in setup phase or in method declaration. |
||
39 | * |
||
40 | * @var string |
||
41 | */ |
||
42 | private $content_type = "text/plain"; |
||
43 | |||
44 | /** |
||
45 | * St status code that service will return (in case of no exceptions). |
||
46 | * Default content is 200 - OK; each service can override, |
||
47 | * both in setup phase or in method declaration. |
||
48 | * |
||
49 | * @var integer |
||
50 | */ |
||
51 | private $status_code = 200; |
||
52 | |||
53 | /** |
||
54 | * An array of headers that will be returned. This value is initially populated |
||
55 | * with headers declared in routing phase (if any). |
||
56 | * |
||
57 | * @var array |
||
58 | */ |
||
59 | private $headers = array(); |
||
60 | |||
61 | /** |
||
62 | * Supported HTTP methods (comma separated, not spaced) |
||
63 | * |
||
64 | * @var string |
||
65 | * @see dispatcher-config.php |
||
66 | */ |
||
67 | private $supported_http_methods = DISPATCHER_SUPPORTED_METHODS; |
||
68 | |||
69 | /** |
||
70 | * Result charset |
||
71 | * |
||
72 | * @var string |
||
73 | * @see dispatcher-config.php |
||
74 | */ |
||
75 | private $charset = DISPATCHER_DEFAULT_ENCODING; |
||
76 | |||
77 | //###### Things a service could use for free ######// |
||
78 | |||
79 | /** |
||
80 | * Request attributes, populated at runtime by dispatcher |
||
81 | * |
||
82 | * @var array |
||
83 | */ |
||
84 | private $attributes = array(); |
||
85 | |||
86 | /** |
||
87 | * Request parameters, populated at runtime by dispatcher |
||
88 | * |
||
89 | * @var array |
||
90 | */ |
||
91 | private $parameters = array(); |
||
92 | |||
93 | /** |
||
94 | * Request raw parameters (php://input), populated at runtime by dispatcher |
||
95 | * |
||
96 | * @var array |
||
97 | */ |
||
98 | private $raw_parameters = array(); |
||
99 | |||
100 | /** |
||
101 | * Request headers, injected by dispatcher |
||
102 | * |
||
103 | * @var array |
||
104 | */ |
||
105 | private $request_headers = array(); |
||
106 | |||
107 | /** |
||
108 | * Logger, injected by dispatcher |
||
109 | * |
||
110 | * @var Object |
||
111 | */ |
||
112 | private $logger = null; |
||
113 | |||
114 | /** |
||
115 | * Cacher, injected by dispatcher |
||
116 | * |
||
117 | * @var Object |
||
118 | */ |
||
119 | private $cacher = null; |
||
120 | |||
121 | /** |
||
122 | * An instance of dispatcher serializer |
||
123 | * |
||
124 | * @var Object |
||
125 | */ |
||
126 | public $serialize = null; |
||
127 | |||
128 | /** |
||
129 | * An instance of dispatcher deserializer |
||
130 | * |
||
131 | * @var Object |
||
132 | */ |
||
133 | public $deserialize = null; |
||
134 | |||
135 | //###### Things a service may define ######// |
||
136 | |||
137 | // Expected attributes |
||
138 | private $expected_attributes = null; |
||
139 | |||
140 | // Expected parameters |
||
141 | private $expected_parameters = null; |
||
142 | |||
143 | // Liked attributes |
||
144 | private $liked_attributes = null; |
||
145 | |||
146 | // Liked parameters |
||
147 | private $liked_parameters = null; |
||
148 | |||
149 | //###### Things dedicated to internal use ######// |
||
150 | |||
151 | /** |
||
152 | * Supported success code (as defined in ObjectSuccess) |
||
153 | * |
||
154 | * @var array |
||
155 | */ |
||
156 | private $supported_success_codes = array(200,202,204); |
||
157 | |||
158 | /*************** HTTP METHODS IMPLEMENTATIONS **************/ |
||
159 | |||
160 | /** |
||
161 | * Implement this method if your service should support |
||
162 | * HTTP-GET requests |
||
163 | */ |
||
164 | //public function get() {} |
||
|
|||
165 | |||
166 | /** |
||
167 | * Implement this method if your service should support |
||
168 | * HTTP-POST requests |
||
169 | */ |
||
170 | // public function post() {} |
||
171 | |||
172 | /** |
||
173 | * Implement this method if your service should support |
||
174 | * HTTP-PUT requests |
||
175 | */ |
||
176 | // public function put() {} |
||
177 | |||
178 | /** |
||
179 | * Implement this method if your service should support |
||
180 | * HTTP-DELETE requests |
||
181 | */ |
||
182 | // public function delete() {} |
||
183 | |||
184 | /** |
||
185 | * Implement this method if your service should support |
||
186 | * any HTTP requests (it's quite a wildcard, please be careful...) |
||
187 | */ |
||
188 | // public function any() {} |
||
189 | |||
190 | /*************** HTTP METHODS IMPLEMENTATIONS **************/ |
||
191 | |||
192 | /******************* OVERRIDABLE METHODS *******************/ |
||
193 | |||
194 | /** |
||
195 | * Service setup. |
||
196 | * |
||
197 | * It is the first method that dispatcher will call and could be used |
||
198 | * to define service parameters in global scope, such as contentType or |
||
199 | * success HTTP code. |
||
200 | * |
||
201 | * PLEASE REMEMBER: setting same parameters in method declaration will |
||
202 | * override first ones. |
||
203 | * |
||
204 | * @return null |
||
205 | */ |
||
206 | public function setup() { } |
||
207 | |||
208 | /** |
||
209 | * Service constructor. |
||
210 | * |
||
211 | * Currently, only for init serialized/deserialized but can be extended to |
||
212 | * do anything service needs. |
||
213 | * |
||
214 | * PLEASE REMEMBER to call parent::__construct() at the end of your method |
||
215 | * |
||
216 | * @return null |
||
217 | */ |
||
218 | public function __construct($logger, $cacher) { |
||
239 | |||
240 | /******************* OVERRIDABLE METHODS *******************/ |
||
241 | |||
242 | /** |
||
243 | * Expected attributes (i.e. ones that will build the URI) |
||
244 | * |
||
245 | * @param string $method HTTP method for punctual attributes rematch or ANY |
||
246 | * @param array $attributes array og attributes that service expects |
||
247 | * @param array $parameters array of parameters that service expects |
||
248 | * |
||
249 | * @return Object $this |
||
250 | */ |
||
251 | final public function expects($method, $attributes, $parameters=array()) { |
||
261 | |||
262 | /** |
||
263 | * Liked (optional) attributes |
||
264 | * |
||
265 | * @param string $method HTTP method for punctual attributes rematch or ANY |
||
266 | * @param array $attributes array og attributes that service likes |
||
267 | * @param array $parameters array of parameters that service likes |
||
268 | * |
||
269 | * @return Object $this |
||
270 | */ |
||
271 | final public function likes($method, $attributes, $parameters=array()) { |
||
281 | |||
282 | /** |
||
283 | * Set methods service will support. |
||
284 | * |
||
285 | * In can be misleading, but supported HTTP methods and implemented HTTP methods |
||
286 | * are not the same thing. |
||
287 | * |
||
288 | * - If method is not SUPPORTED, service will not be initiated and a 405 - Not Allowed |
||
289 | * error will be returned. |
||
290 | * - If method is not IMPLEMENTED - i.e. get() method is not defined - service will not |
||
291 | * be initiated and a 501 - Not Implemented error will be returned |
||
292 | * |
||
293 | * @param string $methods HTTP methods, comma separated |
||
294 | * |
||
295 | * @return Object $this |
||
296 | */ |
||
297 | final public function setSupportedMethods($methods) { |
||
315 | |||
316 | /** |
||
317 | * Set service content type |
||
318 | * |
||
319 | * @param string $type Content Type |
||
320 | * @return Object $this |
||
321 | */ |
||
322 | final public function setContentType($type) { |
||
329 | |||
330 | /** |
||
331 | * Get service declared content type |
||
332 | * |
||
333 | * @return string Content Type |
||
334 | */ |
||
335 | final public function getContentType() { |
||
340 | |||
341 | /** |
||
342 | * Set service charset |
||
343 | * |
||
344 | * @param string $charset Charset |
||
345 | * |
||
346 | * @return Object $this |
||
347 | */ |
||
348 | final public function setCharset($charset) { |
||
355 | |||
356 | /** |
||
357 | * Get service declared charset |
||
358 | * |
||
359 | * @return string Charset |
||
360 | */ |
||
361 | final public function getCharset() { |
||
366 | |||
367 | /** |
||
368 | * Set success status code |
||
369 | * |
||
370 | * @param integer $code HTTP status code (in case of success) |
||
371 | * @return Object $this |
||
372 | */ |
||
373 | View Code Duplication | final public function setStatusCode($code) { |
|
382 | |||
383 | /** |
||
384 | * Get service-defined status code |
||
385 | * |
||
386 | * @return integer HTTP status code (in case of success) |
||
387 | */ |
||
388 | final public function getStatusCode() { |
||
393 | |||
394 | /** |
||
395 | * Get dispatcher logger |
||
396 | * |
||
397 | * @return Object |
||
398 | */ |
||
399 | final public function getLogger() { |
||
404 | |||
405 | /** |
||
406 | * Get dispatcher cacher |
||
407 | * |
||
408 | * @return Object |
||
409 | */ |
||
410 | final public function getCacher() { |
||
415 | |||
416 | /** |
||
417 | * Set header component |
||
418 | * |
||
419 | * @param string $header Header name |
||
420 | * @param string $value Header content (optional) |
||
421 | * |
||
422 | * @return Object $this |
||
423 | */ |
||
424 | final public function setHeader($header, $value=null) { |
||
431 | |||
432 | /** |
||
433 | * Unset header component |
||
434 | * |
||
435 | * @param string $header Header name |
||
436 | * |
||
437 | * @return bool |
||
438 | */ |
||
439 | View Code Duplication | final public function unsetHeader($header) { |
|
452 | |||
453 | /** |
||
454 | * Get header component |
||
455 | * |
||
456 | * @param string $header Header name |
||
457 | * |
||
458 | * @return string Header component in case of success, false otherwise |
||
459 | */ |
||
460 | final public function getHeader($header) { |
||
467 | |||
468 | /** |
||
469 | * Set headers |
||
470 | * |
||
471 | * @param array $headers Headers array |
||
472 | * |
||
473 | * @return Object $this |
||
474 | */ |
||
475 | final public function setHeaders($headers) { |
||
482 | |||
483 | /** |
||
484 | * Unset headers |
||
485 | * |
||
486 | * @return Object $this |
||
487 | */ |
||
488 | final public function unsetHeaders() { |
||
495 | |||
496 | /** |
||
497 | * Get headers |
||
498 | * |
||
499 | * @return array Headers array |
||
500 | */ |
||
501 | final public function getHeaders() { |
||
506 | |||
507 | /** |
||
508 | * Get service-supported HTTP methods |
||
509 | * |
||
510 | * @return array Headers array |
||
511 | */ |
||
512 | final public function getSupportedMethods() { |
||
517 | |||
518 | /** |
||
519 | * Get service-implemented HTTP methods |
||
520 | * |
||
521 | * @return array Headers array |
||
522 | */ |
||
523 | final public function getImplementedMethods() { |
||
540 | |||
541 | /** |
||
542 | * Return the callable class method that reflect the requested one |
||
543 | * |
||
544 | * @return array Headers array |
||
545 | */ |
||
546 | final public function getCallableMethod($method) { |
||
553 | |||
554 | /** |
||
555 | * Get attributes and parameters that service expects |
||
556 | * |
||
557 | * @return array |
||
558 | */ |
||
559 | View Code Duplication | final public function getExpected($method) { |
|
572 | |||
573 | /** |
||
574 | * Get attributes and parameters that service likes |
||
575 | * |
||
576 | * @return array |
||
577 | */ |
||
578 | View Code Duplication | final public function getLiked($method) { |
|
591 | |||
592 | /** |
||
593 | * Set request attributes (used by dispatcher) |
||
594 | * |
||
595 | */ |
||
596 | final public function setAttributes($attributes) { |
||
601 | |||
602 | /** |
||
603 | * Get request attributes, populated by dispatcher |
||
604 | * |
||
605 | * @return array |
||
606 | */ |
||
607 | final public function getAttributes() { |
||
612 | |||
613 | /** |
||
614 | * Get request attribute, populated by dispatcher |
||
615 | * |
||
616 | * @param string $attribute Attribute to search for |
||
617 | * |
||
618 | * @return mixed |
||
619 | */ |
||
620 | final public function getAttribute($attribute) { |
||
627 | |||
628 | /** |
||
629 | * Set raw parameters (used by dispatcher) |
||
630 | * |
||
631 | */ |
||
632 | final public function setRawParameters($raw_parameters) { |
||
637 | |||
638 | /** |
||
639 | * Set request parameters (used by dispatcher) |
||
640 | * |
||
641 | */ |
||
642 | final public function setParameters($parameters) { |
||
647 | |||
648 | /** |
||
649 | * Get request parameters, populated by dispatcher |
||
650 | * |
||
651 | * @return array |
||
652 | */ |
||
653 | final public function getParameters($raw=false) { |
||
658 | |||
659 | /** |
||
660 | * Get request parameter, populated by dispatcher |
||
661 | * |
||
662 | * @param string $parameter Parameter to search for |
||
663 | * |
||
664 | * @return array |
||
665 | */ |
||
666 | final public function getParameter($parameter) { |
||
673 | |||
674 | /** |
||
675 | * Get request parameters, populated by dispatcher |
||
676 | * |
||
677 | */ |
||
678 | final public function setRequestHeaders($headers) { |
||
683 | |||
684 | /** |
||
685 | * Get request header component |
||
686 | * |
||
687 | * @param string $header Header to search for |
||
688 | * |
||
689 | * @return string Header component in case of success, null otherwise |
||
690 | */ |
||
691 | final public function getRequestHeader($header) { |
||
698 | /** |
||
699 | * Get headers |
||
700 | * |
||
701 | * @return array Headers array |
||
702 | */ |
||
703 | final public function getRequestHeaders() { |
||
708 | |||
709 | private function methodsToArray() { |
||
722 | |||
723 | } |
||
724 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.