Passed
Push — master ( 6f0f8c...d68788 )
by Gaetano
06:26
created

Value   F

Complexity

Total Complexity 107

Size/Duplication

Total Lines 657
Duplicated Lines 0 %

Test Coverage

Coverage 65.49%

Importance

Changes 16
Bugs 4 Features 0
Metric Value
eloc 278
dl 0
loc 657
rs 2
c 16
b 4
f 0
ccs 167
cts 255
cp 0.6549
wmc 107

22 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 32 15
B addScalar() 0 43 11
A serialize() 0 6 1
A offsetExists() 0 19 5
A getIterator() 0 12 4
B offsetSet() 0 39 11
A kindOf() 0 11 4
A addArray() 0 15 3
A offsetUnset() 0 16 4
A addStruct() 0 15 3
B offsetGet() 0 16 9
A count() 0 12 4
A structEach() 0 5 1
A structMemExists() 0 5 1
A scalarVal() 0 5 1
D serializeData() 0 92 23
A structMem() 0 5 1
A scalarTyp() 0 9 2
A structSize() 0 5 1
A arrayMem() 0 5 1
A arraySize() 0 5 1
A structReset() 0 5 1

How to fix   Complexity   

Complex Class

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.

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
2
3
namespace PhpXmlRpc;
4
5
use PhpXmlRpc\Exception\StateErrorException;
6
use PhpXmlRpc\Exception\TypeErrorException;
7
use PhpXmlRpc\Exception\ValueErrorException;
8
use PhpXmlRpc\Traits\CharsetEncoderAware;
9
use PhpXmlRpc\Traits\LoggerAware;
10
11
/**
12
 * This class enables the creation of values for XML-RPC, by encapsulating plain php values.
13
 */
