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 Value 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 Value, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
10 | class Value implements \Countable, \IteratorAggregate, \ArrayAccess |
||
11 | { |
||
12 | public static $xmlrpcI4 = "i4"; |
||
13 | public static $xmlrpcI8 = "i8"; |
||
14 | public static $xmlrpcInt = "int"; |
||
15 | public static $xmlrpcBoolean = "boolean"; |
||
16 | public static $xmlrpcDouble = "double"; |
||
17 | public static $xmlrpcString = "string"; |
||
18 | public static $xmlrpcDateTime = "dateTime.iso8601"; |
||
19 | public static $xmlrpcBase64 = "base64"; |
||
20 | public static $xmlrpcArray = "array"; |
||
21 | public static $xmlrpcStruct = "struct"; |
||
22 | public static $xmlrpcValue = "undefined"; |
||
23 | public static $xmlrpcNull = "null"; |
||
24 | |||
25 | public static $xmlrpcTypes = array( |
||
26 | "i4" => 1, |
||
27 | "i8" => 1, |
||
28 | "int" => 1, |
||
29 | "boolean" => 1, |
||
30 | "double" => 1, |
||
31 | "string" => 1, |
||
32 | "dateTime.iso8601" => 1, |
||
33 | "base64" => 1, |
||
34 | "array" => 2, |
||
35 | "struct" => 3, |
||
36 | "null" => 1, |
||
37 | ); |
||
38 | |||
39 | /// @todo: do these need to be public? |
||
40 | public $me = array(); |
||
41 | public $mytype = 0; |
||
42 | public $_php_class = null; |
||
43 | |||
44 | /** |
||
45 | * Build an xmlrpc value. |
||
46 | * |
||
47 | * When no value or type is passed in, the value is left uninitialized, and the value can be added later. |
||
48 | * |
||
49 | * @param mixed $val if passing in an array, all array elements should be PhpXmlRpc\Value themselves |
||
50 | * @param string $type any valid xmlrpc type name (lowercase): i4, int, boolean, string, double, dateTime.iso8601, |
||
51 | * base64, array, struct, null. |
||
52 | * If null, 'string' is assumed. |
||
53 | * You should refer to http://www.xmlrpc.com/spec for more information on what each of these mean. |
||
54 | */ |
||
55 | 528 | public function __construct($val = -1, $type = '') |
|
56 | { |
||
57 | // optimization creep - do not call addXX, do it all inline. |
||
58 | // downside: booleans will not be coerced anymore |
||
59 | 528 | if ($val !== -1 || $type != '') { |
|
60 | 527 | switch ($type) { |
|
61 | 527 | case '': |
|
62 | 64 | $this->mytype = 1; |
|
63 | 64 | $this->me['string'] = $val; |
|
64 | 64 | break; |
|
65 | 468 | case 'i4': |
|
66 | 468 | case 'i8': |
|
67 | 468 | case 'int': |
|
68 | 295 | case 'double': |
|
69 | 276 | case 'string': |
|
70 | 188 | case 'boolean': |
|
71 | 188 | case 'dateTime.iso8601': |
|
72 | 169 | case 'base64': |
|
73 | 150 | case 'null': |
|
74 | 464 | $this->mytype = 1; |
|
75 | 464 | $this->me[$type] = $val; |
|
76 | 464 | break; |
|
77 | 130 | case 'array': |
|
78 | 99 | $this->mytype = 2; |
|
79 | 99 | $this->me['array'] = $val; |
|
80 | 99 | break; |
|
81 | 89 | case 'struct': |
|
82 | 89 | $this->mytype = 3; |
|
83 | 89 | $this->me['struct'] = $val; |
|
84 | 89 | break; |
|
85 | default: |
||
86 | error_log("XML-RPC: " . __METHOD__ . ": not a known type ($type)"); |
||
87 | } |
||
88 | } |
||
89 | 528 | } |
|
90 | |||
91 | /** |
||
92 | * Add a single php value to an xmlrpc value. |
||
93 | * |
||
94 | * If the xmlrpc value is an array, the php value is added as its last element. |
||
95 | * If the xmlrpc value is empty (uninitialized), this method makes it a scalar value, and sets that value. |
||
96 | * Fails if the xmlrpc value is not an array and already initialized. |
||
97 | * |
||
98 | * @param mixed $val |
||
99 | * @param string $type allowed values: i4, i8, int, boolean, string, double, dateTime.iso8601, base64, null. |
||
100 | * |
||
101 | * @return int 1 or 0 on failure |
||
102 | */ |
||
103 | 1 | public function addScalar($val, $type = 'string') |
|
104 | { |
||
105 | 1 | $typeOf = null; |
|
106 | 1 | if (isset(static::$xmlrpcTypes[$type])) { |
|
107 | 1 | $typeOf = static::$xmlrpcTypes[$type]; |
|
108 | } |
||
109 | |||
110 | 1 | if ($typeOf !== 1) { |
|
111 | error_log("XML-RPC: " . __METHOD__ . ": not a scalar type ($type)"); |
||
112 | return 0; |
||
113 | } |
||
114 | |||
115 | // coerce booleans into correct values |
||
116 | // NB: we should either do it for datetimes, integers, i8 and doubles, too, |
||
117 | // or just plain remove this check, implemented on booleans only... |
||
118 | 1 | if ($type == static::$xmlrpcBoolean) { |
|
119 | if (strcasecmp($val, 'true') == 0 || $val == 1 || ($val == true && strcasecmp($val, 'false'))) { |
||
120 | $val = true; |
||
121 | } else { |
||
122 | $val = false; |
||
123 | } |
||
124 | } |
||
125 | |||
126 | 1 | switch ($this->mytype) { |
|
127 | 1 | case 1: |
|
128 | error_log('XML-RPC: ' . __METHOD__ . ': scalar xmlrpc value can have only one value'); |
||
129 | return 0; |
||
130 | 1 | case 3: |
|
131 | 1 | error_log('XML-RPC: ' . __METHOD__ . ': cannot add anonymous scalar to struct xmlrpc value'); |
|
132 | 1 | return 0; |
|
133 | case 2: |
||
134 | // we're adding a scalar value to an array here |
||
135 | $this->me['array'][] = new Value($val, $type); |
||
136 | |||
137 | return 1; |
||
138 | default: |
||
139 | // a scalar, so set the value and remember we're scalar |
||
140 | $this->me[$type] = $val; |
||
141 | $this->mytype = $typeOf; |
||
142 | |||
143 | return 1; |
||
144 | } |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Add an array of xmlrpc value objects to an xmlrpc value. |
||
149 | * |
||
150 | * If the xmlrpc value is an array, the elements are appended to the existing ones. |
||
151 | * If the xmlrpc value is empty (uninitialized), this method makes it an array value, and sets that value. |
||
152 | * Fails otherwise. |
||
153 | * |
||
154 | * @param Value[] $values |
||
155 | * |
||
156 | * @return int 1 or 0 on failure |
||
157 | * |
||
158 | * @todo add some checking for $values to be an array of xmlrpc values? |
||
159 | */ |
||
160 | 1 | View Code Duplication | public function addArray($values) |
177 | |||
178 | /** |
||
179 | * Merges an array of named xmlrpc value objects into an xmlrpc value. |
||
180 | * |
||
181 | * If the xmlrpc value is a struct, the elements are merged with the existing ones (overwriting existing ones). |
||
182 | * If the xmlrpc value is empty (uninitialized), this method makes it a struct value, and sets that value. |
||
183 | * Fails otherwise. |
||
184 | * |
||
185 | * @param Value[] $values |
||
186 | * |
||
187 | * @return int 1 or 0 on failure |
||
188 | * |
||
189 | * @todo add some checking for $values to be an array? |
||
190 | */ |
||
191 | 1 | View Code Duplication | public function addStruct($values) |
208 | |||
209 | /** |
||
210 | * Returns a string containing either "struct", "array", "scalar" or "undef", describing the base type of the value. |
||
211 | * |
||
212 | * @return string |
||
213 | */ |
||
214 | 2 | public function kindOf() |
|
215 | { |
||
216 | 2 | switch ($this->mytype) { |
|
217 | 2 | case 3: |
|
218 | return 'struct'; |
||
219 | break; |
||
220 | 2 | case 2: |
|
221 | 1 | return 'array'; |
|
222 | break; |
||
223 | 1 | case 1: |
|
224 | 1 | return 'scalar'; |
|
225 | break; |
||
226 | default: |
||
227 | return 'undef'; |
||
228 | } |
||
229 | } |
||
230 | |||
231 | 510 | protected function serializedata($typ, $val, $charsetEncoding = '') |
|
232 | { |
||
233 | 510 | $rs = ''; |
|
234 | |||
235 | 510 | if (!isset(static::$xmlrpcTypes[$typ])) { |
|
236 | return $rs; |
||
237 | } |
||
238 | |||
239 | 510 | switch (static::$xmlrpcTypes[$typ]) { |
|
240 | 510 | case 1: |
|
241 | switch ($typ) { |
||
242 | 510 | case static::$xmlrpcBase64: |
|
243 | 19 | $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>"; |
|
244 | 19 | break; |
|
245 | 491 | case static::$xmlrpcBoolean: |
|
246 | 20 | $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>"; |
|
247 | 20 | break; |
|
248 | 472 | case static::$xmlrpcString: |
|
249 | // G. Giunta 2005/2/13: do NOT use htmlentities, since |
||
250 | // it will produce named html entities, which are invalid xml |
||
251 | 241 | $rs .= "<${typ}>" . Charset::instance()->encodeEntities($val, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</${typ}>"; |
|
252 | 241 | break; |
|
253 | 327 | case static::$xmlrpcInt: |
|
254 | 59 | case static::$xmlrpcI4: |
|
255 | 59 | case static::$xmlrpcI8: |
|
256 | 288 | $rs .= "<${typ}>" . (int)$val . "</${typ}>"; |
|
257 | 288 | break; |
|
258 | 59 | case static::$xmlrpcDouble: |
|
259 | // avoid using standard conversion of float to string because it is locale-dependent, |
||
260 | // and also because the xmlrpc spec forbids exponential notation. |
||
261 | // sprintf('%F') could be most likely ok but it fails eg. on 2e-14. |
||
262 | // The code below tries its best at keeping max precision while avoiding exp notation, |
||
263 | // but there is of course no limit in the number of decimal places to be used... |
||
264 | 20 | $rs .= "<${typ}>" . preg_replace('/\\.?0+$/', '', number_format((double)$val, 128, '.', '')) . "</${typ}>"; |
|
265 | 20 | break; |
|
266 | 40 | case static::$xmlrpcDateTime: |
|
267 | 20 | if (is_string($val)) { |
|
268 | 20 | $rs .= "<${typ}>${val}</${typ}>"; |
|
269 | 19 | } elseif (is_a($val, 'DateTime')) { |
|
270 | 19 | $rs .= "<${typ}>" . $val->format('Ymd\TH:i:s') . "</${typ}>"; |
|
271 | 19 | } elseif (is_int($val)) { |
|
272 | 19 | $rs .= "<${typ}>" . strftime("%Y%m%dT%H:%M:%S", $val) . "</${typ}>"; |
|
273 | } else { |
||
274 | // not really a good idea here: but what shall we output anyway? left for backward compat... |
||
275 | $rs .= "<${typ}>${val}</${typ}>"; |
||
276 | } |
||
277 | 20 | break; |
|
278 | 20 | case static::$xmlrpcNull: |
|
279 | 20 | if (PhpXmlRpc::$xmlrpc_null_apache_encoding) { |
|
280 | 20 | $rs .= "<ex:nil/>"; |
|
281 | } else { |
||
282 | 1 | $rs .= "<nil/>"; |
|
283 | } |
||
284 | 20 | break; |
|
285 | default: |
||
286 | // no standard type value should arrive here, but provide a possibility |
||
287 | // for xmlrpc values of unknown type... |
||
288 | $rs .= "<${typ}>${val}</${typ}>"; |
||
289 | } |
||
290 | 510 | break; |
|
291 | 116 | case 3: |
|
292 | // struct |
||
293 | 78 | if ($this->_php_class) { |
|
294 | $rs .= '<struct php_class="' . $this->_php_class . "\">\n"; |
||
295 | } else { |
||
296 | 78 | $rs .= "<struct>\n"; |
|
297 | } |
||
298 | 78 | $charsetEncoder = Charset::instance(); |
|
299 | /** @var Value $val2 */ |
||
300 | 78 | foreach ($val as $key2 => $val2) { |
|
301 | 78 | $rs .= '<member><name>' . $charsetEncoder->encodeEntities($key2, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</name>\n"; |
|
302 | //$rs.=$this->serializeval($val2); |
||
303 | 78 | $rs .= $val2->serialize($charsetEncoding); |
|
304 | 78 | $rs .= "</member>\n"; |
|
305 | } |
||
306 | 78 | $rs .= '</struct>'; |
|
307 | 78 | break; |
|
308 | 96 | case 2: |
|
309 | // array |
||
310 | 96 | $rs .= "<array>\n<data>\n"; |
|
311 | /** @var Value $element */ |
||
312 | 96 | foreach ($val as $element) { |
|
313 | //$rs.=$this->serializeval($val[$i]); |
||
314 | 77 | $rs .= $element->serialize($charsetEncoding); |
|
315 | } |
||
316 | 96 | $rs .= "</data>\n</array>"; |
|
317 | 96 | break; |
|
318 | default: |
||
319 | break; |
||
320 | } |
||
321 | |||
322 | 510 | return $rs; |
|
323 | } |
||
324 | |||
325 | /** |
||
326 | * Returns the xml representation of the value. XML prologue not included. |
||
327 | * |
||
328 | * @param string $charsetEncoding the charset to be used for serialization. if null, US-ASCII is assumed |
||
329 | * |
||
330 | * @return string |
||
331 | */ |
||
332 | public function serialize($charsetEncoding = '') |
||
339 | |||
340 | /** |
||
341 | * Checks whether a struct member with a given name is present. |
||
342 | * |
||
343 | * Works only on xmlrpc values of type struct. |
||
344 | * |
||
345 | * @param string $key the name of the struct member to be looked up |
||
346 | * |
||
347 | * @return boolean |
||
348 | * |
||
349 | * @deprecated use array access, e.g. isset($val[$key]) |
||
350 | */ |
||
351 | public function structmemexists($key) |
||
355 | |||
356 | /** |
||
357 | * Returns the value of a given struct member (an xmlrpc value object in itself). |
||
358 | * Will raise a php warning if struct member of given name does not exist. |
||
359 | * |
||
360 | * @param string $key the name of the struct member to be looked up |
||
361 | * |
||
362 | * @return Value |
||
363 | * |
||
364 | * @deprecated use array access, e.g. $val[$key] |
||
365 | */ |
||
366 | public function structmem($key) |
||
370 | |||
371 | /** |
||
372 | * Reset internal pointer for xmlrpc values of type struct. |
||
373 | * @deprecated iterate directly over the object using foreach instead |
||
374 | */ |
||
375 | public function structreset() |
||
379 | |||
380 | /** |
||
381 | * Return next member element for xmlrpc values of type struct. |
||
382 | * |
||
383 | * @return Value |
||
384 | * |
||
385 | * @deprecated iterate directly over the object using foreach instead |
||
386 | */ |
||
387 | public function structeach() |
||
391 | |||
392 | /** |
||
393 | * Returns the value of a scalar xmlrpc value (base 64 decoding is automatically handled here) |
||
394 | * |
||
395 | * @return mixed |
||
396 | */ |
||
397 | public function scalarval() |
||
403 | |||
404 | /** |
||
405 | * Returns the type of the xmlrpc value. |
||
406 | * |
||
407 | * For integers, 'int' is always returned in place of 'i4'. 'i8' is considered a separate type and returned as such |
||
408 | * |
||
409 | * @return string |
||
410 | */ |
||
411 | public function scalartyp() |
||
412 | { |
||
413 | 1 | reset($this->me); |
|
414 | 1 | $a = key($this->me); |
|
415 | 1 | if ($a == static::$xmlrpcI4) { |
|
416 | $a = static::$xmlrpcInt; |
||
417 | } |
||
418 | |||
419 | 1 | return $a; |
|
420 | } |
||
421 | |||
422 | /** |
||
423 | * Returns the m-th member of an xmlrpc value of array type. |
||
424 | * |
||
425 | * @param integer $key the index of the value to be retrieved (zero based) |
||
426 | * |
||
427 | * @return Value |
||
428 | * |
||
429 | * @deprecated use array access, e.g. $val[$key] |
||
430 | */ |
||
431 | public function arraymem($key) |
||
432 | { |
||
433 | return $this->me['array'][$key]; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Returns the number of members in an xmlrpc value of array type. |
||
438 | * |
||
439 | * @return integer |
||
440 | * |
||
441 | * @deprecated use count() instead |
||
442 | */ |
||
443 | public function arraysize() |
||
447 | |||
448 | /** |
||
449 | * Returns the number of members in an xmlrpc value of struct type. |
||
450 | * |
||
451 | * @return integer |
||
452 | * |
||
453 | * @deprecated use count() instead |
||
454 | */ |
||
455 | public function structsize() |
||
459 | |||
460 | /** |
||
461 | * Returns the number of members in an xmlrpc value: |
||
462 | * - 0 for uninitialized values |
||
463 | * - 1 for scalar values |
||
464 | * - the number of elements for struct and array values |
||
465 | * |
||
466 | * @return integer |
||
467 | */ |
||
468 | public function count() |
||
481 | |||
482 | /** |
||
483 | * Implements the IteratorAggregate interface |
||
484 | * |
||
485 | * @return \ArrayIterator |
||
486 | */ |
||
487 | public function getIterator() { |
||
499 | |||
500 | public function offsetSet($offset, $value) { |
||
539 | |||
540 | public function offsetExists($offset) { |
||
553 | |||
554 | public function offsetUnset($offset) { |
||
569 | |||
570 | public function offsetGet($offset) { |
||
586 | } |
||
587 |
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.