Passed
Pull Request — master (#190)
by Sebastian
03:03
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, function ($list, $item) use ($count, &$index) {
41
          $list .= ($index > 0 && $index < ($count - 1) ? ', ' : '') . ($index === ($count - 1) ? ' or ' : '') .
42
            $item;
43
          $index++;
44
          return $list;
45
      }, '');
46
}
47
48
/**
49
 * Given an invalid input string and a list of valid options, returns a filtered
50
 * list of valid options sorted based on their similarity with the input.
51
 *
52
 * @param string $input
53
 * @param array $options
54
 *
55
 * @return array
56
 */
57
function suggestionList(string $input, array $options): array
58
{
59
    $optionsByDistance = [];
60
    $oLength = \count($options);
61
    $inputThreshold = \strlen($input) / 2;
62
63
    /** @noinspection ForeachInvariantsInspection */
64
    for ($i = 0; $i < $oLength; $i++) {
65
        // Comparison must be case-insenstive.
66
        $distance = \levenshtein(\strtolower($input),
67
          \strtolower($options[$i]));
68
        $threshold = \max($inputThreshold, \strlen($options[$i]) / 2, 1);
69
        if ($distance <= $threshold) {
70
            $optionsByDistance[$options[$i]] = $distance;
71
        }
72
    }
73
74
    $result = \array_keys($optionsByDistance);
75
76
    \usort($result, function ($a, $b) use ($optionsByDistance) {
77
        return $optionsByDistance[$a] - $optionsByDistance[$b];
78
    });
79
80
    return $result;
81
}
82
83
/**
84
 * Given `[A, B, C]` returns `'"A", "B" or "C"'`.
85
 *
86
 * @param array $items
87
 *
88
 * @return string
89
 */
90
function quotedOrList(array $items): string
91
{
92
    return orList(array_map(function ($item) {
93
        return '"' . $item . '"';
94
    }, $items));
95
}
96
97
98
/**
99
 * @param array $array
100
 * @param callable $fn
101
 *
102
 * @return bool
103
 */
104
function arrayEvery(array $array, callable $fn): bool
105
{
106
    return array_reduce($array, function ($result, $value) use ($fn) {
107
        return $result && $fn($value);
108
    }, true);
109
}
110
111
/**
112
 * @param array $array
113
 * @param callable $fn
114
 *
115
 * @return mixed
116
 */
117
function arraySome(array $array, callable $fn)
118
{
119
    return array_reduce($array, function ($result, $value) use ($fn) {
120
        return $result || $fn($value);
121
    });
122
}
123
124
/**
125
 * @param array $array
126
 * @param callable $predicate
127
 *
128
 * @return mixed|null
129
 */
130
function find(array $array, callable $predicate)
131
{
132
    foreach ($array as $value) {
133
        if ($predicate($value)) {
134
            return $value;
135
        }
136
    }
137
138
    return null;
139
}
140
141
/**
142
 * @param array $array
143
 * @param callable $keyFn
144
 *
145
 * @return array
146
 */
147
function keyMap(array $array, callable $keyFn): array
148
{
149
    return array_reduce($array, function ($map, $item) use ($keyFn) {
150
        $map[$keyFn($item)] = $item;
151
        return $map;
152
    }, []);
153
}
154
155
/**
156
 * @param array $array
157
 * @param callable $keyFn
158
 * @param callable $valFn
159
 *
160
 * @return array
161
 */
162
function keyValueMap(array $array, callable $keyFn, callable $valFn): array
163
{
164
    return array_reduce($array, function ($map, $item) use ($keyFn, $valFn) {
165
        $map[$keyFn($item)] = $valFn($item);
166
        return $map;
167
    }, []);
168
}
169
170
/**
171
 * @param $value
172
 *
173
 * @return string
174
 */
175
function toString($value): string
176
{
177
    if ($value instanceof TypeInterface) {
178
        return (string)$value;
179
    }
180
    if ($value instanceof NodeInterface) {
181
        return printNode($value);
182
    }
183
    if (\is_object($value)) {
184
        return 'Object';
185
    }
186
    if (\is_array($value)) {
187
        return 'Array';
188
    }
189
    if (\is_callable($value)) {
190
        return 'Function';
191
    }
192
    if ($value === '') {
193
        return '(empty string)';
194
    }
195
    if ($value === null) {
196
        return '(null)';
197
    }
198
    if ($value === true) {
199
        return '(true)';
200
    }
201
    if ($value === false) {
202
        return '(false)';
203
    }
204
    if (\is_string($value)) {
205
        return "\"{$value}\"";
206
    }
207
    if (\is_scalar($value)) {
208
        return (string)$value;
209
    }
210
    return \gettype($value);
211
}
212