14
class Value implements \Countable, \IteratorAggregate, \ArrayAccess
15
{
16
    use CharsetEncoderAware;
17
    use LoggerAware;
18
19
    public static $xmlrpcI4 = "i4";
20
    public static $xmlrpcI8 = "i8";
21
    public static $xmlrpcInt = "int";
22
    public static $xmlrpcBoolean = "boolean";
23
    public static $xmlrpcDouble = "double";
24
    public static $xmlrpcString = "string";
25
    public static $xmlrpcDateTime = "dateTime.iso8601";
26
    public static $xmlrpcBase64 = "base64";
27
    public static $xmlrpcArray = "array";
28
    public static $xmlrpcStruct = "struct";
29
    public static $xmlrpcValue = "undefined";
30
    public static $xmlrpcNull = "null";
31
32
    public static $xmlrpcTypes = array(
33
        "i4" => 1,
34
        "i8" => 1,
35
        "int" => 1,
36
        "boolean" => 1,
37
        "double" => 1,
38
        "string" => 1,
39
        "dateTime.iso8601" => 1,
40
        "base64" => 1,
41
        "array" => 2,
42
        "struct" => 3,
43
        "null" => 1,
44
    );
45
46
    /// @todo: do these need to be public?
47
    /** @var Value[]|mixed */
48
    public $me = array();
49
    /**
50
     * @var int
51
     * @internal
52
     */
53
    public $mytype = 0;
54 1
    /** @var string|null */
55
    public $_php_class = null;
56 1
57 1
    /**
58
     * Build an xml-rpc value.
59 1
     *
60
     * When no value or type is passed in, the value is left uninitialized, and the value can be added later.
61
     *
62
     * @param Value[]|mixed $val if passing in an array, all array elements should be PhpXmlRpc\Value themselves
63
     * @param string $type any valid xml-rpc type name (lowercase): i4, int, boolean, string, double, dateTime.iso8601,
64
     *                     base64, array, struct, null.
65
     *                     If null, 'string' is assumed.
66
     *                     You should refer to http://xmlrpc.com/spec.md for more information on what each of these mean.
67 594
     */
68
    public function __construct($val = -1, $type = '')
69 594
    {
70 435
        // optimization creep - do not call addXX, do it all inline.
71
        // downside: booleans will not be coerced anymore
72 594
        if ($val !== -1 || $type != '') {
73
            switch ($type) {
74
                case '':
75
                    $this->mytype = 1;
76
                    $this->me['string'] = $val;
77
                    break;
78
                case 'i4':
79
                case 'i8':
80
                case 'int':
81
                case 'double':
82
                case 'string':
83
                case 'boolean':
84
                case 'dateTime.iso8601':
85
                case 'base64':
86
                case 'null':
87
                    $this->mytype = 1;
88
                    $this->me[$type] = $val;
89
                    break;
90
                case 'array':
91 746
                    $this->mytype = 2;
92
                    $this->me['array'] = $val;
93
                    break;
94
                case 'struct':
95 746
                    $this->mytype = 3;
96 745
                    $this->me['struct'] = $val;
97 745
                    break;
98 251
                default:
99 251
                    $this->getLogger()->error("XML-RPC: " . __METHOD__ . ": not a known type ($type)");
100 251
            }
101 743
        }
102 743
    }
103 743
104 698
    /**
105 677
     * Add a single php value to an xml-rpc value.
106 469
     *
107 448
     * If the xml-rpc value is an array, the php value is added as its last element.
108 425
     * If the xml-rpc value is empty (uninitialized), this method makes it a scalar value, and sets that value.
109 404
     * Fails if the xml-rpc value is not an array (i.e. a struct or a scalar) and already initialized.
110 717
     *
111 717
     * @param mixed $val
112 717
     * @param string $type allowed values: i4, i8, int, boolean, string, double, dateTime.iso8601, base64, null.
113 403
     * @return int 1 or 0 on failure
114 241
     *
115 241
     * @todo arguably, as we have addArray to add elements to an Array value, and addStruct to add elements to a Struct
116 241
     *       value, we should not allow this method to add values to an Array. The 'scalar' in the method name refers to
117 295
     *       the expected state of the target object, not to the type of $val. Also, this works differently from
118 295
     *       addScalar/addStruct in that, when adding an element to an array, it wraps it into a new Value
119 295
     * @todo rename?
120 295
     */
121
    public function addScalar($val, $type = 'string')
122
    {
123
        $typeOf = null;
124
        if (isset(static::$xmlrpcTypes[$type])) {
125 746
            $typeOf = static::$xmlrpcTypes[$type];
126
        }
127
128
        if ($typeOf !== 1) {
129
            $this->getLogger()->error("XML-RPC: " . __METHOD__ . ": not a scalar type ($type)");
130
            return 0;
131
        }
132
133
        // coerce booleans into correct values
134
        /// @todo we should either do it for datetimes, integers, i8 and doubles, too, or just plain remove this check,
135
        ///       implemented on booleans only...
136
        if ($type == static::$xmlrpcBoolean) {
137
            if (strcasecmp($val, 'true') == 0 || $val == 1 || ($val == true && strcasecmp($val, 'false'))) {
138
                $val = true;
139 1
            } else {
140
                $val = false;
141 1
            }
142 1
        }
143 1
144
        switch ($this->mytype) {
145
            case 1:
146 1
                $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': scalar xmlrpc value can have only one value');
147
                return 0;
148
            case 3:
149
                $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': cannot add anonymous scalar to struct xmlrpc value');
150
                return 0;
151
            case 2:
152
                // we're adding a scalar value to an array here
153
                /// @todo should we try avoiding re-wrapping Value objects?
154 1
                $class = get_class($this);
155
                $this->me['array'][] = new $class($val, $type);
156
157
                return 1;
158
            default:
159
                // a scalar, so set the value and remember we're scalar
160
                $this->me[$type] = $val;
161
                $this->mytype = $typeOf;
162 1
163 1
                return 1;
164
        }
165
    }
166 1
167 1
    /**
168 1
     * Add an array of xml-rpc value objects to an xml-rpc value.
169
     *
170
     * If the xml-rpc value is an array, the elements are appended to the existing ones.
171
     * If the xml-rpc value is empty (uninitialized), this method makes it an array value, and sets that value.
172
     * Fails otherwise.
173
     *
174
     * @param Value[] $values
175
     * @return int 1 or 0 on failure
176
     *
177
     * @todo add some checking for $values to be an array of xml-rpc values?
178
     * @todo rename to addToArray?
179
     */
180
    public function addArray($values)
181
    {
182
        if ($this->mytype == 0) {
183
            $this->mytype = static::$xmlrpcTypes['array'];
184
            $this->me['array'] = $values;
185
186
            return 1;
187
        } elseif ($this->mytype == 2) {
188
            // we're adding to an array here
189
            $this->me['array'] = array_merge($this->me['array'], $values);
190
191
            return 1;
192
        } else {
193
            $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
194
            return 0;
195
        }
196 1
    }
