Completed
Push — master ( 081f49...6be6c0 )
by Gaetano
03:29
created

Value::__construct()   C

Complexity

Conditions 15
Paths 14

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 15.204

Importance

Changes 0
Metric Value
cc 15
nc 14
nop 2
dl 0
loc 35
ccs 28
cts 31
cp 0.9032
crap 15.204
rs 5.9166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpXmlRpc;
4
5
use PhpXmlRpc\Helper\Charset;
6
7
/**
8
 * This class enables the creation of values for XML-RPC, by encapsulating plain php values.
9
 */
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
            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 468
                case 'double':
69 468
                case 'string':
70 468
                case 'boolean':
71 468
                case 'dateTime.iso8601':
72 468
                case 'base64':
73 468
                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 527
        }
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 1
        }
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
161
    {
162 1
        if ($this->mytype == 0) {
163
            $this->mytype = static::$xmlrpcTypes['array'];
164
            $this->me['array'] = $values;
165
166
            return 1;
167 1
        } elseif ($this->mytype == 2) {
168
            // we're adding to an array here
169 1
            $this->me['array'] = array_merge($this->me['array'], $values);
170
171 1
            return 1;
172
        } else {
173
            error_log('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
174
            return 0;
175
        }
176
    }
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
192
    {
193 1
        if ($this->mytype == 0) {
194
            $this->mytype = static::$xmlrpcTypes['struct'];
195
            $this->me['struct'] = $values;
196
197
            return 1;
198 1
        } elseif ($this->mytype == 3) {
199
            // we're adding to a struct here
200 1
            $this->me['struct'] = array_merge($this->me['struct'], $values);
201
202 1
            return 1;
203
        } else {
204
            error_log('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
205
            return 0;
206
        }
207
    }
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;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
220 2
            case 2:
221 1
                return 'array';
222
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
223 1
            case 1:
224 1
                return 'scalar';
225
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
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 327
                    case static::$xmlrpcI4:
255 327
                    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 20
                        } 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 19
                        } 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 20
                        } 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 78
                }
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 96
                }
316 96
                $rs .= "</data>\n</array>";
317 96
                break;
318
            default:
319
                break;
320 510
        }
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 = '')
333
    {
334 510
        $val = reset($this->me);
335 510
        $typ = key($this->me);
336
337 510
        return '<value>' . $this->serializedata($typ, $val, $charsetEncoding) . "</value>\n";
338
    }
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)
352
    {
353 2
        return array_key_exists($key, $this->me['struct']);
354
    }
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)
367
    {
368 7
        return $this->me['struct'][$key];
369
    }
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()
376
    {
377
        reset($this->me['struct']);
378
    }
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()
388
    {
389
        return each($this->me['struct']);
390
    }
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()
398
    {
399 10
        $b = reset($this->me);
400
401 10
        return $b;
402
    }
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()
444
    {
445 1
        return count($this->me['array']);
446
    }
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()
456
    {
457 1
        return count($this->me['struct']);
458
    }
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()
469
    {
470 1
        switch ($this->mytype) {
471 1
            case 3:
472
                return count($this->me['struct']);
473 1
            case 2:
474 1
                return count($this->me['array']);
475
            case 1:
476
                return 1;
477
            default:
478
                return 0;
479
        }
480
    }
481
482
    /**
483
     * Implements the IteratorAggregate interface
484
     *
485
     * @return \ArrayIterator
486
     */
487
    public function getIterator() {
488 1
        switch ($this->mytype) {
489 1
            case 3:
490
                return new \ArrayIterator($this->me['struct']);
491 1
            case 2:
492 1
                return new \ArrayIterator($this->me['array']);
493
            case 1:
494
                return new \ArrayIterator($this->me);
495
            default:
496
                return new \ArrayIterator();
497
        }
498
    }
499
500
    public function offsetSet($offset, $value) {
501
502
        switch ($this->mytype) {
503
            case 3:
504
                if (!($value instanceof \PhpXmlRpc\Value)) {
505
                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Struct');
506
                }
507
                if (is_null($offset)) {
508
                    // disallow struct members with empty names
509
                    throw new \Exception('It is not possible to add anonymous members to an XML-RPC Struct');
510
                } else {
511
                    $this->me['struct'][$offset] = $value;
512
                }
513
                return;
514
            case 2:
515
                if (!($value instanceof \PhpXmlRpc\Value)) {
516
                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Array');
517
                }
518
                if (is_null($offset)) {
519
                    $this->me['array'][] = $value;
520
                } else {
521
                    // nb: we are not checking that $offset is above the existing array range...
522
                    $this->me['array'][$offset] = $value;
523
                }
524
                return;
525
            case 1:
526
// todo: handle i4 vs int
527
                reset($this->me);
528
                $type = key($this->me);
529
                if ($type != $offset) {
530
                    throw new \Exception('');
531
                }
532
                $this->me[$type] = $value;
533
                return;
534
            default:
535
                // it would be nice to allow empty values to be be turned into non-empty ones this way, but we miss info to do so
536
                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be set using array index");
537
        }
538
    }
539
540
    public function offsetExists($offset) {
541
        switch ($this->mytype) {
542
            case 3:
543
                return isset($this->me['struct'][$offset]);
544
            case 2:
545
                return isset($this->me['array'][$offset]);
546
            case 1:
547
// todo: handle i4 vs int
548
                return $offset == $this->scalartyp();
549
            default:
550
                return false;
551
        }
552
    }
553
554
    public function offsetUnset($offset) {
555
        switch ($this->mytype) {
556
            case 3:
557
                unset($this->me['struct'][$offset]);
558
                return;
559
            case 2:
560
                unset($this->me['array'][$offset]);
561
                return;
562
            case 1:
563
                // can not remove value from a scalar
564
                throw new \Exception("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
565
            default:
566
                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
567
        }
568
    }
569
570
    public function offsetGet($offset) {
571 1
        switch ($this->mytype) {
572 1
            case 3:
573 1
                return isset($this->me['struct'][$offset]) ? $this->me['struct'][$offset] : null;
574
            case 2:
575
                return isset($this->me['array'][$offset]) ? $this->me['array'][$offset] : null;
576
            case 1:
577
// on bad type: null or exception?
578
                $value = reset($this->me);
579
                $type = key($this->me);
580
                return $type == $offset ? $value : null;
581
            default:
582
// return null or exception?
583
                throw new \Exception("XML-RPC Value is of type 'undef' and can not be accessed using array index");
584
        }
585
    }
586
}
587