Issues (5)

src/LinqCollection.php (2 issues)

1
<?php
2
3
namespace SubjectivePHP\Linq;
4
5
use ArrayIterator;
6
use CallbackFilterIterator;
7
use Countable;
8
use InvalidArgumentException;
9
use Traversable;
10
use IteratorAggregate;
11
use LimitIterator;
12
13
final class LinqCollection implements IteratorAggregate, Countable
14
{
15
    /**
16
     * @var Traversable
17
     */
18
    private $traversable;
19
20
    /**
21
     * LinqCollection constructor.
22
     *
23
     * @param Traversable $traversable The items to be linq'd.
24
     */
25
    private function __construct(Traversable $traversable)
26
    {
27
        $this->traversable = $traversable;
28
    }
29
30
    /**
31
     * Create a new LinqCollection from the given input.
32
     *
33
     * @param array|Traversable $collection The items to be linq'd.
34
     *
35
     * @return LinqCollection
36
     *
37
     * @throws InvalidArgumentException Thrown if the given $collection is not an array or Traversable.
38
     */
39
    public static function from($collection) : LinqCollection
40
    {
41
        if ($collection instanceof Traversable) {
42
            return new self($collection);
43
        }
44
45
        if (is_array($collection)) {
46
            return new self(new ArrayIterator($collection));
47
        }
48
49
        throw new InvalidArgumentException(
50
            sprintf("Cannot create linq collection with type '%s'", gettype($collection))
51
        );
52
    }
53
54
    /**
55
     * Returns the first element in a sequence that satisfies a specified condition.
56
     *
57
     * @param callable|null $predicate A function to test each element for a condition.
58
     *
59
     * @return mixed
60
     *
61
     * @throws InvalidOperationException Thrown if no element satisfies the condition in $predicate or the sequence
62
     *                                   is empty.
63
     */
64
    public function first(callable $predicate = null)
65
    {
66
        $noMatchValue = new \StdClass();
67
        $result = $this->firstOrDefault($predicate, $noMatchValue);
68
        if ($result === $noMatchValue) {
69
            throw new InvalidOperationException('No elements in sequence match the condition.');
70
        }
71
72
        return $result;
73
    }
74
75
    /**
76
     * Returns the first element of a sequence, or a default value if no element is found.
77
     *
78
     * @param callable|null $predicate    A function to test each element for a condition.
79
     * @param mixed         $defaultValue The default value to return.
80
     *
81
     * @return mixed
82
     */
83
    public function firstOrDefault(callable $predicate = null, $defaultValue = null)
84
    {
85
        $predicate = $predicate ?? function ($item) : bool {
0 ignored issues
show
The parameter $item is not used and could be removed. ( Ignorable by Annotation )

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

85
        $predicate = $predicate ?? function (/** @scrutinizer ignore-unused */ $item) : bool {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
86
            return true;
87
        };
88
        foreach ($this->traversable as $item) {
89
            if ($predicate($item)) {
90
                return $item;
91
            }
92
        }
93
94
        return $defaultValue;
95
    }
96
97
    /**
98
     * Filters a sequence of values based on a predicate.
99
     *
100
     * @param callable $where A function to test each source element for a condition
101
     *
102
     * @return LinqCollection
103
     */
104
    public function where(callable $where) : LinqCollection
105
    {
106
        return new self(new CallbackFilterIterator($this->traversable, $where));
107
    }
108
109
    /**
110
     * Bypasses a specified number of elements in a sequence and then returns the remaining elements
111
     *
112
     * @param int $offset The number of elements to skip before returning the remaining elements.
113
     *
114
     * @return LinqCollection
115
     */
116
    public function skip(int $offset) : LinqCollection
117
    {
118
        return new self(new LimitIterator($this->traversable, $offset));
119
    }
120
121
    /**
122
     * Returns a specified number of contiguous elements from the start of a sequence.
123
     *
124
     * @param int $count The number of elements to return.
125
     *
126
     * @return LinqCollection
127
     */
128
    public function take(int $count) : LinqCollection
129
    {
130
        return new self(new LimitIterator($this->traversable, 0, $count));
131
    }
132
133
    /**
134
     * Counts the elements in the sequence.
135
     *
136
     * @return int
137
     */
138
    public function count() : int
139
    {
140
        return iterator_count($this->traversable);
141
    }
142
143
    /**
144
     * Projects each element of a sequence into a new form.
145
     *
146
     * @param callable $selector A transform function to apply to each element.
147
     *
148
     * @return LinqCollection
149
     */
150
    public function select(callable $selector) : LinqCollection
151
    {
152
        return new self(new SelectIterator($this->traversable, $selector));
153
    }
154
155
    /**
156
     * Sorts the elements of a sequence in order by using a specified comparer.
157
     *
158
     * @param callable $comparer A function used to compare each element in the sequence.
159
     *
160
     * @return LinqCollection
161
     */
162
    public function orderBy(callable $comparer) : LinqCollection
163
    {
164
        $iterator = new ArrayIterator(iterator_to_array($this->traversable));
165
        $iterator->uasort($comparer);
0 ignored issues
show
$comparer of type callable is incompatible with the type string expected by parameter $cmp_function of ArrayIterator::uasort(). ( Ignorable by Annotation )

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

165
        $iterator->uasort(/** @scrutinizer ignore-type */ $comparer);
Loading history...
166
        return new self($iterator);
167
    }
168
169
    /**
170
     * Return an external iterator.
171
     *
172
     * @return Traversable
173
     */
174
    public function getIterator() : Traversable
175
    {
176
        return $this->traversable;
177
    }
178
}
179