197
198 1
    /**
199
     * Merges an array of named xml-rpc value objects into an xml-rpc value.
200
     *
201
     * If the xml-rpc value is a struct, the elements are merged with the existing ones (overwriting existing ones).
202
     * If the xml-rpc value is empty (uninitialized), this method makes it a struct value, and sets that value.
203 1
     * Fails otherwise.
204
     *
205 1
     * @param Value[] $values
206
     * @return int 1 or 0 on failure
207 1
     *
208
     * @todo add some checking for $values to be an array of xml-rpc values?
209
     * @todo rename to addToStruct?
210
     */
211
    public function addStruct($values)
212
    {
213
        if ($this->mytype == 0) {
214
            $this->mytype = static::$xmlrpcTypes['struct'];
215
            $this->me['struct'] = $values;
216
217
            return 1;
218
        } elseif ($this->mytype == 3) {
219
            // we're adding to a struct here
220
            $this->me['struct'] = array_merge($this->me['struct'], $values);
221
222
            return 1;
223
        } else {
224
            $this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': already initialized as a [' . $this->kindOf() . ']');
225
            return 0;
226
        }
227 1
    }
228
229 1
    /**
230
     * Returns a string describing the base type of the value.
231
     *
232
     * @return string either "struct", "array", "scalar" or "undef"
233
     */
234 1
    public function kindOf()
235
    {
236 1
        switch ($this->mytype) {
237
            case 3:
238 1
                return 'struct';
239
            case 2:
240
                return 'array';
241
            case 1:
242
                return 'scalar';
243
            default:
244
                return 'undef';
245
        }
246
    }
247
248
    /**
249
     * @param string $typ
250 627
     * @param Value[]|mixed $val
251
     * @param string $charsetEncoding
252 627
     * @return string
253 627
     *
254 150
     * @deprecated this should be folded back into serialize()
255 606
     */
256 214
    protected function serializeData($typ, $val, $charsetEncoding = '')
