Passed
Pull Request — master (#57)
by
unknown
02:46
created

filters.php ➔ after()   C

Complexity

Conditions 12
Paths 3

Size

Total Lines 33

Duplication

Lines 20
Ratio 60.61 %

Code Coverage

Tests 14
CRAP Score 12

Importance

Changes 0
Metric Value
cc 12
nc 3
nop 3
dl 20
loc 33
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 21
    $strategy = conversions\mixed_to_value_getter($strategy);
151
152
    // Support DateTimeInterface
153 21 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 9
            $value = $strategy($value, $key);
156
            // Try to convert strings that are longer than 3 characters.  This simple test will ensure
157
            // that '' or 'now' will not be accepted.
158 9
            if (is_string($value) && strlen($value) > 3) {
159
                try {
160 2
                    $value = new \DateTimeImmutable($value);
161 1
                } catch (\Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
162
                }
163
            }
164 9
            return $value instanceof \DateTimeInterface && ($orEqual ? $expected <= $value : $expected < $value);
165 9
        };
166
    }
167
168
    // Support numbers
169 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...
170
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
171 11
            $value = $strategy($value, $key);
172 11
            return (is_int($value) || is_float($value)) && ($orEqual ? $expected <= $value : $expected < $value);
173 11
        };
174
    }
175
176
    // Everything else fails
177
    return function () {
178 1
        return false;
179 1
    };
180
}
181
182
/**
183
 * Returns a filter closure that only accepts values that are before $EXPECTED
184
 *
185
 * For example, the following will return a list where only
186
 * returns entries that are before 2020-04-01:
187
 * > $list = iterable([new \DateTime('2020-01-01'), new \DateTime('2020-03-01'), new \DateTime('2020-05-01')])
188
 * > $result = $list->filer(before(new \DateTimeImmutable('2020-04-01')));
189
 * > // [new \DateTime('2020-01-01'), new \DateTime('2020-03-01')]
190
 *
191
 * @param \DateTimeInterface|int|float $expected
192
 * @param null|string|\Closure $strategy
193
 * @param bool $orEqual
194
 * @return \Closure
195
 */
196
function before($expected, $strategy = null, $orEqual = false)
197
{
198 21
    $strategy = conversions\mixed_to_value_getter($strategy);
199
200
    // Support DateTimeInterface
201 21 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...
202
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
203 9
            $value = $strategy($value, $key);
204
            // Try to convert strings that are longer than 3 characters.  This simple test will ensure
205
            // that '' or 'now' will not be accepted.
206 9
            if (is_string($value) && strlen($value) > 3) {
207
                try {
208 2
                    $value = new \DateTimeImmutable($value);
209 1
                } catch (\Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
210
                }
211
            }
212 9
            return $value instanceof \DateTimeInterface && ($orEqual ? $expected >= $value : $expected > $value);
213 9
        };
214
    }
215
216
    // Support numbers
217 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...
218
        return function ($value, $key = null) use ($expected, $strategy, $orEqual) {
219 11
            $value = $strategy($value, $key);
220 11
            return (is_int($value) || is_float($value)) && ($orEqual ? $expected >= $value : $expected > $value);
221 11
        };
222
    }
223
224
    // Everything else fails
225
    return function () {
226 1
        return false;
227 1
    };
228
}
229
230
/**
231
 * Returns a filter closure that inverts the value
232
 *
233
 * For example, the following will return a list where none
234
 * of the items equal 'bar'
235
 * > $list = iterable(['foo', 'bar']);
236
 * > $result = $list->filter(not(equals('bar')));
237
 * > // {0: 'foo'}
238
 *
239
 * @param null|string|\Closure $strategy
240
 * @return \Closure
241
 */
242
function not($strategy = null)
243
{
244 2
    $strategy = conversions\mixed_to_value_getter($strategy);
245
    return function ($value, $key = null) use ($strategy) {
246 2
        return !($strategy($value, $key));
247 2
    };
248
}
249
250
/**
251
 * Returns a filter closure that only accepts values that match the given regular expression.
252
 *
253
 * For example, the following will return a list where all items
254
 * match 'bar':
255
 * > $list = iterable(['-= foo =-', '-= bar =-']);
256
 * > $result = $list->filter(match('/bar/i'));
257
 * > // {1: '-= bar =-'}
258
 *
259
 * @param string $pattern
260
 * @param null|string|\Closure $strategy
261
 * @return \Closure
262
 */
263
function match($pattern, $strategy = null)
264
{
265 7
    if (!is_string($pattern)) {
266 4
        throw new \InvalidArgumentException('$PATTERN must be a string');
267
    }
268
269 3
    $strategy = conversions\mixed_to_value_getter($strategy);
270
    return function ($value, $key = null) use ($pattern, $strategy) {
271 3
        return (bool)preg_match($pattern, $strategy($value, $key));
272 3
    };
273
}
274