Passed
Push — master ( da2c1e...110169 )
by Gaetano
04:55 queued 01:13
created

Value::addScalar()   B

Complexity

Conditions 11
Paths 26

Size

Total Lines 41
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 30.2336

Importance

Changes 4
Bugs 2 Features 0
Metric Value
cc 11
eloc 25
c 4
b 2
f 0
nc 26
nop 2
dl 0
loc 41
rs 7.3166
ccs 11
cts 24
cp 0.4583
crap 30.2336

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
use PhpXmlRpc\Helper\Logger;
7
8
/**
9
 * This class enables the creation of values for XML-RPC, by encapsulating plain php values.
10
 */
11
class Value implements \Countable, \IteratorAggregate, \ArrayAccess
12
{
13
    public static $xmlrpcI4 = "i4";
14
    public static $xmlrpcI8 = "i8";
15
    public static $xmlrpcInt = "int";
16
    public static $xmlrpcBoolean = "boolean";
17
    public static $xmlrpcDouble = "double";
18
    public static $xmlrpcString = "string";
19
    public static $xmlrpcDateTime = "dateTime.iso8601";
20
    public static $xmlrpcBase64 = "base64";
21
    public static $xmlrpcArray = "array";
22
    public static $xmlrpcStruct = "struct";
23
    public static $xmlrpcValue = "undefined";
24
    public static $xmlrpcNull = "null";
25
26
    public static $xmlrpcTypes = array(
27
        "i4" => 1,
28
        "i8" => 1,
29
        "int" => 1,
30
        "boolean" => 1,
31
        "double" => 1,
32
        "string" => 1,
33
        "dateTime.iso8601" => 1,
34
        "base64" => 1,
35
        "array" => 2,
36
        "struct" => 3,
37
        "null" => 1,
38
    );
39
40
    /// @todo: do these need to be public?
41
    /** @var Value[]|mixed */
42
    public $me = array();
43
    /**
44
     * @var int $mytype
45
     * @internal
46
     */
47
    public $mytype = 0;
48
    /** @var string|null $_php_class */
49
    public $_php_class = null;
50
51
    /**
52
     * Build an xmlrpc value.
53
     *
54
     * When no value or type is passed in, the value is left uninitialized, and the value can be added later.
55
     *
56
     * @param mixed $val if passing in an array, all array elements should be PhpXmlRpc\Value themselves
57
     * @param string $type any valid xmlrpc type name (lowercase): i4, int, boolean, string, double, dateTime.iso8601,
58
     *                     base64, array, struct, null.
59
     *                     If null, 'string' is assumed.
60
     *                     You should refer to http://www.xmlrpc.com/spec for more information on what each of these mean.
61
     */
62 637
    public function __construct($val = -1, $type = '')
63
    {
64
        // optimization creep - do not call addXX, do it all inline.
65
        // downside: booleans will not be coerced anymore
66 637
        if ($val !== -1 || $type != '') {
67 636
            switch ($type) {
68 636
                case '':
69 207
                    $this->mytype = 1;
70 207
                    $this->me['string'] = $val;
71 207
                    break;
72 636
                case 'i4':
73 636
                case 'i8':
74 636
                case 'int':
75 595
                case 'double':
76 576
                case 'string':
77 405
                case 'boolean':
78 386
                case 'dateTime.iso8601':
79 366
                case 'base64':
80 347
                case 'null':
81 612
                    $this->mytype = 1;
82 612
                    $this->me[$type] = $val;
83 612
                    break;
84 346
                case 'array':
85 217
                    $this->mytype = 2;
86 217
                    $this->me['array'] = $val;
87 217
                    break;
88 248
                case 'struct':
89 248
                    $this->mytype = 3;
90 248
                    $this->me['struct'] = $val;
91 248
                    break;
92
                default:
93
                    Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": not a known type ($type)");
94
            }
95
        }
96 637
    }
97
98
    /**
99
     * Add a single php value to an xmlrpc value.
100
     *
101
     * If the xmlrpc value is an array, the php value is added as its last element.
102
     * If the xmlrpc value is empty (uninitialized), this method makes it a scalar value, and sets that value.
103
     * Fails if the xmlrpc value is not an array and already initialized.
104
     *
105
     * @param mixed $val
106
     * @param string $type allowed values: i4, i8, int, boolean, string, double, dateTime.iso8601, base64, null.
107
     *
108
     * @return int 1 or 0 on failure
109
     */
