Passed
Push — master ( 2e2f60...f000ad )
by
unknown
01:21 queued 12s
created

filters.php ➔ after()   C

Complexity

Conditions 12
Paths 3

Size

Total Lines 32

Duplication

Lines 19
Ratio 59.38 %

Code Coverage

Tests 14
CRAP Score 12

Importance

Changes 0
Metric Value
cc 12
nc 3
nop 3
dl 19
loc 32
ccs 14
cts 14
cp 1
crap 12
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Zicht Online <http://zicht.nl>
4
 */
5
6
namespace Zicht\Itertools\filters;
7
8
use Zicht\Itertools\conversions;
9
use Zicht\Itertools;
10
11
/**
12
 * Returns a filter closure that only accepts values that are instances of $CLASS.
13
 *
14
 * For example, the following will return a list where all items
15
 * are instances of class Bar:
16
 * > $list = iterable([new Foo(), new Bar(), new Moo()]);
17
 * > $result = $list->filter(type(Bar::class));
18
 * > // {1: Bar}
19
 *
20
 * For example, the following will return a list where all items
21
 * have a property or array index 'key' that is an instance
22
 * of class Foo:
23
 * > $list = iterable([['key' => new Foo()], ['key' => new Bar()], ['key' => new Moo()]]);
24
 * > $result = $list->filter(type(Bar::class, 'key'));
25
 * > // {1: ['key' => Bar]}
26
 *
27
 * @param string $class
28
 * @param null|string|\Closure $strategy
29
 * @return \Closure
30
 */
31
function type($class, $strategy = null)
32
{
33 4
    $strategy = conversions\mixed_to_value_getter($strategy);
34
    return function ($value, $key = null) use ($class, $strategy) {
35 3
        return $strategy($value, $key) instanceof $class;
36 4
    };
37
}
38
39
/**
40
 * Returns a filter closure that only accepts values that are in $HAYSTACK.
41
 *
42
 * For example, the following will return a list where all items
43
 * are either 'b' or 'c':
44
 * > $list = iterable(['a', 'b', 'c', 'd', 'e']);
45
 * > $result = $list->filter(in(['b', 'c']));
46
 * > // {1: 'b', 2: 'c'}
47
 *
48
 * For example, the following will return a list where all items
49
 * have a property or array index 'key' that is either 'b' or 'c':
50
 * > $list = iterable([['key' => 'a'], ['key' => 'b'], ['key' => 'c'], ['key' => 'd'], ['key' => 'e']]);
51
 * > $result = $list->filter(in(['b', 'c'], 'key'));
52
 * > // {1: ['key' => 'b'], 2: ['key' => 'c']}
53
 *
54
 * @param null|array|string|\Iterator $haystack
55
 * @param null|string|\Closure $strategy
56
 * @param boolean $strict
57
 * @return \Closure
58
 */
59 View Code Duplication
function in($haystack, $strategy = null, $strict = false)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
{
61 14
    if (!is_bool($strict)) {
62 5
        throw new \InvalidArgumentException('$STRICT must be a boolean');
63
    }
64 9
    if (!is_array($haystack)) {
65 5
        $haystack = Itertools\iterable($haystack)->values();
0 ignored issues
show
Bug introduced by
It seems like $haystack can also be of type null; however, Zicht\Itertools\iterable() does only seem to accept array|string|object<Iterator>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
66
    }
67 6
    $strategy = conversions\mixed_to_value_getter($strategy);
68
    return function ($value, $key = null) use ($haystack, $strategy, $strict) {
69 6
        return in_array($strategy($value, $key), $haystack, $strict);
70 6
    };
71
}
72
73
/**
74
 * Returns a filter closure that only accepts values that are not in $HAYSTACK.
75
 *
76
 * @param array|string|\Iterator $haystack
77
 * @param null|string|\Closure $strategy
78
 * @param boolean $strict
79
 * @return \Closure
80
 *
81
 * @deprecated Instead use not(in(...))
82
 */
83 View Code Duplication
function not_in($haystack, $strategy = null, $strict = false)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
84
{
85
    if (!is_bool($strict)) {
86
        throw new \InvalidArgumentException('$STRICT must be a boolean');
87
    }
88
    if (!is_array($haystack)) {
89
        $haystack = Itertools\iterable($haystack)->values();
90
    }
91
    $strategy = conversions\mixed_to_value_getter($strategy);
92
    return function ($value, $key = null) use ($haystack, $strategy, $strict) {
93
        return !in_array($strategy($value, $key), $haystack, $strict);
94
    };
95
}
96
97
/**
98
 * Returns a filter closure that only accepts values that are equal to $EXPECTED
99
 *
100
 * For example, the following will return a list where all items
101
 * equal 'bar':
102
 * > $list = iterable(['foo', 'bar']);
103
 * > $result = $list->filter(equals('bar'));
104
 * > // {1: 'bar'}
105
 *
106
 * For example, the following will return a list where all items
107
 * have a property or array index 'foo' that equals 'bar':
108
 * > $list = iterable([['key' => 'foo'], ['key' => 'bar']]);
109
 * > $result = $list->filter(equals('bar', 'key'));
110
 * > // {1: ['key' => 'bar']}
111
 *
112
 * @param mixed $expected
113
 * @param null|string|\Closure $strategy
114
 * @param boolean $strict
115
 * @return \Closure
116
 */
