Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/**
3
 * LICENSE
4
 *
5
 * This file is part of CFPropertyList.
6
 *
7
 * The PHP implementation of Apple's PropertyList can handle XML PropertyLists
8
 * as well as binary PropertyLists. It offers functionality to easily convert
9
 * data between worlds, e.g. recalculating timestamps from unix epoch to apple
10
 * epoch and vice versa. A feature to automagically create (guess) the plist
11
 * structure from a normal PHP data structure will help you dump your data to
12
 * plist in no time.
13
 *
14
 * Copyright (c) 2018 Teclib'
15
 *
16
 * Permission is hereby granted, free of charge, to any person obtaining a copy
17
 * of this software and associated documentation files (the "Software"), to deal
18
 * in the Software without restriction, including without limitation the rights
19
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
 * copies of the Software, and to permit persons to whom the Software is
21
 * furnished to do so, subject to the following conditions:
22
 *
23
 * The above copyright notice and this permission notice shall be included in all
24
 * copies or substantial portions of the Software.
25
 *
26
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
 * SOFTWARE.
33
 *
34
 * ------------------------------------------------------------------------------
35
 * @author    Rodney Rehm <rodney.rehm@medialize.de>
36
 * @author    Christian Kruse <cjk@wwwtech.de>
37
 * @copyright Copyright © 2018 Teclib
38
 * @package   plist
39
 * @license   MIT
40
 * @link      https://github.com/TECLIB/CFPropertyList/
41
 * @link      http://developer.apple.com/documentation/Darwin/Reference/ManPages/man5/plist.5.html Property Lists
42
 */
43
 
44
namespace CFPropertyList;
45
 
46
use Iterator;
47
use DOMDocument;
48
use DOMException;
49
use DOMImplementation;
50
use DOMNode;
51
 
52
/**
53
 * Property List
54
 * Interface for handling reading, editing and saving Property Lists as defined by Apple.
55
 * @example   example-read-01.php Read an XML PropertyList
56
 * @example   example-read-02.php Read a Binary PropertyList
57
 * @example   example-read-03.php Read a PropertyList without knowing the type
58
 * @example   example-create-01.php Using the CFPropertyList API
59
 * @example   example-create-02.php Using CFTypeDetector
60
 * @example   example-create-03.php Using CFTypeDetector with CFDate and CFData
61
 * @example   example-modify-01.php Read, modify and save a PropertyList
62
 * ------------------------------------------------------------------------------
63
 */
