Passed
Pull Request — master (#18)
by
unknown
06:05
created

conversions.php ➔ mixed_to_value_getter()   C

Complexity

Conditions 11
Paths 2

Size

Total Lines 44
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11

Importance

Changes 0
Metric Value
cc 11
eloc 25
nc 2
nop 1
dl 0
loc 44
ccs 24
cts 24
cp 1
crap 11
rs 5.2653
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
 * @author Boudewijn Schoon <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
7
namespace Zicht\Itertools\conversions;
8
9
use Doctrine\Common\Collections\Collection;
10
use Zicht\Itertools\lib\StringIterator;
11
12
/**
13
 * Transforms anything into an Iterator or throws an InvalidArgumentException
14
 *
15
 * > mixedToIterator([1, 2, 3])
16
 * 1 2 3
17
 *
18
 * > mixedToIterator('foo')
19
 * f o o
20
 *
21
 * @param array|string|\Iterator $iterable
22
 * @return \Iterator
23
 */
24
function mixed_to_iterator($iterable)
25
{
26
    // NULL is often used to indicate that nothing is there,
27
    // for robustness we will deal with NULL as it is an empty array
28 356
    if (is_null($iterable)) {
29 5
        $iterable = new \ArrayIterator([]);
30
    }
31
32
    // an array is *not* an instance of Traversable (as it is not an
33
    // object and hence can not 'implement Traversable')
34 356
    if (is_array($iterable)) {
35 280
        $iterable = new \ArrayIterator($iterable);
36
    }
37
38
    // a string is considered iterable in Python
39 356
    if (is_string($iterable)) {
40 6
        $iterable = new StringIterator($iterable);
41
    }
42
43
    // a doctrine Collection (i.e. Array or Persistent) is also an iterator
44 356
    if ($iterable instanceof Collection) {
45 3
        $iterable = $iterable->getIterator();
46
    }
47
48 356
    if ($iterable instanceof \Traversable and !($iterable instanceof \Iterator)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
49 1
        $iterable = new \IteratorIterator($iterable);
50
    }
51
52
    // by now it should be an Iterator, otherwise throw an exception
53 356
    if (!($iterable instanceof \Iterator)) {
54 50
        throw new \InvalidArgumentException('Argument $ITERABLE must be a Traversable');
55
    }
56
57 306
    return $iterable;
58
}
59
60
/**
61
 * Try to transforms something into a Closure.
62
 *
63
 * When $CLOSURE is null the returned Closure behaves like an identity function,
64
 * i.e. it will return the value that it is given.
65
 *
66
 * @param null|array|\Closure $closure
67
 * @return \Closure
68
 */
69
function mixed_to_closure($closure)
70
{
71 231
    if (is_null($closure)) {
72
        return function ($value) {
73 60
            return $value;
74 87
        };
75
    }
76
77 149
    if (!($closure instanceof \Closure)) {
78
79
        // A \Closure is always callable, but a callable is not always a \Closure.
80
        // Checking within this if statement is a slight optimization, preventing an unnecessary function wrap
81 16
        if (is_callable($closure)) {
82
            $closure = function () use ($closure) {
83 2
                return call_user_func_array($closure, func_get_args());
84 2
            };
85
        } else {
86 14
            throw new \InvalidArgumentException('Argument $CLOSURE must be a Closure');
87
        }
88
    }
89
90 135
    return $closure;
91
}
92
93
/**
94
 * Try to transforms something into a Closure that gets a value from $STRATEGY.
95
 *
96
 * When $STRATEGY is null the returned Closure behaves like an identity function,
97
 * i.e. it will return the value that it is given.
98
 *
99
 * When $STRATEGY is a string the returned Closure tries to find a properties,
100
 * methods, or array indexes named by the string.  Multiple property, method,
101
 * or index names can be separated by a dot.
102
 * - 'getId'
103
 * - 'getData.key'
104
 *
105
 * When $STRATEGY is callable it is converted into a Closure (see mixedToClosure).
106
 *
107
 * @param null|string|\Closure $strategy
108
 * @return \Closure
109
 */
110
function mixed_to_value_getter($strategy)
111
{
112 223
    if (is_string($strategy)) {
113 45
        $keyParts = explode('.', $strategy);
114
        $strategy = function ($value) use ($keyParts) {
115 45
            foreach ($keyParts as $keyPart) {
116 45
                if (is_object($value)) {
117
                    // property_exists does not distinguish between public, protected, or private properties, hence we need to use reflection
118 25
                    $reflection = new \ReflectionObject($value);
119 25
                    if ($reflection->hasProperty($keyPart)) {
120 13
                        $property = $reflection->getProperty($keyPart);
121 13
                        if ($property->isPublic()) {
122 10
                            $value = $property->getValue($value);
123 10
                            continue;
124
                        }
125
                    }
126
                }
127
128 36
                if (is_callable([$value, $keyPart])) {
129 9
                    $value = call_user_func([$value, $keyPart]);
130 9
                    continue;
131
                }
132
133 28
                if (is_array($value) && array_key_exists($keyPart, $value)) {
134 19
                    $value = $value[$keyPart];
135 19
                    continue;
136
                }
137
138 14
                if (is_object($value) && method_exists($value, '__get')) {
139 4
                    $value = $value->$keyPart;
140 4
                    continue;
141
                }
142
143
                // no match found
144 11
                $value = null;
145 11
                break;
146
            }
147
148 45
            return $value;
149 45
        };
150
    }
151
152 223
    return mixed_to_closure($strategy);
153
}
154
155
/**
156
 * Transforms anything into an Iterator or throws an InvalidArgumentException
157
 *
158
 * @param array|string|\Iterator $iterable
159
 * @return \Iterator
160
 * @deprecated Use mixed_to_iterator() instead, will be removed in version 3.0
161
 */
162
function mixedToIterator($iterable)
163
{
164
    return mixed_to_iterator($iterable);
165
}
166
167
168
/**
169
 * Try to transforms something into a Closure.
170
 *
171
 * @param null|\Closure $closure
172
 * @return \Closure
173
 * @deprecated Use mixed_to_closure() instead, will be removed in version 3.0
174
 */
175
function mixedToClosure($closure)
176
{
177
    return mixed_to_closure($closure);
178
}
179
180
181
/**
182
 * Try to transforms something into a Closure that gets a value from $STRATEGY.
183
 *
184
 * @param null|string|\Closure $strategy
185
 * @return \Closure
186
 * @deprecated Use mixed_to_closure() instead, will be removed in version 3.0
187
 */
188
function mixedToValueGetter($strategy)
189
{
190
    return mixed_to_value_getter($strategy);
191
}
192