110 1
    public function addScalar($val, $type = 'string')
111
    {
112 1
        $typeOf = null;
113 1
        if (isset(static::$xmlrpcTypes[$type])) {
114 1
            $typeOf = static::$xmlrpcTypes[$type];
115
        }
116
117 1
        if ($typeOf !== 1) {
118
            Logger::instance()->errorLog("XML-RPC: " . __METHOD__ . ": not a scalar type ($type)");
119
            return 0;
120
        }
121
122
        // coerce booleans into correct values
123
        // NB: we should either do it for datetimes, integers, i8 and doubles, too,
124
        // or just plain remove this check, implemented on booleans only...
125 1
        if ($type == static::$xmlrpcBoolean) {
126
            if (strcasecmp($val, 'true') == 0 || $val == 1 || ($val == true && strcasecmp($val, 'false'))) {
127
                $val = true;
128
            } else {
129
                $val = false;
130
            }
131
        }
132
133 1
        switch ($this->mytype) {
134 1
            case 1:
135
                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': scalar xmlrpc value can have only one value');
136
                return 0;
137 1
            case 3:
138 1
                Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': cannot add anonymous scalar to struct xmlrpc value');
139 1
                return 0;
140
            case 2:
141
                // we're adding a scalar value to an array here
142
                $this->me['array'][] = new Value($val, $type);
143
144
                return 1;
145
            default:
146
                // a scalar, so set the value and remember we're scalar
147
                $this->me[$type] = $val;
148
                $this->mytype = $typeOf;
149
150
                return 1;
151
        }
152
    }
153
154
    /**
155
     * Add an array of xmlrpc value objects to an xmlrpc value.
156
     *
157
     * If the xmlrpc value is an array, the elements are appended to the existing ones.
158
     * If the xmlrpc value is empty (uninitialized), this method makes it an array value, and sets that value.
159
     * Fails otherwise.
160
     *
161
     * @param Value[] $values
162
     *
163
     * @return int 1 or 0 on failure
164
     *
165
     * @todo add some checking for $values to be an array of xmlrpc values?
166
     */
167 1
    public function addArray($values)
168
    {
169 1
        if ($this->mytype == 0) {
170
            $this->mytype = static::$xmlrpcTypes['array'];
171
            $this->me['array'] = $values;
172
173
            return 1;
174 1
        } elseif ($this->mytype == 2) {
175
            // we're adding to an array here
176 1
            $this->me['array'] = array_merge($this->me['array'], $values);
177
178 1
            return 1;
179
        } else {
180
            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
181
            return 0;
182
        }
183
    }
184
185
    /**
186
     * Merges an array of named xmlrpc value objects into an xmlrpc value.
187
     *
188
     * If the xmlrpc value is a struct, the elements are merged with the existing ones (overwriting existing ones).
189
     * If the xmlrpc value is empty (uninitialized), this method makes it a struct value, and sets that value.
190
     * Fails otherwise.
191
     *
192
     * @param Value[] $values
193
     *
194
     * @return int 1 or 0 on failure
195
     *
196
     * @todo add some checking for $values to be an array?
197
     */
198 1
    public function addStruct($values)
199
    {
200 1
        if ($this->mytype == 0) {
201
            $this->mytype = static::$xmlrpcTypes['struct'];
202
            $this->me['struct'] = $values;
203
204
            return 1;
205 1
        } elseif ($this->mytype == 3) {
206
            // we're adding to a struct here
207 1
            $this->me['struct'] = array_merge($this->me['struct'], $values);
208
209 1
            return 1;
210
        } else {
211
            Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
212
            return 0;
213
        }
214
    }
215
216
    /**
217
     * Returns a string containing either "struct", "array", "scalar" or "undef", describing the base type of the value.
218
     *
219
     * @return string
220
     */
221 549
    public function kindOf()
222
    {
223 549
        switch ($this->mytype) {
224 549
            case 3:
225 136
                return 'struct';
226 530
            case 2:
227 193
                return 'array';
228 510
            case 1:
229 510
                return 'scalar';
230
            default:
231
                return 'undef';
232
        }
233
    }
234
235 598
    protected function serializedata($typ, $val, $charsetEncoding = '')