64
class CFPropertyList extends CFBinaryPropertyList implements Iterator
65
{
66
  /**
67
   * Format constant for binary format
68
   * @var integer
69
   */
70
    const FORMAT_BINARY = 1;
71
 
72
  /**
73
   * Format constant for xml format
74
   * @var integer
75
   */
76
    const FORMAT_XML = 2;
77
 
78
  /**
79
   * Format constant for automatic format recognizing
80
   * @var integer
81
   */
82
    const FORMAT_AUTO = 0;
83
 
84
  /**
85
   * Path of PropertyList
86
   * @var string
87
   */
88
    protected $file = null;
89
 
90
  /**
91
   * Detected format of PropertyList
92
   * @var integer
93
   */
94
    protected $detectedFormat = null;
95
 
96
  /**
97
   * Path of PropertyList
98
   * @var integer
99
   */
100
    protected $format = null;
101
 
102
  /**
103
   * CFType nodes
104
   * @var array
105
   */
106
    protected $value = array();
107
 
108
  /**
109
   * Position of iterator {@link http://php.net/manual/en/class.iterator.php}
110
   * @var integer
111
   */
112
    protected $iteratorPosition = 0;
113
 
114
  /**
115
   * List of Keys for numerical iterator access {@link http://php.net/manual/en/class.iterator.php}
116
   * @var array
117
   */
118
    protected $iteratorKeys = null;
119
 
120
  /**
121
   * List of NodeNames to ClassNames for resolving plist-files
122
   * @var array
123
   */
124
    protected static $types = array(
125
    'string'  => 'CFString',
126
    'real'    => 'CFNumber',
127
    'integer' => 'CFNumber',
128
    'date'    => 'CFDate',
129
    'true'    => 'CFBoolean',
130
    'false'   => 'CFBoolean',
131
    'data'    => 'CFData',
132
    'array'   => 'CFArray',
133
    'dict'    => 'CFDictionary'
134
    );
135
 
136
 
137
  /**
138
   * Create new CFPropertyList.
139
   * If a path to a PropertyList is specified, it is loaded automatically.
140
   * @param string $file Path of PropertyList
141
   * @param integer $format he format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link FORMAT_AUTO}
142
   * @throws IOException if file could not be read by {@link load()}
143
   * @uses $file for storing the current file, if specified
144
   * @uses load() for loading the plist-file
145
   */
146
    public function __construct($file = null, $format = self::FORMAT_AUTO)
147
    {
148
        $this->file = $file;
149
        $this->format = $format;
150
        $this->detectedFormat = $format;
151
        if ($this->file) {
152
            $this->load();
153
        }
154
    }
155
 
156
  /**
157
   * Load an XML PropertyList.
158
   * @param string $file Path of PropertyList, defaults to {@link $file}
159
   * @return void
160
   * @throws IOException if file could not be read
161
   * @throws DOMException if XML-file could not be read properly
162
   * @uses load() to actually load the file
163
   */
164
    public function loadXML($file = null)
165
    {
166
        $this->load($file, CFPropertyList::FORMAT_XML);
167
    }
168
 
169
  /**
170
   * Load an XML PropertyList.
171
   * @param resource $stream A stream containing the xml document.
172
   * @return void
173
   * @throws IOException if stream could not be read
174
   * @throws DOMException if XML-stream could not be read properly
175
   */
176
    public function loadXMLStream($stream)
177
    {
178
        if (($contents = stream_get_contents($stream)) === false) {
179
            throw IOException::notReadable('<stream>');
180
        }
181
        $this->parse($contents, CFPropertyList::FORMAT_XML);
182
    }
183
 
184
  /**
185
   * Load an binary PropertyList.
186
   * @param string $file Path of PropertyList, defaults to {@link $file}
187
   * @return void
188
   * @throws IOException if file could not be read
189
   * @throws PListException if binary plist-file could not be read properly
190
   * @uses load() to actually load the file
191
   */
192
    public function loadBinary($file = null)
193
    {
194
        $this->load($file, CFPropertyList::FORMAT_BINARY);
195
    }
196
 
197
  /**
198
   * Load an binary PropertyList.
199
   * @param stream $stream Stream containing the PropertyList
200
   * @return void
201
   * @throws IOException if file could not be read
202
   * @throws PListException if binary plist-file could not be read properly
203
   * @uses parse() to actually load the file
204
   */
205
    public function loadBinaryStream($stream)
206
    {
207
        if (($contents = stream_get_contents($stream)) === false) {
208
            throw IOException::notReadable('<stream>');
209
        }
210
        $this->parse($contents, CFPropertyList::FORMAT_BINARY);
211
    }
212
 
213
  /**
214
   * Load a plist file.
215
   * Load and import a plist file.
216
   * @param string $file Path of PropertyList, defaults to {@link $file}
217
   * @param integer $format The format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link $format}
218
   * @return void
219
   * @throws PListException if file format version is not 00
220
   * @throws IOException if file could not be read
221
   * @throws DOMException if plist file could not be parsed properly
222
   * @uses $file if argument $file was not specified
223
   * @uses $value reset to empty array
224
   * @uses import() for importing the values
225
   */
226
    public function load($file = null, $format = null)
227
    {
228
        $file = $file ? $file : $this->file;
229
        $format = $format !== null ? $format : $this->format;
230
        $this->value = array();
231
 
232
        if (!is_readable($file)) {
233
            throw IOException::notReadable($file);
234
        }
235
 
236
        switch ($format) {
237
            case CFPropertyList::FORMAT_BINARY:
238
                $this->readBinary($file);
239
                break;
240
            case CFPropertyList::FORMAT_AUTO: // what we now do is ugly, but neccessary to recognize the file format
241
                $fd = fopen($file, "rb");
242
                if (($magic_number = fread($fd, 8)) === false) {
243
                    throw IOException::notReadable($file);
244
                }
245
                fclose($fd);
246
 
247
                $filetype = substr($magic_number, 0, 6);
248
                $version  = substr($magic_number, -2);
249
 
250
                if ($filetype == "bplist") {
251
                    if ($version != "00") {
252
                        throw new PListException("Wrong file format version! Expected 00, got $version!");
253
                    }
254
                    $this->detectedFormat = CFPropertyList::FORMAT_BINARY;
255
                    $this->readBinary($file);
256
                    break;
257
                }
258
                $this->detectedFormat = CFPropertyList::FORMAT_XML;
259
              // else: xml format, break not neccessary
260
            case CFPropertyList::FORMAT_XML:
261
                $doc = new DOMDocument();
262
                $prevXmlErrors = libxml_use_internal_errors(true);
263
                libxml_clear_errors();
264
                if (!$doc->load($file)) {
265
                    $message = $this->getLibxmlErrors();
266
                    libxml_clear_errors();
267
                    libxml_use_internal_errors($prevXmlErrors);
268
                    throw new DOMException($message);
269
                }
270
                libxml_use_internal_errors($prevXmlErrors);
271
                $this->import($doc->documentElement, $this);
272
                break;
273
        }
274
    }
275
 
276
  /**
277
   * Parse a plist string.
278
   * Parse and import a plist string.
279
   * @param string $str String containing the PropertyList, defaults to {@link $content}
280
   * @param integer $format The format of the property list, see {@link FORMAT_XML}, {@link FORMAT_BINARY} and {@link FORMAT_AUTO}, defaults to {@link $format}
281
   * @return void
282
   * @throws PListException if file format version is not 00
283
   * @throws IOException if file could not be read
284
   * @throws DOMException if plist file could not be parsed properly
285
   * @uses $content if argument $str was not specified
286
   * @uses $value reset to empty array
287
   * @uses import() for importing the values
288
   */
289
    public function parse($str = null, $format = null)
290
    {
291
        $format = $format !== null ? $format : $this->format;
292
        $str = $str !== null ? $str : $this->content;
293
        if ($str === null || strlen($str) === 0) {
294
            throw IOException::readError('');
295
        }
296
        $this->value = array();
297
 
298
        switch ($format) {
299
            case CFPropertyList::FORMAT_BINARY:
300
                $this->parseBinary($str);
301
                break;
302
            case CFPropertyList::FORMAT_AUTO: // what we now do is ugly, but neccessary to recognize the file format
303
                if (($magic_number = substr($str, 0, 8)) === false) {
304
                    throw IOException::notReadable("<string>");
305
                }
306
 
307
                $filetype = substr($magic_number, 0, 6);
308
                $version  = substr($magic_number, -2);
309
 
310
                if ($filetype == "bplist") {
311
                    if ($version != "00") {
312
                        throw new PListException("Wrong file format version! Expected 00, got $version!");
313
                    }
314
                    $this->detectedFormat = CFPropertyList::FORMAT_BINARY;
315
                    $this->parseBinary($str);
316
                    break;
317
                }
318
                $this->detectedFormat = CFPropertyList::FORMAT_XML;
319
              // else: xml format, break not neccessary
320
            case CFPropertyList::FORMAT_XML:
321
                $doc = new DOMDocument();
322
                $prevXmlErrors = libxml_use_internal_errors(true);
323
                libxml_clear_errors();
324
                if (!$doc->loadXML($str)) {
325
                    $message = $this->getLibxmlErrors();
326
                    libxml_clear_errors();
327
                    libxml_use_internal_errors($prevXmlErrors);
328
                    throw new DOMException($message);
329
                }
330
                libxml_use_internal_errors($prevXmlErrors);
331
                $this->import($doc->documentElement, $this);
332
                break;
333
        }
334
    }
335
 
336
    protected function getLibxmlErrors()
337
    {
338
        return implode(', ', array_map(function (\LibXMLError $error) {
339
            return trim("{$error->line}:{$error->column} [$error->code] $error->message");
340
        }, libxml_get_errors()));
341
    }
342
 
343
  /**
344
   * Convert a DOMNode into a CFType.
345
   * @param DOMNode $node Node to import children of
346
   * @param CFDictionary|CFArray|CFPropertyList $parent
347
   * @return void
348
   */
349
    protected function import(DOMNode $node, $parent)
350
    {
351
      // abort if there are no children
352
        if (!$node->childNodes->length) {
353
            return;
354
        }
355
 
356
        foreach ($node->childNodes as $n) {
357
          // skip if we can't handle the element
358
            if (!isset(self::$types[$n->nodeName])) {
359
                continue;
360
            }
361
 
362
            $class = __NAMESPACE__ . '\\'.self::$types[$n->nodeName];
363
            $key = null;
364
 
365
          // find previous <key> if possible
366
            $ps = $n->previousSibling;
367
            while ($ps && $ps->nodeName == '#text' && $ps->previousSibling) {
368
                $ps = $ps->previousSibling;
369
            }
370
 
371
          // read <key> if possible
372
            if ($ps && $ps->nodeName == 'key') {
373
                $key = $ps->firstChild->nodeValue;
374
            }
375
 
376
            switch ($n->nodeName) {
377
                case 'date':
378
                    $value = new $class(CFDate::dateValue($n->nodeValue));
379
                    break;
380
                case 'data':
381
                    $value = new $class($n->nodeValue, true);
382
                    break;
383
                case 'string':
384
                    $value = new $class($n->nodeValue);
385
                    break;
386
 
387
                case 'real':
388
                case 'integer':
389
                    $value = new $class($n->nodeName == 'real' ? floatval($n->nodeValue) : intval($n->nodeValue));
390
                    break;
391
 
392
                case 'true':
393
                case 'false':
394
                    $value = new $class($n->nodeName == 'true');
395
                    break;
396
 
397
                case 'array':
398
                case 'dict':
399
                    $value = new $class();
400
                    $this->import($n, $value);
401
 
402
                    if ($value instanceof CFDictionary) {
403
                        $hsh = $value->getValue();
404
                        if (isset($hsh['CF$UID']) && count($hsh) == 1) {
405
                            $value = new CFUid($hsh['CF$UID']->getValue());
406
                        }
407
                    }
408
 
409
                    break;
410
            }
411
 
412
            if ($parent instanceof CFDictionary) {
413
                // Dictionaries need a key
414
                $parent->add($key, $value);
415
            } else {
416
                // others don't
417
                $parent->add($value);
418
            }
419
        }
420
    }
421
 
422
  /**
423
   * Convert CFPropertyList to XML and save to file.
424
   * @param string $file Path of PropertyList, defaults to {@link $file}
425
   * @param bool $formatted Print plist formatted (i.e. with newlines and whitespace indention) if true; defaults to false
426
   * @return void
427
   * @throws IOException if file could not be read
428
   * @uses $file if $file was not specified
429
   */
430
    public function saveXML($file, $formatted = false)
431
    {
432
        $this->save($file, CFPropertyList::FORMAT_XML, $formatted);
433
    }
434
 
435
  /**
436
   * Convert CFPropertyList to binary format (bplist00) and save to file.
437
   * @param string $file Path of PropertyList, defaults to {@link $file}
438
   * @return void
439
   * @throws IOException if file could not be read
440
   * @uses $file if $file was not specified
441
   */
442
    public function saveBinary($file)
443
    {
444
        $this->save($file, CFPropertyList::FORMAT_BINARY);
445
    }
446
 
447
  /**
448
   * Convert CFPropertyList to XML or binary and save to file.
449
   * @param string $file Path of PropertyList, defaults to {@link $file}
450
   * @param string $format Format of PropertyList, defaults to {@link $format}
451
   * @param bool $formatted_xml Print XML plist formatted (i.e. with newlines and whitespace indention) if true; defaults to false
452
   * @return void
453
   * @throws IOException if file could not be read
454
   * @throws PListException if evaluated $format is neither {@link FORMAT_XML} nor {@link FORMAL_BINARY}
455
   * @uses $file if $file was not specified
456
   * @uses $format if $format was not specified
457
   */
458
    public function save($file = null, $format = null, $formatted_xml = false)
459
    {
460
        $file = $file ? $file : $this->file;
461
        $format = $format ? $format : $this->format;
462
        if ($format == self::FORMAT_AUTO) {
463
            $format = $this->detectedFormat;
464
        }
465
 
466
        if (!in_array($format, array( self::FORMAT_BINARY, self::FORMAT_XML ))) {
467
            throw new PListException("format {$format} is not supported, use CFPropertyList::FORMAT_BINARY or CFPropertyList::FORMAT_XML");
468
        }
469
 
470
        if (!file_exists($file)) {
471
          // dirname("file.xml") == "" and is treated as the current working directory
472
            if (!is_writable(dirname($file))) {
473
                throw IOException::notWritable($file);
474
            }
475
        } elseif (!is_writable($file)) {
476
            throw IOException::notWritable($file);
477
        }
478
 
479
        $content = $format == self::FORMAT_BINARY ? $this->toBinary() : $this->toXML($formatted_xml);
480
 
481
        $fh = fopen($file, 'wb');
482
        fwrite($fh, $content);
483
        fclose($fh);
484
    }
485
 
486
  /**
487
   * Convert CFPropertyList to XML
488
   * @param bool $formatted Print plist formatted (i.e. with newlines and whitespace indention) if true; defaults to false
489
   * @return string The XML content
490
   */
491
    public function toXML($formatted = false)
492
    {
493
        $domimpl = new DOMImplementation();
494
      // <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
495
        $dtd = $domimpl->createDocumentType('plist', '-//Apple//DTD PLIST 1.0//EN', 'http://www.apple.com/DTDs/PropertyList-1.0.dtd');
496
        $doc = $domimpl->createDocument(null, "plist", $dtd);
497
        $doc->encoding = "UTF-8";
498
 
499
      // format output
500
        if ($formatted) {
501
            $doc->formatOutput = true;
502
            $doc->preserveWhiteSpace = true;
503
        }
504
 
505
      // get documentElement and set attribs
506
        $plist = $doc->documentElement;
507
        $plist->setAttribute('version', '1.0');
508
 
509
      // add PropertyList's children
510
        $plist->appendChild($this->getValue(true)->toXML($doc));
511
 
512
        return $doc->saveXML();
513
    }
514
 
515
 
516
  /************************************************************************************************
517
   *    M A N I P U L A T I O N
518
   ************************************************************************************************/
519
 
520
  /**
521
   * Add CFType to collection.
522
   * @param CFType $value CFType to add to collection
523
   * @return void
524
   * @uses $value for adding $value
525
   */
526
    public function add(CFType $value = null)
527
    {
528
      // anything but CFType is null, null is an empty string - sad but true
529
        if (!$value) {
530
            $value = new CFString();
531
        }
532
 
533
        $this->value[] = $value;
534
    }
535
 
536
  /**
537
   * Get CFType from collection.
538
   * @param integer $key Key of CFType to retrieve from collection
539
   * @return CFType CFType found at $key, null else
540
   * @uses $value for retrieving CFType of $key
541
   */
542
    public function get($key)
543
    {
544
        if (isset($this->value[$key])) {
545
            return $this->value[$key];
546
        }
547
        return null;
548
    }
549
 
550
  /**
551
   * Generic getter (magic)
552
   *
553
   * @param integer $key Key of CFType to retrieve from collection
554
   * @return CFType CFType found at $key, null else
555
   * @author Sean Coates <sean@php.net>
556
   * @link http://php.net/oop5.overloading
557
   */
558
    public function __get($key)
559
    {
560
        return $this->get($key);
561
    }
562
 
563
  /**
564
   * Remove CFType from collection.
565
   * @param integer $key Key of CFType to removes from collection
566
   * @return CFType removed CFType, null else
567
   * @uses $value for removing CFType of $key
568
   */
569
    public function del($key)
570
    {
571
        if (isset($this->value[$key])) {
572
            $t = $this->value[$key];
573
            unset($this->value[$key]);
574
            return $t;
575
        }
576
 
577
        return null;
578
    }
579
 
580
  /**
581
   * Empty the collection
582
   * @return array the removed CFTypes
583
   * @uses $value for removing CFType of $key
584
   */
585
    public function purge()
586
    {
587
        $t = $this->value;
588
        $this->value = array();
589
        return $t;
590
    }
591
 
592
  /**
593
   * Get first (and only) child, or complete collection.
594
   * @param boolean $cftype if set to true returned value will be CFArray instead of an array in case of a collection
595
   * @return CFType|array CFType or list of CFTypes known to the PropertyList
596
   * @uses $value for retrieving CFTypes
597
   */
598
    public function getValue($cftype = false)
599
    {
600
        if (count($this->value) === 1) {
601
            $t = array_values($this->value);
602
            return $t[0];
603
        }
604
        if ($cftype) {
605
            $t = new CFArray();
606
            foreach ($this->value as $value) {
607
                if ($value instanceof CFType) {
608
                    $t->add($value);
609
                }
610
            }
611
            return $t;
612
        }
613
        return $this->value;
614
    }
615
 
616
  /**
617
   * Create CFType-structure from guessing the data-types.
618
   * The functionality has been moved to the more flexible {@link CFTypeDetector} facility.
619
   * @param mixed $value Value to convert to CFType
620
   * @param array $options Configuration for casting values [autoDictionary, suppressExceptions, objectToArrayMethod, castNumericStrings]
621
   * @return CFType CFType based on guessed type
622
   * @uses CFTypeDetector for actual type detection
623
   * @deprecated
624
   */
625
    public static function guess($value, $options = array())
626
    {
627
        static $t = null;
628
        if ($t === null) {
629
            $t = new CFTypeDetector($options);
630
        }
631
 
632
        return $t->toCFType($value);
633
    }
634
 
635
 
636
  /************************************************************************************************
637
   *    S E R I A L I Z I N G
638
   ************************************************************************************************/
639
 
640
  /**
641
   * Get PropertyList as array.
642
   * @return mixed primitive value of first (and only) CFType, or array of primitive values of collection
643
   * @uses $value for retrieving CFTypes
644
   */
645
    public function toArray()
646
    {
647
        $a = array();
648
        foreach ($this->value as $value) {
649
            $a[] = $value->toArray();
650
        }
651
        if (count($a) === 1) {
652
            return $a[0];
653
        }
654
 
655
        return $a;
656
    }
657
 
658
 
659
  /************************************************************************************************
660
   *    I T E R A T O R   I N T E R F A C E
661
   ************************************************************************************************/
662
 
663
  /**
664
   * Rewind {@link $iteratorPosition} to first position (being 0)
665
   * @link http://php.net/manual/en/iterator.rewind.php
666
   * @return void
667
   * @uses $iteratorPosition set to 0
668
   * @uses $iteratorKeys store keys of {@link $value}
669
   */
670
    public function rewind(): void
671
    {
672
        $this->iteratorPosition = 0;
673
        $this->iteratorKeys = array_keys($this->value);
674
    }
675
 
676
  /**
677
   * Get Iterator's current {@link CFType} identified by {@link $iteratorPosition}
678
   * @link http://php.net/manual/en/iterator.current.php
679
   * @return mixed current Item
680
   * @uses $iteratorPosition identify current key
681
   * @uses $iteratorKeys identify current value
682
   */
683
    #[\ReturnTypeWillChange]
684
    public function current()
685
    {
686
        return $this->value[$this->iteratorKeys[$this->iteratorPosition]];
687
    }
688
 
689
  /**
690
   * Get Iterator's current key identified by {@link $iteratorPosition}
691
   * @link http://php.net/manual/en/iterator.key.php
692
   * @return mixed key of the current Item
693
   * @uses $iteratorPosition identify current key
694
   * @uses $iteratorKeys identify current value
695
   */
696
    #[\ReturnTypeWillChange]
697
    public function key()
698
    {
699
        return $this->iteratorKeys[$this->iteratorPosition];
700
    }
701
 
702
  /**
703
   * Increment {@link $iteratorPosition} to address next {@see CFType}
704
   * @link http://php.net/manual/en/iterator.next.php
705
   * @return void
706
   * @uses $iteratorPosition increment by 1
707
   */
708
    public function next(): void
709
    {
710
        $this->iteratorPosition++;
711
    }
712
 
713
  /**
714
   * Test if {@link $iteratorPosition} addresses a valid element of {@link $value}
715
   * @link http://php.net/manual/en/iterator.valid.php
716
   * @return boolean true if current position is valid, false else
717
   * @uses $iteratorPosition test if within {@link $iteratorKeys}
718
   * @uses $iteratorPosition test if within {@link $value}
719
   */
720
    public function valid(): bool
721
    {
722
        return isset($this->iteratorKeys[$this->iteratorPosition]) && isset($this->value[$this->iteratorKeys[$this->iteratorPosition]]);
723
    }
724
}
725
 
726
# eof