Completed
Push — master ( e7d171...e89ccd )
by Vitaliy
06:33
created

CollectionReadTrait   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 60.32%

Importance

Changes 14
Bugs 2 Features 7
Metric Value
wmc 24
c 14
b 2
f 7
lcom 1
cbo 0
dl 0
loc 209
ccs 38
cts 63
cp 0.6032
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
items() 0 1 ?
A toArray() 0 4 1
A isEmpty() 0 4 1
A count() 0 4 1
A contains() 0 4 1
A first() 0 4 2
A find() 0 13 4
A createCollection() 0 8 1
A getIterator() 0 7 1
A filter() 0 7 1
A map() 0 7 1
B sort() 0 22 4
A random() 0 8 3
A isWritable() 0 4 1
A bindAdditionalArguments() 0 10 2
1
<?php
2
3
namespace Nayjest\Collection;
4
5
use ArrayIterator;
6
use IteratorIterator;
7
8
/**
9
 * Trait CollectionReadTrait.
10
 *
11
 * @implements \Nayjest\Collection\CollectionWriteInterface
12
 */
13
trait CollectionReadTrait
14
{
15
    /**
16
     * Returns reference to array storing collection items.
17
     *
18
     * @return array
19
     */
20
    abstract protected function &items();
21
22
    /**
23
     * Creates collection of items.
24
     *
25
     * Override it if you need to implement
26
     * derived collection that requires specific initialization.
27
     *
28
     * @param array $items
29
     *
30
     * @return static
31
     */
32
    protected function createCollection(array $items)
33
    {
34
        $collection = new static();
35
        $itemsRef = &$collection->items();
36
        $itemsRef = $items;
37
38
        return $collection;
39
    }
40
41
    /**
42
     * Returns collection items in array.
43
     *
44
     * @return array
45
     */
46 15
    public function toArray()
47
    {
48 15
        return $this->items();
49
    }
50
51
    /**
52
     * Returns true if collection is empty.
53
     *
54
     * @return bool
55
     */
56 12
    public function isEmpty()
57
    {
58 12
        return count($this->items()) === 0;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 5
    public function count()
65
    {
66 5
        return count($this->items());
67
    }
68
69
    /**
70
     * Checks that collections contains target item.
71
     *
72
     * @param $item
73
     *
74
     * @return bool
75
     */
76 6
    public function contains($item)
77
    {
78 6
        return in_array($item, $this->items(), true);
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 12
    public function getIterator()
85
    {
86
        // Modifying or deleting values while iterating will not affect collection data.
87
        // ArrayIterator wrapped to IteratorIterator to deny modifying and deleting values while iterating.
88
        // That will help to avoid possible errors in client code.
89 12
        return new IteratorIterator(new ArrayIterator($this->items()));
90
    }
91
92
    /**
93
     * Returns first item of the collection or null if collection is empty.
94
     *
95
     * @return mixed|null
96
     */
97 4
    public function first()
98
    {
99 4
        return $this->isEmpty() ? null : array_values($this->items())[0];
100
    }
101
102
    /**
103
     * Iterates over each value in the <b>collection</b>
104
     * passing them to the <b>callback</b> function.
105
     * If the <b>callback</b> function returns true, the
106
     * current value from <b>collection</b> is returned into
107
     * the result collection.
108
     *
109
     * @param callable   $callback          the callback function to use
110
     * @param array|null $optionalArguments [optional] additional arguments passed to callback
111
     *
112
     * @return CollectionReadInterface|static filtered collection
113
     */
114 5
    public function filter(callable $callback, array $optionalArguments = null)
115
    {
116 5
        return $this->createCollection(array_filter(
117 5
            $this->items(),
118 5
            self::bindAdditionalArguments($callback, $optionalArguments)
119 5
        ));
120
    }
121
122
    /**
123
     * Iterates over each value in the <b>collection</b>
124
     * passing them to the <b>callback</b> function.
125
     * If the <b>callback</b> function returns true, the
126
     * current value from <b>collection</b> is returned.
127
     *
128
     * @param callable   $callback          the callback function to use
129
     * @param array|null $optionalArguments [optional] additional arguments passed to callback
130
     *
131
     * @return mixed|FALSE collection item or false if item is not found.
132
     */
133 5
    public function find(callable $callback, array $optionalArguments = null)
134
    {
135 5
        foreach ($this->items() as $item) {
136 5
            $arguments = ($optionalArguments === null)
137 5
                ? [$item]
138 5
                : array_merge([$item], $optionalArguments);
139 5
            if (call_user_func_array($callback, $arguments)) {
140 5
                return $item;
141
            }
142 5
        }
143
144 1
        return false;
145
    }
146
147
148
    /**
149
     * @param callable   $callback          the callback function to use
150
     * @param array|null $optionalArguments [optional] additional arguments passed to callback
151
     * @return CollectionReadInterface|static
152
     */
153
    public function map(callable $callback, array $optionalArguments = null)
154
    {
155
        return $this->createCollection(array_map(
156
            self::bindAdditionalArguments($callback, $optionalArguments),
157
            $this->items()
158
        ));
159
    }
160
161
    /**
162
     * Sorts collection items.
163
     *
164
     * This method preserves key order (stable sort) using Schwartzian Transform.
165
     * @see http://stackoverflow.com/questions/4353739/preserve-key-order-stable-sort-when-sorting-with-phps-uasort
166
     * @see http://en.wikipedia.org/wiki/Schwartzian_transform
167
     *
168
     * @param callable $compareFunction
169
     * @return CollectionReadInterface|static new collection with sorted items.
170
     */
171
    public function sort(callable $compareFunction)
172
    {
173
        $items = $this->toArray();
174
175
        # Sorting with Schwartzian Transform
176
        # If stable sort is not required,
177
        # following code can be replaced to usort($items, $compareFunction);
178
        $index = 0;
179
        foreach ($items as &$item) {
180
            $item = [$index++, $item];
181
        }
182
        usort($items, function($a, $b) use($compareFunction) {
183
            $result = call_user_func($compareFunction, $a[1], $b[1]);
184
            return $result == 0 ? $a[0] - $b[0] : $result;
185
        });
186
        foreach ($items as &$item) {
187
            $item = $item[1];
188
        }
189
        # End of sorting with Schwartzian Transform
190
191
        return $this->createCollection($items);
192
    }
193
194
    /**
195
     * @return mixed|null
196
     */
197 4
    public function random()
198
    {
199 4
        if ($this->isEmpty()) {
200 4
            return null;
201
        }
202 4
        $index = array_rand($this->items(), 1);
203 4
        return $index === null ? null : $this->items()[$index];
204
    }
205
206
    public function isWritable()
207
    {
208
        return $this instanceof CollectionWriteInterface;
209
    }
210
211 5
    protected static function bindAdditionalArguments(callable $callback, array $additionalArguments = null)
212
    {
213 5
        if ($additionalArguments === null) {
214 5
            return $callback;
215
        }
216 4
        return function ($item) use ($callback, $additionalArguments) {
217 4
            $arguments = array_merge([$item], $additionalArguments);
218 4
            return call_user_func_array($callback, $arguments);
219 4
        };
220
    }
221
}
222