Passed
Pull Request — master (#190)
by Sebastian
03:29
created

suggestionList()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 13
nc 3
nop 2
1
<?php
2
3
namespace Digia\GraphQL\Util;
4
5
use Digia\GraphQL\Error\InvariantException;
6
use Digia\GraphQL\Language\Node\NodeInterface;
7
use Digia\GraphQL\Type\Definition\TypeInterface;
8
use function Digia\GraphQL\printNode;
9
10
/**
11
 * @param bool $condition
12
 * @param string $message
13
 *
14
 * @throws InvariantException
15
 */
16
function invariant(bool $condition, string $message)
17
{
18
    if (!$condition) {
19
        throw new InvariantException($message);
20
    }
21
}
22
23
/**
24
 * Given `[A, B, C]` returns `'A, B or C'`.
25
 *
26
 * @param array $items
27
 *
28
 * @return string
29
 */
30
function orList(array $items): string
31
{
32
    static $MAX_LENGTH = 5;
33
34
    $selected = \array_slice($items, 0, $MAX_LENGTH);
35
    $count = \count($selected);
36
    $index = 0;
37
38
    return $count === 1
39
        ? $selected[0]
40
        : \array_reduce($selected,
41
            function ($list, $item) use ($count, &$index) {
42
                $list .= ($index > 0 && $index < ($count - 1) ? ', ' : '').($index === ($count - 1) ? ' or ' : '').
43
                    $item;
44
                $index++;
45
46
                return $list;
47
            }, '');
48
}
49
50
/**
51
 * Given an invalid input string and a list of valid options, returns a filtered
52
 * list of valid options sorted based on their similarity with the input.
53
 *
54
 * @param string $input
55
 * @param array $options
56
 *
57
 * @return array
58
 */
59
function suggestionList(string $input, array $options): array
60
{
61
    $optionsByDistance = [];
62
    $oLength = \count($options);
63
    $inputThreshold = \strlen($input) / 2;
64
65
    /** @noinspection ForeachInvariantsInspection */
66
    for ($i = 0; $i < $oLength; $i++) {
67
        // Comparison must be case-insenstive.
68
        $distance = \levenshtein(\strtolower($input),
69
            \strtolower($options[$i]));
70
        $threshold = \max($inputThreshold, \strlen($options[$i]) / 2, 1);
71
        if ($distance <= $threshold) {
72
            $optionsByDistance[$options[$i]] = $distance;
73
        }
74
    }
75
76
    $result = \array_keys($optionsByDistance);
77
78
    \usort($result, function ($a, $b) use ($optionsByDistance) {
79
        return $optionsByDistance[$a] - $optionsByDistance[$b];
80
    });
81
82
    return $result;
83
}
84
85
/**
86
 * Given `[A, B, C]` returns `'"A", "B" or "C"'`.
87
 *
88
 * @param array $items
89
 *
90
 * @return string
91
 */
92
function quotedOrList(array $items): string
93
{
94
    return orList(array_map(function ($item) {
95
        return '"'.$item.'"';
96
    }, $items));
97
}
98
99
100
/**
101
 * @param array $array
102
 * @param callable $fn
103
 *
104
 * @return bool
105
 */
106
function arrayEvery(array $array, callable $fn): bool
107
{
108
    return array_reduce($array, function ($result, $value) use ($fn) {
109
        return $result && $fn($value);
110
    }, true);
111
}
112
113
/**
114
 * @param array $array
115
 * @param callable $fn
116
 *
117
 * @return mixed
118
 */
119
function arraySome(array $array, callable $fn)
120
{
121
    return array_reduce($array, function ($result, $value) use ($fn) {
122
        return $result || $fn($value);
123
    });
124
}
125
126
/**
127
 * @param array $array
128
 * @param callable $predicate
129
 *
130
 * @return mixed|null
131
 */
132
function find(array $array, callable $predicate)
133
{
134
    foreach ($array as $value) {
135
        if ($predicate($value)) {
136
            return $value;
137
        }
138
    }
139
140
    return null;
141
}
142
143
/**
144
 * @param array $array
145
 * @param callable $keyFn
146
 *
147
 * @return array
148
 */
149
function keyMap(array $array, callable $keyFn): array
150
{
151
    return array_reduce($array, function ($map, $item) use ($keyFn) {
152
        $map[$keyFn($item)] = $item;
153
154
        return $map;
155
    }, []);
156
}
157
158
/**
159
 * @param array $array
160
 * @param callable $keyFn
161
 * @param callable $valFn
162
 *
163
 * @return array
164
 */
165
function keyValueMap(array $array, callable $keyFn, callable $valFn): array
166
{
167
    return array_reduce($array, function ($map, $item) use ($keyFn, $valFn) {
168
        $map[$keyFn($item)] = $valFn($item);
169
170
        return $map;
171
    }, []);
172
}
173
174
/**
175
 * @param $value
176
 *
177
 * @return string
178
 */
179
function toString($value): string
180
{
181
    if ($value instanceof TypeInterface) {
182
        return (string)$value;
183
    }
184
    if ($value instanceof NodeInterface) {
185
        return printNode($value);
186
    }
187
    if (\is_object($value)) {
188
        return 'Object';
189
    }
190
    if (\is_array($value)) {
191
        return 'Array';
192
    }
193
    if (\is_callable($value)) {
194
        return 'Function';
195
    }
196
    if ($value === '') {
197
        return '(empty string)';
198
    }
199
    if ($value === null) {
200
        return '(null)';
201
    }
202
    if ($value === true) {
203
        return '(true)';
204
    }
205
    if ($value === false) {
206
        return '(false)';
207
    }
208
    if (\is_string($value)) {
209
        return "\"{$value}\"";
210
    }
211
    if (\is_scalar($value)) {
212
        return (string)$value;
213
    }
214
215
    return \gettype($value);
216
}
217