Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — master (#118)
by Jérémiah
05:21
created

connectionFromPromisedArraySlice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
crap 1
1
<?php
2
3
/*
4
 * This file is part of the OverblogGraphQLBundle package.
5
 *
6
 * (c) Overblog <http://github.com/overblog/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Overblog\GraphQLBundle\Relay\Connection\Output;
13
14
use Overblog\GraphQLBundle\Definition\Argument;
15
16
/**
17
 * Class ConnectionBuilder.
18
 *
19
 * @see https://github.com/graphql/graphql-relay-js/blob/master/src/connection/arrayconnection.js
20
 */
21
class ConnectionBuilder
22
{
23
    const PREFIX = 'arrayconnection:';
24
25
    /**
26
     * A simple function that accepts an array and connection arguments, and returns
27
     * a connection object for use in GraphQL. It uses array offsets as pagination,
28
     * so pagination will only work if the array is static.
29
     *
30
     * @param array          $data
31
     * @param array|Argument $args
32
     *
33
     * @return Connection
34
     */
35 37
    public static function connectionFromArray($data, $args = [])
36
    {
37 37
        return static::connectionFromArraySlice(
38 37
            $data,
39 37
            $args,
40
            [
41 37
                'sliceStart' => 0,
42 37
                'arrayLength' => count($data),
43
            ]
44 37
        );
45
    }
46
47
    /**
48
     * A version of `connectionFromArray` that takes a promised array, and returns a
49
     * promised connection.
50
     *
51
     * @param mixed          $dataPromise a promise
52
     * @param array|Argument $args
53
     *
54
     * @return mixed a promise
55
     */
56 2
    public static function connectionFromPromisedArray($dataPromise, $args = [])
57
    {
58 2
        self::checkPromise($dataPromise);
59
60
        return $dataPromise->then(function ($data) use ($args) {
61 2
            return static::connectionFromArray($data, $args);
62 2
        });
63
    }
64
65
    /**
66
     * Given a slice (subset) of an array, returns a connection object for use in
67
     * GraphQL.
68
     *
69
     * This function is similar to `connectionFromArray`, but is intended for use
70
     * cases where you know the cardinality of the connection, consider it too large
71
     * to materialize the entire array, and instead wish pass in a slice of the
72
     * total result large enough to cover the range specified in `args`.
73
     *
74
     * @param array          $arraySlice
75
     * @param array|Argument $args
76
     * @param array          $meta
77
     *
78
     * @return Connection
79
     */
80 54
    public static function connectionFromArraySlice($arraySlice, $args, array $meta)
81
    {
82 54
        $connectionArguments = self::getOptionsWithDefaults(
83 54
            $args instanceof Argument ? $args->getRawArguments() : $args,
84
            [
85 54
                'after' => '',
86 54
                'before' => '',
87 54
                'first' => null,
88 54
                'last' => null,
89
            ]
90 54
        );
91 54
        $arraySliceMetaInfo = self::getOptionsWithDefaults(
92 54
            $meta,
93
            [
94 54
                'sliceStart' => 0,
95 54
                'arrayLength' => 0,
96
            ]
97 54
        );
98
99 54
        $arraySliceLength = count($arraySlice);
100 54
        $after = $connectionArguments['after'];
101 54
        $before = $connectionArguments['before'];
102 54
        $first = $connectionArguments['first'];
103 54
        $last = $connectionArguments['last'];
104 54
        $sliceStart = $arraySliceMetaInfo['sliceStart'];
105 54
        $arrayLength = $arraySliceMetaInfo['arrayLength'];
106 54
        $sliceEnd = $sliceStart + $arraySliceLength;
107 54
        $beforeOffset = static::getOffsetWithDefault($before, $arrayLength);
108 54
        $afterOffset = static::getOffsetWithDefault($after, -1);
109
110 54
        $startOffset = max($sliceStart - 1, $afterOffset, -1) + 1;
111 54
        $endOffset = min($sliceEnd, $beforeOffset, $arrayLength);
112
113 54 View Code Duplication
        if (is_numeric($first)) {
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...
114 33
            if ($first < 0) {
115 1
                throw new \InvalidArgumentException('Argument "first" must be a non-negative integer');
116
            }
117 32
            $endOffset = min($endOffset, $startOffset + $first);
118 32
        }
119
120 53 View Code Duplication
        if (is_numeric($last)) {
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...
121 16
            if ($last < 0) {
122 1
                throw new \InvalidArgumentException('Argument "last" must be a non-negative integer');
123
            }
124
125 15
            $startOffset = max($startOffset, $endOffset - $last);
126 15
        }
127
128
        // If supplied slice is too large, trim it down before mapping over it.
129 52
        $offset = max($startOffset - $sliceStart, 0);
130 52
        $length = ($arraySliceLength - ($sliceEnd - $endOffset)) - $offset;
131
132 52
        $slice = array_slice(
133 52
            $arraySlice,
134 52
            $offset,
135
            $length
136 52
        );
137
138 52
        $edges = [];
139
140 52
        foreach ($slice as $index => $value) {
141 49
            $edges[] = new Edge(static::offsetToCursor($startOffset + $index), $value);
142 52
        }
143
144 52
        $firstEdge = isset($edges[0]) ? $edges[0] : null;
145 52
        $lastEdge = end($edges);
146 52
        $lowerBound = $after ? ($afterOffset + 1) : 0;
147 52
        $upperBound = $before ? $beforeOffset : $arrayLength;
148
149 52
        return new Connection(
150 52
            $edges,
151 52
            new PageInfo(
152 52
                $firstEdge instanceof Edge ? $firstEdge->cursor : null,
153 52
                $lastEdge instanceof Edge ? $lastEdge->cursor : null,
154 52
                $last !== null ? $startOffset > $lowerBound : false,
155 52
                $first !== null ? $endOffset < $upperBound : false
156 52
            )
157 52
        );
158
    }
159
160
    /**
161
     * A version of `connectionFromArraySlice` that takes a promised array slice,
162
     * and returns a promised connection.
163
     *
164
     * @param mixed          $dataPromise a promise
165
     * @param array|Argument $args
166
     * @param array          $meta
167
     *
168
     * @return mixed a promise
169
     */
170 1
    public static function connectionFromPromisedArraySlice($dataPromise, $args, array $meta)
171
    {
172 1
        self::checkPromise($dataPromise);
173
174 1
        return $dataPromise->then(function ($arraySlice) use ($args, $meta) {
175 1
            return static::connectionFromArraySlice($arraySlice, $args, $meta);
176 1
        });
177
    }
178
179
    /**
180
     * Return the cursor associated with an object in an array.
181
     *
182
     * @param array $data
183
     * @param mixed $object
184
     *
185
     * @return null|string
186
     */
187 2
    public static function cursorForObjectInConnection($data, $object)
188
    {
189 2
        $offset = null;
190
191 2
        foreach ($data as $i => $entry) {
192
            // When using the comparison operator (==), object variables are compared in a simple manner,
193
            // namely: Two object instances are equal if they have the same attributes and values,
194
            // and are instances of the same class.
195 2
            if ($entry == $object) {
196 1
                $offset = $i;
197 1
                break;
198
            }
199 2
        }
200
201 2
        if (null === $offset) {
202 1
            return;
203
        }
204
205 1
        return static::offsetToCursor($offset);
206
    }
207
208
    /**
209
     * Given an optional cursor and a default offset, returns the offset
210
     * to use; if the cursor contains a valid offset, that will be used,
211
     * otherwise it will be the default.
212
     *
213
     * @param string $cursor
214
     * @param int    $defaultOffset
215
     *
216
     * @return int
217
     */
218 55
    public static function getOffsetWithDefault($cursor, $defaultOffset)
219
    {
220 55
        if (empty($cursor)) {
221 46
            return $defaultOffset;
222
        }
223 25
        $offset = static::cursorToOffset($cursor);
224
225 25
        return !is_numeric($offset) ? $defaultOffset : (int) $offset;
226
    }
227
228
    /**
229
     * Creates the cursor string from an offset.
230
     *
231
     * @param $offset
232
     *
233
     * @return string
234
     */
235 50
    public static function offsetToCursor($offset)
236
    {
237 50
        return base64_encode(static::PREFIX.$offset);
238
    }
239
240
    /**
241
     * Redefines the offset from the cursor string.
242
     *
243
     * @param $cursor
244
     *
245
     * @return int
246
     */
247 28
    public static function cursorToOffset($cursor)
248
    {
249 28
        return str_replace(static::PREFIX, '', base64_decode($cursor, true));
250
    }
251
252 54
    private static function getOptionsWithDefaults(array $options, array $defaults)
253
    {
254 54
        return $options + $defaults;
255
    }
256
257 3
    private static function checkPromise($value)
258
    {
259 3
        if (!is_callable([$value, 'then'])) {
260
            throw new \InvalidArgumentException('This is not a valid promise.');
261
        }
262 3
    }
263
}
264