236
    {
237 598
        $rs = '';
238
239 598
        if (!isset(static::$xmlrpcTypes[$typ])) {
240 2
            return $rs;
241
        }
242
243 598
        switch (static::$xmlrpcTypes[$typ]) {
244 598
            case 1:
245
                switch ($typ) {
246 579
                    case static::$xmlrpcBase64:
247 20
                        $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
248 20
                        break;
249 579
                    case static::$xmlrpcBoolean:
250 40
                        $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
251 40
                        break;
252 560
                    case static::$xmlrpcString:
253
                        // Do NOT use htmlentities, since it will produce named html entities, which are invalid xml
254 479
                        $rs .= "<${typ}>" . Charset::instance()->encodeEntities($val, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</${typ}>";
255 479
                        break;
256 408
                    case static::$xmlrpcInt:
257 60
                    case static::$xmlrpcI4:
258 60
                    case static::$xmlrpcI8:
259 369
                        $rs .= "<${typ}>" . (int)$val . "</${typ}>";
260 369
                        break;
261 60
                    case static::$xmlrpcDouble:
262
                        // avoid using standard conversion of float to string because it is locale-dependent,
263
                        // and also because the xmlrpc spec forbids exponential notation.
264
                        // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
265
                        // The code below tries its best at keeping max precision while avoiding exp notation,
266
                        // but there is of course no limit in the number of decimal places to be used...
267 21
                        $rs .= "<${typ}>" . preg_replace('/\\.?0+$/', '', number_format((double)$val, PhpXmlRpc::$xmlpc_double_precision, '.', '')) . "</${typ}>";
268 21
                        break;
269 41
                    case static::$xmlrpcDateTime:
270 21
                        if (is_string($val)) {
271 21
                            $rs .= "<${typ}>${val}</${typ}>";
272 20
                        } elseif (is_a($val, 'DateTime')) {
273 20
                            $rs .= "<${typ}>" . $val->format('Ymd\TH:i:s') . "</${typ}>";
274 20
                        } elseif (is_int($val)) {
275 20
                            $rs .= "<${typ}>" . strftime("%Y%m%dT%H:%M:%S", $val) . "</${typ}>";
276
                        } else {
277
                            // not really a good idea here: but what shall we output anyway? left for backward compat...
278
                            $rs .= "<${typ}>${val}</${typ}>";
279
                        }
280 21
                        break;
281 21
                    case static::$xmlrpcNull:
282 21
                        if (PhpXmlRpc::$xmlrpc_null_apache_encoding) {
283 21
                            $rs .= "<ex:nil/>";
284
                        } else {
285 1
                            $rs .= "<nil/>";
286
                        }
287 21
                        break;
288
                    default:
289
                        // no standard type value should arrive here, but provide a possibility
290
                        // for xmlrpc values of unknown type...
291
                        $rs .= "<${typ}>${val}</${typ}>";
292
                }
293 579
                break;
294 196
            case 3:
295
                // struct
296 119
                if ($this->_php_class) {
297 1
                    $rs .= '<struct php_class="' . $this->_php_class . "\">\n";
298
                } else {
299 119
                    $rs .= "<struct>\n";
300
                }
301 119
                $charsetEncoder = Charset::instance();
302
                /** @var Value $val2 */
303 119
                foreach ($val as $key2 => $val2) {
304 100
                    $rs .= '<member><name>' . $charsetEncoder->encodeEntities($key2, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</name>\n";
305
                    //$rs.=$this->serializeval($val2);
306 100
                    $rs .= $val2->serialize($charsetEncoding);
307 100
                    $rs .= "</member>\n";
308
                }
309 119
                $rs .= '</struct>';
310 119
                break;
311 138
            case 2:
312
                // array
313 138
                $rs .= "<array>\n<data>\n";
314
                /** @var Value $element */
315 138
                foreach ($val as $element) {
316
                    //$rs.=$this->serializeval($val[$i]);
317 138
                    $rs .= $element->serialize($charsetEncoding);
318
                }
319 138
                $rs .= "</data>\n</array>";
320 138
                break;
321
            default:
322
                break;
323
        }
324
325 598
        return $rs;
326
    }
327
328
    /**
329
     * Returns the xml representation of the value. XML prologue not included.
330
     *
331
     * @param string $charsetEncoding the charset to be used for serialization. if null, US-ASCII is assumed
332
     *
333
     * @return string
334
     */
335 598
    public function serialize($charsetEncoding = '')
336
    {
337 598
        $val = reset($this->me);
338 598
        $typ = key($this->me);
339
340 598
        return '<value>' . $this->serializedata($typ, $val, $charsetEncoding) . "</value>\n";
341
    }
342
343
    /**
344
     * Checks whether a struct member with a given name is present.
345
     *
346
     * Works only on xmlrpc values of type struct.
347
     *
348
     * @param string $key the name of the struct member to be looked up
349
     *
350
     * @return boolean
351
     *
352
     * @deprecated use array access, e.g. isset($val[$key])
353
     */
354 2
    public function structmemexists($key)
355
    {
356
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
357
358 2
        return array_key_exists($key, $this->me['struct']);
359
    }
360
361
    /**
362
     * Returns the value of a given struct member (an xmlrpc value object in itself).
363
     * Will raise a php warning if struct member of given name does not exist.
364
     *
365
     * @param string $key the name of the struct member to be looked up
366
     *
367
     * @return Value
368
     *
369
     * @deprecated use array access, e.g. $val[$key]
370
     */
371 27
    public function structmem($key)
372
    {
373
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
374
375 27
        return $this->me['struct'][$key];
376
    }
377
378
    /**
379
     * Reset internal pointer for xmlrpc values of type struct.
380
     * @deprecated iterate directly over the object using foreach instead
381
     */
382
    public function structreset()
383
    {
384
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
385
386
        reset($this->me['struct']);
387
    }
388
389
    /**
390
     * Return next member element for xmlrpc values of type struct.
391
     *
392
     * @return Value
393
     * @throws \Error starting with php 8.0, this function should not be used, as it will always throw
394
     *
395
     * @deprecated iterate directly over the object using foreach instead
396
     */
397
    public function structeach()
398
    {
399
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
400
401
        return @each($this->me['struct']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return @each($this->me['struct']) returns the type array which is incompatible with the documented return type PhpXmlRpc\Value.
Loading history...
Deprecated Code introduced by
The function each() has been deprecated: 7.2 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

401
        return @/** @scrutinizer ignore-deprecated */ each($this->me['struct']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
402
    }
403
404
    /**
405
     * Returns the value of a scalar xmlrpc value (base 64 decoding is automatically handled here)
406
     *
407
     * @return mixed
408
     */
409 561
    public function scalarval()
410
    {
411 561
        $b = reset($this->me);
412
413 561
        return $b;
414
    }
415
416
    /**
417
     * Returns the type of the xmlrpc value.
418
     *
419
     * For integers, 'int' is always returned in place of 'i4'. 'i8' is considered a separate type and returned as such
420
     *
421
     * @return string
422
     */
423 393
    public function scalartyp()
424
    {
425 393
        reset($this->me);
426 393
        $a = key($this->me);
427 393
        if ($a == static::$xmlrpcI4) {
428
            $a = static::$xmlrpcInt;
429
        }
430
431 393
        return $a;
432
    }
433
434
    /**
435
     * Returns the m-th member of an xmlrpc value of array type.
436
     *
437
     * @param integer $key the index of the value to be retrieved (zero based)
438
     *
439
     * @return Value
440
     *
441
     * @deprecated use array access, e.g. $val[$key]
442
     */
443 39
    public function arraymem($key)
444
    {
445
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
446
447 39
        return $this->me['array'][$key];
448
    }
449
450
    /**
451
     * Returns the number of members in an xmlrpc value of array type.
452
     *
453
     * @return integer
454
     *
455
     * @deprecated use count() instead
456
     */
457 40
    public function arraysize()
458
    {
459
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
460
461 40
        return count($this->me['array']);
462
    }
463
464
    /**
465
     * Returns the number of members in an xmlrpc value of struct type.
466
     *
467
     * @return integer
468
     *
469
     * @deprecated use count() instead
470
     */
471 21
    public function structsize()
472
    {
473
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
474
475 21
        return count($this->me['struct']);
476
    }
477
478
    /**
479
     * Returns the number of members in an xmlrpc value:
480
     * - 0 for uninitialized values
481
     * - 1 for scalar values
482
     * - the number of elements for struct and array values
483
     *
484
     * @return integer
485
     */
486 22
    public function count()
487
    {
488 22
        switch ($this->mytype) {
489 22
            case 3:
490
                return count($this->me['struct']);
491 22
            case 2:
492 22
                return count($this->me['array']);
493
            case 1:
494
                return 1;
495
            default:
496
                return 0;
497
        }
498
    }
499
500
    /**
501
     * Implements the IteratorAggregate interface
502
     *
503
     * @return \ArrayIterator
504
     */
505 233
    public function getIterator()
506
    {
507 233
        switch ($this->mytype) {
508 233
            case 3:
509 59
                return new \ArrayIterator($this->me['struct']);
510 194
            case 2:
511 194
                return new \ArrayIterator($this->me['array']);
512
            case 1:
513
                return new \ArrayIterator($this->me);
514
            default:
515
                return new \ArrayIterator();
516
        }
517
    }
518
519
    /**
520
     * @internal required to be public to implement an Interface
521
     * @param mixed $offset
522
     * @param mixed $value
523
     * @throws \Exception
524
     */
525 20
    public function offsetSet($offset, $value)
526
    {
527 20
        switch ($this->mytype) {
528 20
            case 3:
529
                if (!($value instanceof \PhpXmlRpc\Value)) {
530
                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Struct');
531
                }
532
                if (is_null($offset)) {
533
                    // disallow struct members with empty names
534
                    throw new \Exception('It is not possible to add anonymous members to an XML-RPC Struct');
535
                } else {
536
                    $this->me['struct'][$offset] = $value;
537
                }
538
                return;
539 20
            case 2:
540 20
                if (!($value instanceof \PhpXmlRpc\Value)) {
541
                    throw new \Exception('It is only possible to add Value objects to an XML-RPC Array');
542
                }
543 20
                if (is_null($offset)) {
544 20
                    $this->me['array'][] = $value;
545
                } else {
546
                    // nb: we are not checking that $offset is above the existing array range...
547
                    $this->me['array'][$offset] = $value;
548
                }
549 20
                return;
550
            case 1:
551
// todo: handle i4 vs int
552
                reset($this->me);
553
                $type = key($this->me);
554
                if ($type != $offset) {
555
                    throw new \Exception('');
556
                }
557
                $this->me[$type] = $value;
558
                return;
559
            default:
560
                // 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
561
                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be set using array index");
562
        }
563
    }
564
565
    /**
566
     * @internal required to be public to implement an Interface
567
     * @param mixed $offset
568
     * @return bool
569
     */
570
    public function offsetExists($offset)
571
    {
572
        switch ($this->mytype) {
573
            case 3:
574
                return isset($this->me['struct'][$offset]);
575
            case 2:
576
                return isset($this->me['array'][$offset]);
577
            case 1:
578
// todo: handle i4 vs int
579
                return $offset == $this->scalartyp();
580
            default:
581
                return false;
582
        }
583
    }
584
585
    /**
586
     * @internal required to be public to implement an Interface
587
     * @param mixed $offset
588
     * @throws \Exception
589
     */
590
    public function offsetUnset($offset)
591
    {
592
        switch ($this->mytype) {
593
            case 3:
594
                unset($this->me['struct'][$offset]);
595
                return;
596
            case 2:
597
                unset($this->me['array'][$offset]);
598
                return;
599
            case 1:
600
                // can not remove value from a scalar
601
                throw new \Exception("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
602
            default:
603
                throw new \Exception("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
604
        }
605
    }
606
607
    /**
608
     * @internal required to be public to implement an Interface
609
     * @param mixed $offset
610
     * @return mixed|Value|null
611
     * @throws \Exception
612
     */
613 139
    public function offsetGet($offset)
614
    {
615 139
        switch ($this->mytype) {
616 139
            case 3:
617 138
                return isset($this->me['struct'][$offset]) ? $this->me['struct'][$offset] : null;
618 21
            case 2:
619 21
                return isset($this->me['array'][$offset]) ? $this->me['array'][$offset] : null;
620
            case 1:
621
// on bad type: null or exception?
622
                $value = reset($this->me);
623
                $type = key($this->me);
624
                return $type == $offset ? $value : null;
625
            default:
626
// return null or exception?
627
                throw new \Exception("XML-RPC Value is of type 'undef' and can not be accessed using array index");
628
        }
629
    }
630
}
631