117
function equals($expected, $strategy = null, $strict = false)
118
{
119 11
    if (!is_bool($strict)) {
120 5
        throw new \InvalidArgumentException('$STRICT must be a boolean');
121
    }
122 6
    $strategy = conversions\mixed_to_value_getter($strategy);
123 6
    if ($strict) {
124
        return function ($value, $key = null) use ($expected, $strategy) {
125 1
            return $expected === $strategy($value, $key);
126 1
        };
127
    } else {
128
        return function ($value, $key = null) use ($expected, $strategy) {
129 6
            return $expected == $strategy($value, $key);
130 6
        };
131
    }
132
}
133
134
/**
135
 * Returns a filter closure that only accepts values that are after $EXPECTED
136
 *
137
 * For example, the following will return a list where only
138
 * returns entries that are after 2020-04-01:
139
 * > $list = iterable([new \DateTime('2020-01-01'), new \DateTime('2020-03-01'), new \DateTime('2020-05-01')])
140
 * > $result = $list->filer(after(new \DateTimeImmutable('2020-04-01')));
141
 * > // [new \DateTime('2020-05-01')]
142
 *
143
 * @param \DateTimeInterface|int|float $expected
144
 * @param null|string|\Closure $strategy
145
 * @param bool $orEqual
146
 * @return \Closure
147
 */
148
function after($expected, $strategy = null, $orEqual = false)
149
{
150 26
    $strategy = conversions\mixed_to_value_getter($strategy);
151
152
    // Support DateTimeInterface
153 26 View Code Duplication
    if ($expected instanceof \DateTimeInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
154
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
155 14
            $value = $strategy($value, $key);
156
            // Try to convert strings that look like ISO date format
157 14
            if (is_string($value) && preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}/', $value)) {
158
                try {
159 4
                    $value = new \DateTimeImmutable($value);
160 2
                } catch (\Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
161
                }
162
            }
163 14
            return $value instanceof \DateTimeInterface && ($orEqual ? $expected <= $value : $expected < $value);
164 14
        };
165
    }
166
167
    // Support numbers
168 12 View Code Duplication
    if (is_int($expected) || is_float($expected)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
169
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
170 11
            $value = $strategy($value, $key);
171 11
            return (is_int($value) || is_float($value)) && ($orEqual ? $expected <= $value : $expected < $value);
172 11
        };
173
    }
174
175
    // Everything else fails
176
    return function () {
177 1
        return false;
178 1
    };
179
}
180
181
/**
182
 * Returns a filter closure that only accepts values that are before $EXPECTED
183
 *
184
 * For example, the following will return a list where only
185
 * returns entries that are before 2020-04-01:
186
 * > $list = iterable([new \DateTime('2020-01-01'), new \DateTime('2020-03-01'), new \DateTime('2020-05-01')])
187
 * > $result = $list->filer(before(new \DateTimeImmutable('2020-04-01')));
188
 * > // [new \DateTime('2020-01-01'), new \DateTime('2020-03-01')]
189
 *
190
 * @param \DateTimeInterface|int|float $expected
191
 * @param null|string|\Closure $strategy
192
 * @param bool $orEqual
193
 * @return \Closure
194
 */
195
function before($expected, $strategy = null, $orEqual = false)
196
{
197 26
    $strategy = conversions\mixed_to_value_getter($strategy);
198
199
    // Support DateTimeInterface
200 26 View Code Duplication
    if ($expected instanceof \DateTimeInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
202 14
            $value = $strategy($value, $key);
203
            // Try to convert strings that look like ISO date format
204 14
            if (is_string($value) && preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}/', $value)) {
205
                try {
206 4
                    $value = new \DateTimeImmutable($value);
207 2
                } catch (\Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
208
                }
209
            }
210 14
            return $value instanceof \DateTimeInterface && ($orEqual ? $expected >= $value : $expected > $value);
211 14
        };
212
    }
213
214
    // Support numbers
215 12 View Code Duplication
    if (is_int($expected) || is_float($expected)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
217 11
            $value = $strategy($value, $key);
218 11
            return (is_int($value) || is_float($value)) && ($orEqual ? $expected >= $value : $expected > $value);
219 11
        };
220
    }
221
222
    // Everything else fails
223
    return function () {
224 1
        return false;
225 1
    };
226
}
227
228
/**
229
 * Returns a filter closure that inverts the value
230
 *
231
 * For example, the following will return a list where none
232
 * of the items equal 'bar'
233
 * > $list = iterable(['foo', 'bar']);
234
 * > $result = $list->filter(not(equals('bar')));
235
 * > // {0: 'foo'}
236
 *
237
 * @param null|string|\Closure $strategy
238
 * @return \Closure
239
 */
240
function not($strategy = null)
241
{
242 2
    $strategy = conversions\mixed_to_value_getter($strategy);
243
    return function ($value, $key = null) use ($strategy) {
244 2
        return !($strategy($value, $key));
245 2
    };
246
}
247
248
/**
249
 * Returns a filter closure that only accepts values that match the given regular expression.
250
 *
251
 * For example, the following will return a list where all items
252
 * match 'bar':
253
 * > $list = iterable(['-= foo =-', '-= bar =-']);
254
 * > $result = $list->filter(match('/bar/i'));
255
 * > // {1: '-= bar =-'}
256
 *
257
 * @param string $pattern
258
 * @param null|string|\Closure $strategy
259
 * @return \Closure
260
 */
261
function match($pattern, $strategy = null)
262
{
263 7
    if (!is_string($pattern)) {
264 4
        throw new \InvalidArgumentException('$PATTERN must be a string');
265
    }
266
267 3
    $strategy = conversions\mixed_to_value_getter($strategy);
268
    return function ($value, $key = null) use ($pattern, $strategy) {
269 3
        return (bool)preg_match($pattern, $strategy($value, $key));
270 3
    };
271
}
272