1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 3.0.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Collection\Iterator;
16:
17: use ArrayIterator;
18: use Cake\Collection\Collection;
19: use Cake\Collection\CollectionInterface;
20:
21: /**
22: * Creates an iterator from another iterator that extract the requested column
23: * or property based on a path
24: */
25: class ExtractIterator extends Collection
26: {
27: /**
28: * A callable responsible for extracting a single value for each
29: * item in the collection.
30: *
31: * @var callable
32: */
33: protected $_extractor;
34:
35: /**
36: * Creates the iterator that will return the requested property for each value
37: * in the collection expressed in $path
38: *
39: * ### Example:
40: *
41: * Extract the user name for all comments in the array:
42: *
43: * ```
44: * $items = [
45: * ['comment' => ['body' => 'cool', 'user' => ['name' => 'Mark']],
46: * ['comment' => ['body' => 'very cool', 'user' => ['name' => 'Renan']]
47: * ];
48: * $extractor = new ExtractIterator($items, 'comment.user.name'');
49: * ```
50: *
51: * @param array|\Traversable $items The list of values to iterate
52: * @param string $path a dot separated string symbolizing the path to follow
53: * inside the hierarchy of each value so that the column can be extracted.
54: */
55: public function __construct($items, $path)
56: {
57: $this->_extractor = $this->_propertyExtractor($path);
58: parent::__construct($items);
59: }
60:
61: /**
62: * Returns the column value defined in $path or null if the path could not be
63: * followed
64: *
65: * @return mixed
66: */
67: public function current()
68: {
69: $extractor = $this->_extractor;
70:
71: return $extractor(parent::current());
72: }
73:
74: /**
75: * {@inheritDoc}
76: *
77: * We perform here some strictness analysis so that the
78: * iterator logic is bypassed entirely.
79: *
80: * @return \Iterator
81: */
82: public function unwrap()
83: {
84: $iterator = $this->getInnerIterator();
85:
86: if ($iterator instanceof CollectionInterface) {
87: $iterator = $iterator->unwrap();
88: }
89:
90: if (get_class($iterator) !== ArrayIterator::class) {
91: return $this;
92: }
93:
94: // ArrayIterator can be traversed strictly.
95: // Let's do that for performance gains
96:
97: $callback = $this->_extractor;
98: $res = [];
99:
100: /** @var \ArrayObject $iterator */
101: foreach ($iterator->getArrayCopy() as $k => $v) {
102: $res[$k] = $callback($v);
103: }
104:
105: return new ArrayIterator($res);
106: }
107: }
108: