Completed
Pull Request — master (#11)
by Arnold
13:39
created

functions.php ➔ _is_operator()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 1
dl 0
loc 5
ccs 0
cts 0
cp 0
crap 20
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Cypress\Curry;
4
5
use Cypress\Curry\Placeholder;
6
7
/**
8
 * @param callable|string $callable
9
 * @return callable
10
 */
11
function curry($callable)
12
{
13 9
    if (_is_operator($callable))
14 9
        $callable = _operator_function($callable);
15 7
16
    if (_number_of_required_params($callable) === 0) {
17
        return _make_function($callable);
18
    }
19
    if (_number_of_required_params($callable) === 1) {
20
        return _curry_array_args($callable, _rest(func_get_args()));
21
    }
22
23
    return _curry_array_args($callable, _rest(func_get_args()));
24
}
25
26 2
/**
27
 * @param $callable
28
 * @param array $args pass the arguments to be curried as an array
29
 *
30
 * @return callable
31
 */
32
function curry_args($callable, array $args)
33
{
34
    if (_is_operator($callable))
35 4
        $callable = _operator_function($callable);
36 4
37 4
    return _curry_array_args($callable, $args);
38
}
39
40
/**
41
 * @param callable $callable
42
 * @return callable
43
 */
44
function curry_right($callable)
45
{
46
    if (_is_operator($callable))
47
        $callable = _operator_function($callable);
48 3
49
    if (_number_of_required_params($callable) < 2)
50
        return _make_function($callable);
51
    return _curry_array_args($callable, _rest(func_get_args()), false);
52
}
53
54
/**
55
 * @param callable $callable
56
 * @param array $args pass the arguments to be curried as an array
57
 *
58
 * @return callable
59
 */
60 15
function curry_right_args($callable, array $args)
61 4
{
62
    if (_is_operator($callable))
63 11
        $callable = _operator_function($callable);
64 11
65 11
    return _curry_array_args($callable, $args, false);
66
}
67 6
68 15
/**
69
 * @param callable $callable
70
 * @param $args
71
 * @param bool $left
72
 * @return callable
73
 */
74
function _curry_array_args($callable, $args, $left = true)
75
{
76
    return function () use ($callable, $args, $left) {
77
        if (_is_fullfilled($callable, $args)) {
78
            return _execute($callable, $args, $left);
79
        }
80 15
        $newArgs = array_merge($args, func_get_args());
81 7
        if (_is_fullfilled($callable, $newArgs)) {
82 7
            return _execute($callable, $newArgs, $left);
83
        }
84 15
        return _curry_array_args($callable, $newArgs, $left);
85 15
    };
86 1
}
87 1
88
/**
89
 * @internal
90
 * @param $callable
91
 * @param $args
92
 * @param $left
93 1
 * @return mixed
94 1
 */
95 1
function _execute($callable, $args, $left)
96 1
{
97 1
    if (! $left) {
98
        $args = array_reverse($args);
99 15
    }
100
101
    $placeholders = _placeholder_positions($args);
102
    if (0 < count($placeholders)) {
103
        $n = _number_of_required_params($callable);
104
        if ($n <= _last($placeholders[count($placeholders) - 1])) {
105
            // This means that we have more placeholders then needed
106
            // I know that throwing exceptions is not really the 
107
            // functional way, but this case should not happen.
108
            throw new \Exception("Argument Placeholder found on unexpected position !");
109 11
        }
110
        foreach ($placeholders as $i) {
111
            $args[$i] = $args[$n];
112
            array_splice($args, $n, 1);
113
        }
114
    }
115
116
    return call_user_func_array($callable, $args);
117
}
118
119
/**
120
 * @internal
121 20
 * @param array $args
122 22
 * @return array
123 22
 */
124
function _rest(array $args)
125
{
126
    return array_slice($args, 1);
127
}
128
129
/**
130
 * @internal
131
 * @param callable $callable
132
 * @param $args
133 24
 * @return bool
134 16
 */
135 16
function _is_fullfilled($callable, $args)
136 16
{
137
    $args = array_filter($args, function($arg) {
138 8
        return ! _is_placeholder($arg);
139 8
    });
140
    return count($args) >= _number_of_required_params($callable);
141
}
142
143
/**
144
 * @internal
145
 * @param $callable
146
 * @return int
147
 */
148
function _number_of_required_params($callable)
149
{
150
    if (is_array($callable)) {
151
        $refl = new \ReflectionClass($callable[0]);
152 3
        $method = $refl->getMethod($callable[1]);
153
        return $method->getNumberOfRequiredParameters();
154 1
    }
155 1
    $refl = new \ReflectionFunction($callable);
156 2
    return $refl->getNumberOfRequiredParameters();
157
}
158
159
/**
160
 * if the callback is an array(instance, method), 
161
 * it returns an equivalent function for PHP 5.3 compatibility.
162
 *
163
 * @internal 
164
 * @param  callable $callable
165
 * @return callable
166
 */
167
function _make_function($callable)
168 20
{
169
    if (is_array($callable))
170
        return function() use($callable) {
171
            return call_user_func_array($callable, func_get_args());
172
        };
173
174
    return $callable;
175
}
176
177
/**
178
 * Checks if the callable is an operator.
179
 *
180 15
 * @internal
181
 * @param $callable
182
 * @return boolean
183
 */
184
function _is_operator($callable)
185
{
186
    return is_string($callable) && strlen($callable) <= 3 &&
187
        (ctype_punct($callable) || in_array($callable, ['and', 'or', 'xor', 'not']));
188
}
189
190
/**
191
 * @internal
192 1
 * @param string $operator
193
 * @return callable
194
 */
195
function _operator_function($operator)
196
{
197
    $callable = __NAMESPACE__ . '\\_operator_' .
198
        (ctype_punct($operator) ? sha1($operator) : $operator);
199
200
    if (!function_exists($callable)) {
201
        throw new \Exception("Operator '$operator' can't be used as curry function");
202
    }
203
204
    return $callable;
205 1
}
206
207
/**
208
 * Checks if an argument is a placeholder.
209
 *
210
 * @internal
211
 * @param  mixed  $arg
212
 * @return boolean
213
 */
214
function _is_placeholder($arg)
215
{
216
    return $arg instanceof Placeholder;
217
}
218
219
/**
220
 * Gets an array of placeholders positions in the given arguments.
221
 *
222
 * @internal
223
 * @param  array $args
224
 * @return array
225
 */
226
function _placeholder_positions($args)
227
{
228
    return array_keys(array_filter($args, 'Cypress\\Curry\\_is_placeholder'));
229
}
230
231
/**
232
 * Get the last element in an array.
233
 *
234
 * @internal
235
 * @param  array $array
236
 * @return mixed
237
 */
238
function _last($array)
239
{
240
    return $array[count($array) - 1];
241
}
242
243
/**
244
 * Gets a special placeholder value used to specify "gaps" within curried 
245
 * functions, allowing partial application of any combination of arguments, 
246
 * regardless of their positions. Should be used only for required arguments.
247
 * When used, optional arguments must be at the end of the arguments list.
248
 *
249
 * @return Cypress\Curry\Placeholder
250
 */
251
function __()
252
{
253
    return Placeholder::get();
254
}
255