257 583
    {
258 583
        $rs = '';
259
260
        if (!isset(static::$xmlrpcTypes[$typ])) {
261
            return $rs;
262
        }
263
264
        switch (static::$xmlrpcTypes[$typ]) {
265
            case 1:
266
                switch ($typ) {
267
                    case static::$xmlrpcBase64:
268
                        $rs .= "<{$typ}>" . base64_encode($val) . "</{$typ}>";
0 ignored issues
show
Bug introduced by
It seems like $val can also be of type PhpXmlRpc\Value[]; however, parameter $string of base64_encode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

268
                        $rs .= "<{$typ}>" . base64_encode(/** @scrutinizer ignore-type */ $val) . "</{$typ}>";
Loading history...
269
                        break;
270 705
                    case static::$xmlrpcBoolean:
271
                        $rs .= "<{$typ}>" . ($val ? '1' : '0') . "</{$typ}>";
272 705
                        break;
273
                    case static::$xmlrpcString:
274 705
                        // Do NOT use htmlentities, since it will produce named html entities, which are invalid xml
275 2
                        $rs .= "<{$typ}>" . $this->getCharsetEncoder()->encodeEntities($val, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</{$typ}>";
276
                        break;
277
                    case static::$xmlrpcInt:
278 705
                    case static::$xmlrpcI4:
279 705
                    case static::$xmlrpcI8:
280
                        $rs .= "<{$typ}>" . (int)$val . "</{$typ}>";
281 684
                        break;
282 23
                    case static::$xmlrpcDouble:
283 23
                        // avoid using standard conversion of float to string because it is locale-dependent,
284 684
                        // and also because the xml-rpc spec forbids exponential notation.
285 46
                        // sprintf('%F') could be most likely ok, but it fails e.g. on 2e-14.
286 46
                        // The code below tries its best at keeping max precision while avoiding exp notation,
287 663
                        // but there is of course no limit in the number of decimal places to be used...
288
                        $rs .= "<{$typ}>" . preg_replace('/\\.?0+$/', '', number_format((double)$val, PhpXmlRpc::$xmlpc_double_precision, '.', '')) . "</{$typ}>";
289 573
                        break;
290 573
                    case static::$xmlrpcDateTime:
291 474
                        if (is_string($val)) {
292 68
                            $rs .= "<{$typ}>{$val}</{$typ}>";
293 68
                        // DateTimeInterface is not present in php 5.4...
294 430
                        } elseif (is_a($val, 'DateTimeInterface') || is_a($val, 'DateTime')) {
295 430
                            $rs .= "<{$typ}>" . $val->format('Ymd\TH:i:s') . "</{$typ}>";
296 68
                        } elseif (is_int($val)) {
297
                            $rs .= "<{$typ}>" . date('Ymd\TH:i:s', $val) . "</{$typ}>";
298
                        } else {
299
                            // not really a good idea here: but what should we output anyway? left for backward compat...
300
                            $rs .= "<{$typ}>{$val}</{$typ}>";
301
                        }
302 24
                        break;
303 24
                    case static::$xmlrpcNull:
304 47
                        if (PhpXmlRpc::$xmlrpc_null_apache_encoding) {
305 25
                            $rs .= "<ex:nil/>";
306 24
                        } else {
307 23
                            $rs .= "<nil/>";
308 22
                        }
309 23
                        break;
310 23
                    default:
311
                        // no standard type value should arrive here, but provide a possibility
312
                        // for xml-rpc values of unknown type...
313
                        $rs .= "<{$typ}>{$val}</{$typ}>";
314
                }
315 25
                break;
316 23
            case 3:
317 23
                // struct
318 23
                if ($this->_php_class) {
319
                    $rs .= '<struct php_class="' . $this->_php_class . "\">\n";
320 1
                } else {
321
                    $rs .= "<struct>\n";
322 23
                }
323
                $charsetEncoder = $this->getCharsetEncoder();
324
                /** @var Value $val2 */
325
                foreach ($val as $key2 => $val2) {
326
                    $rs .= '<member><name>' . $charsetEncoder->encodeEntities($key2, PhpXmlRpc::$xmlrpc_internalencoding, $charsetEncoding) . "</name>\n";
327
                    //$rs.=$this->serializeval($val2);
328 684
                    $rs .= $val2->serialize($charsetEncoding);
329 218
                    $rs .= "</member>\n";
330
                }
331 133
                $rs .= '</struct>';
332 1
                break;
333
            case 2:
334 133
                // array
335
                $rs .= "<array>\n<data>\n";
336 133
                /** @var Value $element */
337
                foreach ($val as $element) {
338 133
                    //$rs.=$this->serializeval($val[$i]);
339 112
                    $rs .= $element->serialize($charsetEncoding);
340
                }
341 112
                $rs .= "</data>\n</array>";
342 112
                break;
343
            default:
344 133
                break;
345 133
        }
346 154
347
        return $rs;
348 154
    }
349
350 154
    /**
351
     * Returns the xml representation of the value. XML prologue not included.
352 154
     *
353
     * @param string $charsetEncoding the charset to be used for serialization. If null, US-ASCII is assumed
354 154
     * @return string
355 154
     */
356
    public function serialize($charsetEncoding = '')
357
    {
358
        $val = reset($this->me);
359
        $typ = key($this->me);
360 705
361
        return '<value>' . $this->serializeData($typ, $val, $charsetEncoding) . "</value>\n";
0 ignored issues
show
Deprecated Code introduced by
The function PhpXmlRpc\Value::serializeData() has been deprecated: this should be folded back into serialize() ( Ignorable by Annotation )

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

361
        return '<value>' . /** @scrutinizer ignore-deprecated */ $this->serializeData($typ, $val, $charsetEncoding) . "</value>\n";

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...
362
    }
363
364
    /**
365
     * Checks whether a struct member with a given name is present.
366
     *
367
     * Works only on xml-rpc values of type struct.
368
     *
369
     * @param string $key the name of the struct member to be looked up
370 705
     * @return boolean
371
     *
372 705
     * @deprecated use array access, e.g. isset($val[$key])
373 705
     */
374
    public function structMemExists($key)
375 705
    {
376
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
377
378
        return array_key_exists($key, $this->me['struct']);
379
    }
380
381
    /**
382
     * Returns the value of a given struct member (an xml-rpc value object in itself).
383
     * Will raise a php warning if struct member of given name does not exist.
384
     *
385
     * @param string $key the name of the struct member to be looked up
386
     * @return Value
387
     *
388
     * @deprecated use array access, e.g. $val[$key]
389 2
     */
390
    public function structMem($key)
391
    {
392
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
393 2
394
        return $this->me['struct'][$key];
395
    }
396
397
    /**
398
     * Reset internal pointer for xml-rpc values of type struct.
399
     * @return void
400
     *
401
     * @deprecated iterate directly over the object using foreach instead
402
     */
403
    public function structReset()
404
    {
405
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
406 29
407
        reset($this->me['struct']);
408
    }
409
410 29
    /**
411
     * Return next member element for xml-rpc values of type struct.
412
     *
413
     * @return Value
414
     * @throws \Error starting with php 8.0, this function should not be used, as it will always throw
415
     *
416
     * @deprecated iterate directly over the object using foreach instead
417
     */
418
    public function structEach()
419
    {
420
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
421
422
        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...
423
    }
424
425
    /**
426
     * Returns the value of a scalar xml-rpc value (base 64 decoding is automatically handled here)
427
     *
428
     * @return mixed
429
     */
430
    public function scalarVal()
431
    {
432
        $b = reset($this->me);
433
434
        return $b;
435
    }
436
437
    /**
438
     * Returns the type of the xml-rpc value.
439
     *
440
     * @return string For integers, 'int' is always returned in place of 'i4'. 'i8' is considered a separate type and
441
     *                returned as such
442
     */
443
    public function scalarTyp()
444 660
    {
445
        reset($this->me);
446 660
        $a = key($this->me);
447
        if ($a == static::$xmlrpcI4) {
448 660
            $a = static::$xmlrpcInt;
449
        }
450
451
        return $a;
452
    }
453
454
    /**
455
     * Returns the m-th member of an xml-rpc value of array type.
456
     *
457
     * @param integer $key the index of the value to be retrieved (zero based)
458 454
     *
459
     * @return Value
460 454
     *
461 454
     * @deprecated use array access, e.g. $val[$key]
462 454
     */
463
    public function arrayMem($key)
464
    {
465
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
466 454
467
        return $this->me['array'][$key];
468
    }
469
470
    /**
471
     * Returns the number of members in an xml-rpc value of array type.
472
     *
473
     * @return integer
474
     *
475
     * @deprecated use count() instead
476
     */
477
    public function arraySize()
478 43
    {
479
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
480
481
        return count($this->me['array']);
482 43
    }
483
484
    /**
485
     * Returns the number of members in an xml-rpc value of struct type.
486
     *
487
     * @return integer
488
     *
489
     * @deprecated use count() instead
490
     */
491
    public function structSize()
492 44
    {
493
        //trigger_error('Method ' . __METHOD__ . ' is deprecated', E_USER_DEPRECATED);
494
495
        return count($this->me['struct']);
496 44
    }
497
498
    /**
499
     * Returns the number of members in an xml-rpc value:
500
     * - 0 for uninitialized values
501
     * - 1 for scalar values
502
     * - the number of elements for struct and array values
503
     *
504
     * @return integer
505
     */
506 23
    #[\ReturnTypeWillChange]
507
    public function count()
508
    {
509
        switch ($this->mytype) {
510 23
            case 3:
511
                return count($this->me['struct']);
512
            case 2:
513
                return count($this->me['array']);
514
            case 1:
515
                return 1;
516
            default:
517
                return 0;
518
        }
519
    }
520
521
    /**
522 25
     * Implements the IteratorAggregate interface
523
     * @internal required to be public to implement an Interface
524 25
     *
525 25
     * @return \ArrayIterator
526
     */
527 25
    #[\ReturnTypeWillChange]
528 25
    public function getIterator()
529
    {
530
        switch ($this->mytype) {
531
            case 3:
532
                return new \ArrayIterator($this->me['struct']);
533
            case 2:
534
                return new \ArrayIterator($this->me['array']);
535
            case 1:
536
                return new \ArrayIterator($this->me);
537
            default:
538
                return new \ArrayIterator();
539
        }
540
    }
541
542
    /**
543 258
     * @internal required to be public to implement an Interface
544
     *
545 258
     * @param mixed $offset
546 258
     * @param mixed $value
547 65
     * @return void
548 215
     *
549 215
     * @throws ValueErrorException|TypeErrorException
550
     */
551
    #[\ReturnTypeWillChange]
552
    public function offsetSet($offset, $value)
553
    {
554
        switch ($this->mytype) {
555
            case 3:
556
                if (!($value instanceof Value)) {
557
                    throw new TypeErrorException('It is only possible to add Value objects to an XML-RPC Struct');
558
                }
559
                if (is_null($offset)) {
560
                    // disallow struct members with empty names
561
                    throw new ValueErrorException('It is not possible to add anonymous members to an XML-RPC Struct');
562
                } else {
563
                    $this->me['struct'][$offset] = $value;
564 22
                }
565
                return;
566 22
            case 2:
567 22
                if (!($value instanceof Value)) {
568
                    throw new TypeErrorException('It is only possible to add Value objects to an XML-RPC Array');
569
                }
570
                if (is_null($offset)) {
571
                    $this->me['array'][] = $value;
572
                } else {
573
                    // nb: we are not checking that $offset is above the existing array range...
574
                    $this->me['array'][$offset] = $value;
575
                }
576
                return;
577
            case 1:
578 22
                /// @todo: should we handle usage of i4 to retrieve int (in both set/unset/isset)? After all we consider
579 22
                ///        'int' to be the preferred form, as evidenced in scalarTyp()
580
                reset($this->me);
581
                $type = key($this->me);
582 22
                if ($type != $offset && ($type != 'i4' || $offset != 'int')) {
583 22
                    throw new ValueErrorException('...');
584
                }
585
                $this->me[$type] = $value;
586
                return;
587
            default:
588 22
                // it would be nice to allow empty values to be turned into non-empty ones this way, but we miss info to do so
589
                throw new ValueErrorException("XML-RPC Value is of type 'undef' and its value can not be set using array index");
590
        }
591
    }
592
593
    /**
594
     * @internal required to be public to implement an Interface
595
     *
596
     * @param mixed $offset
597
     * @return bool
598
     */
599
    #[\ReturnTypeWillChange]
600
    public function offsetExists($offset)
601
    {
602
        switch ($this->mytype) {
603
            case 3:
604
                return isset($this->me['struct'][$offset]);
605
            case 2:
606
                return isset($this->me['array'][$offset]);
607
            case 1:
608
                // handle i4 vs int
609
                if ($offset == 'i4') {
610
                    // to be consistent with set and unset, we disallow usage of i4 to check for int
611
                    reset($this->me);
612
                    return $offset == key($this->me);
613
                } else {
614
                    return $offset == $this->scalarTyp();
615
                }
616
            default:
617
                return false;
618
        }
619
    }
620
621
    /**
622
     * @internal required to be public to implement an Interface
623
     *
624
     * @param mixed $offset
625
     * @return void
626
     *
627
     * @throws ValueErrorException|StateErrorException
628
     */
629
    #[\ReturnTypeWillChange]
630
    public function offsetUnset($offset)
631
    {
632
        switch ($this->mytype) {
633
            case 3:
634
                unset($this->me['struct'][$offset]);
635
                return;
636
            case 2:
637
                unset($this->me['array'][$offset]);
638
                return;
639
            case 1:
640
                // can not remove value from a scalar
641
                /// @todo feature creep - allow this to move back the value to 'undef' state?
642
                throw new StateErrorException("XML-RPC Value is of type 'scalar' and its value can not be unset using array index");
643
            default:
644
                throw new StateErrorException("XML-RPC Value is of type 'undef' and its value can not be unset using array index");
645
        }
646
    }
647
648
    /**
649
     * @internal required to be public to implement an Interface
650
     *
651
     * @param mixed $offset
652
     * @return mixed|Value|null
653
     * @throws StateErrorException
654
     */
655 175
    #[\ReturnTypeWillChange]
656
    public function offsetGet($offset)
657 175
    {
658 175
        switch ($this->mytype) {
659 173
            case 3:
660 24
                return isset($this->me['struct'][$offset]) ? $this->me['struct'][$offset] : null;
661 24
            case 2:
662
                return isset($this->me['array'][$offset]) ? $this->me['array'][$offset] : null;
663
            case 1:
664
                /// @todo what to return on bad type: null or exception?
665
                $value = reset($this->me);
666
                $type = key($this->me);
667
                return $type == $offset ? $value : (($type == 'i4' && $offset == 'int') ? $value : null);
668
            default:
669
                // return null or exception?
670
                throw new StateErrorException("XML-RPC Value is of type 'undef' and can not be accessed using array index");
671
        }
672
    }
673
}
674