Skip to content

Commit 2be1273

Browse files
committed
Make the ConnectionBuilder class a generic type
1 parent f33db28 commit 2be1273

File tree

7 files changed

+115
-62
lines changed

7 files changed

+115
-62
lines changed

phpstan-baseline.neon

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -481,87 +481,87 @@ parameters:
481481
path: tests/Functional/Validator/DummyEntity.php
482482

483483
-
484-
message: "#^Cannot access offset mixed on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\>\\.$#"
484+
message: "#^Cannot access offset mixed on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<mixed\\>\\>\\.$#"
485485
count: 2
486486
path: tests/Relay/Connection/AbstractConnectionBuilderTest.php
487487

488488
-
489-
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\> given\\.$#"
489+
message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<mixed\\>\\> given\\.$#"
490490
count: 1
491491
path: tests/Relay/Connection/AbstractConnectionBuilderTest.php
492492

493493
-
494-
message: "#^Cannot access offset 0 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\>\\.$#"
494+
message: "#^Cannot access offset 0 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<mixed\\>\\>\\.$#"
495495
count: 4
496496
path: tests/Relay/Connection/ConnectionBuilderTest.php
497497

498498
-
499-
message: "#^Cannot access offset 1 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\>\\.$#"
499+
message: "#^Cannot access offset 1 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<mixed\\>\\>\\.$#"
500500
count: 4
501501
path: tests/Relay/Connection/ConnectionBuilderTest.php
502502

503503
-
504-
message: "#^Cannot access offset 2 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\>\\.$#"
504+
message: "#^Cannot access offset 2 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<mixed\\>\\>\\.$#"
505505
count: 1
506506
path: tests/Relay/Connection/ConnectionBuilderTest.php
507507

508508
-
509-
message: "#^Cannot access offset 3 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\>\\.$#"
509+
message: "#^Cannot access offset 3 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<mixed\\>\\>\\.$#"
510510
count: 1
511511
path: tests/Relay/Connection/ConnectionBuilderTest.php
512512

513513
-
514-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\:\\:\\$cursor\\.$#"
514+
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\<mixed\\>\\:\\:\\$extra\\.$#"
515515
count: 1
516516
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
517517

518518
-
519-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\:\\:\\$node\\.$#"
519+
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$endCursor\\.$#"
520520
count: 1
521521
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
522522

523523
-
524-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\:\\:\\$extra\\.$#"
524+
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$hasNextPage\\.$#"
525525
count: 1
526526
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
527527

528528
-
529-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$endCursor\\.$#"
529+
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$hasPreviousPage\\.$#"
530530
count: 1
531531
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
532532

533533
-
534-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$hasNextPage\\.$#"
534+
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$startCursor\\.$#"
535535
count: 1
536536
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
537537

538538
-
539-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$hasPreviousPage\\.$#"
539+
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\<mixed\\>\\:\\:\\$edges\\.$#"
540540
count: 1
541541
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
542542

543543
-
544-
message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\:\\:\\$startCursor\\.$#"
544+
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\<mixed\\>\\:\\:\\$pageInfo\\.$#"
545545
count: 1
546546
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
547547

548548
-
549-
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\:\\:\\$edges\\.$#"
550-
count: 2
549+
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\<string\\>\\:\\:\\$edges\\.$#"
550+
count: 1
551551
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
552552

553553
-
554-
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\:\\:\\$pageInfo\\.$#"
555-
count: 2
554+
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\<string\\>\\:\\:\\$pageInfo\\.$#"
555+
count: 1
556556
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
557557

558558
-
559-
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Edge\\:\\:\\$cursor\\.$#"
559+
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Edge\\<null\\>\\:\\:\\$cursor\\.$#"
560560
count: 1
561561
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
562562

563563
-
564-
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Edge\\:\\:\\$node\\.$#"
564+
message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Edge\\<null\\>\\:\\:\\$node\\.$#"
565565
count: 1
566566
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
567567

@@ -585,3 +585,18 @@ parameters:
585585
count: 1
586586
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
587587

588+
-
589+
message: "#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with 'node' and null will always evaluate to false\\.$#"
590+
count: 1
591+
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
592+
593+
-
594+
message: "#^Cannot access offset 0 on iterable\\<Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\EdgeInterface\\<string\\>\\>\\.$#"
595+
count: 4
596+
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
597+
598+
-
599+
message: "#^Property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Edge\\<null\\>\\:\\:\\$node \\(null\\) does not accept string\\.$#"
600+
count: 1
601+
path: tests/Relay/Connection/Output/DeprecatedPropertyPublicAccessTraitTest.php
602+

src/Relay/Connection/ConnectionBuilder.php

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
use function is_numeric;
2020
use function max;
2121
use function min;
22-
use function sprintf;
2322
use function str_replace;
2423

2524
/**
2625
* Class ConnectionBuilder.
2726
*
2827
* https://github.com/graphql/graphql-relay-js/blob/master/src/connection/arrayconnection.js
28+
*
29+
* @phpstan-type ConnectionFactoryFunc callable(EdgeInterface<T>[], PageInfoInterface): ConnectionInterface
30+
* @phpstan-type EdgeFactoryFunc callable(string, T, int): EdgeInterface<T>
31+
*
32+
* @phpstan-template T
2933
*/
3034
final class ConnectionBuilder
3135
{
@@ -34,24 +38,32 @@ final class ConnectionBuilder
3438
private CursorEncoderInterface $cursorEncoder;
3539

3640
/**
37-
* If set, used to generate the connection object.
41+
* Factorty callback used to generate the connection object.
42+
*
43+
* @var callable
3844
*
39-
* @var callable|null
45+
* @phpstan-var ConnectionFactoryFunc
4046
*/
4147
private $connectionCallback;
4248

4349
/**
44-
* If set, used to generate the edge object.
50+
* Factorty callback used to generate the edge object.
4551
*
46-
* @var ?callable
52+
* @var callable
53+
*
54+
* @phpstan-var EdgeFactoryFunc
4755
*/
4856
private $edgeCallback;
4957

50-
public function __construct(?CursorEncoderInterface $cursorEncoder = null, callable $connectionCallback = null, callable $edgeCallback = null)
58+
/**
59+
* @phpstan-param ConnectionFactoryFunc|null $connectionCallback
60+
* @phpstan-param EdgeFactoryFunc|null $edgeCallback
61+
*/
62+
public function __construct(?CursorEncoderInterface $cursorEncoder = null, ?callable $connectionCallback = null, ?callable $edgeCallback = null)
5163
{
5264
$this->cursorEncoder = $cursorEncoder ?? new Base64CursorEncoder();
53-
$this->connectionCallback = $connectionCallback;
54-
$this->edgeCallback = $edgeCallback;
65+
$this->connectionCallback = $connectionCallback ?? static fn (array $edges, PageInfoInterface $pageInfo): Connection => new Connection($edges, $pageInfo);
66+
$this->edgeCallback = $edgeCallback ?? static fn (string $cursor, mixed $value): Edge => new Edge($cursor, $value);
5567
}
5668

5769
/**
@@ -60,6 +72,10 @@ public function __construct(?CursorEncoderInterface $cursorEncoder = null, calla
6072
* so pagination will only work if the array is static.
6173
*
6274
* @param array|ArgumentInterface $args
75+
*
76+
* @phpstan-param T[] $data
77+
*
78+
* @return ConnectionInterface<T>
6379
*/
6480
public function connectionFromArray(array $data, $args = []): ConnectionInterface
6581
{
@@ -99,6 +115,10 @@ public function connectionFromPromisedArray($dataPromise, $args = [])
99115
* total result large enough to cover the range specified in `args`.
100116
*
101117
* @param array|ArgumentInterface $args
118+
*
119+
* @phpstan-param T[] $arraySlice
120+
*
121+
* @phpstan-return ConnectionInterface<T>
102122
*/
103123
public function connectionFromArraySlice(array $arraySlice, $args, array $meta): ConnectionInterface
104124
{
@@ -257,41 +277,43 @@ public function cursorToOffset(?string $cursor): string
257277
return str_replace(static::PREFIX, '', $this->cursorEncoder->decode($cursor));
258278
}
259279

280+
/**
281+
* @phpstan-param iterable<T> $slice
282+
*
283+
* @phpstan-return EdgeInterface<T>[]
284+
*/
260285
private function createEdges(iterable $slice, int $startOffset): array
261286
{
262287
$edges = [];
263288

264289
foreach ($slice as $index => $value) {
265290
$cursor = $this->offsetToCursor($startOffset + $index);
266-
if ($this->edgeCallback) {
267-
$edge = ($this->edgeCallback)($cursor, $value, $index);
268-
if (!($edge instanceof EdgeInterface)) {
269-
throw new InvalidArgumentException(sprintf('The $edgeCallback of the ConnectionBuilder must return an instance of EdgeInterface'));
270-
}
271-
} else {
272-
$edge = new Edge($cursor, $value);
291+
$edge = ($this->edgeCallback)($cursor, $value, $index);
292+
293+
if (!($edge instanceof EdgeInterface)) {
294+
throw new InvalidArgumentException('The $edgeCallback of the ConnectionBuilder must return an instance of EdgeInterface');
273295
}
296+
274297
$edges[] = $edge;
275298
}
276299

277300
return $edges;
278301
}
279302

280303
/**
281-
* @param mixed $edges
304+
* @phpstan-param EdgeInterface<T>[] $edges
305+
*
306+
* @phpstan-return ConnectionInterface<T>
282307
*/
283-
private function createConnection($edges, PageInfoInterface $pageInfo): ConnectionInterface
308+
private function createConnection(array $edges, PageInfoInterface $pageInfo): ConnectionInterface
284309
{
285-
if ($this->connectionCallback) {
286-
$connection = ($this->connectionCallback)($edges, $pageInfo);
287-
if (!($connection instanceof ConnectionInterface)) {
288-
throw new InvalidArgumentException(sprintf('The $connectionCallback of the ConnectionBuilder must return an instance of ConnectionInterface'));
289-
}
310+
$connection = ($this->connectionCallback)($edges, $pageInfo);
290311

291-
return $connection;
312+
if (!($connection instanceof ConnectionInterface)) {
313+
throw new InvalidArgumentException('The $connectionCallback of the ConnectionBuilder must return an instance of ConnectionInterface');
292314
}
293315

294-
return new Connection($edges, $pageInfo);
316+
return $connection;
295317
}
296318

297319
private function getOptionsWithDefaults(array $options, array $defaults): array

src/Relay/Connection/ConnectionInterface.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@
66

77
use GraphQL\Executor\Promise\Promise;
88

9+
/**
10+
* @phpstan-template T
11+
*/
912
interface ConnectionInterface
1013
{
1114
/**
1215
* Get the connection edges.
1316
*
14-
* @return iterable|EdgeInterface[]
17+
* @return iterable<EdgeInterface<T>>
1518
*/
1619
public function getEdges();
1720

1821
/**
1922
* Set the connection edges.
2023
*
21-
* @param iterable|EdgeInterface[] $edges
24+
* @param iterable<EdgeInterface<T>> $edges
2225
*/
2326
public function setEdges(iterable $edges): void;
2427

src/Relay/Connection/EdgeInterface.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,31 @@
44

55
namespace Overblog\GraphQLBundle\Relay\Connection;
66

7+
/**
8+
* @phpstan-template T
9+
*/
710
interface EdgeInterface
811
{
912
/**
1013
* Get the edge node.
1114
*
1215
* @return mixed
16+
*
17+
* @phpstan-return T|null
1318
*/
1419
public function getNode();
1520

1621
/**
1722
* Set the edge node.
1823
*
1924
* @param mixed $node
25+
*
26+
* @phpstan-param T|null $node
2027
*/
2128
public function setNode($node): void;
2229

2330
/**
2431
* Get the edge cursor.
25-
*
26-
* @return string
2732
*/
2833
public function getCursor(): ?string;
2934

src/Relay/Connection/Output/Connection.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,26 @@
99
use Overblog\GraphQLBundle\Relay\Connection\EdgeInterface;
1010
use Overblog\GraphQLBundle\Relay\Connection\PageInfoInterface;
1111

12+
/**
13+
* @phpstan-template T
14+
*
15+
* @phpstan-implements ConnectionInterface<T>
16+
*/
1217
class Connection implements ConnectionInterface
1318
{
1419
use DeprecatedPropertyPublicAccessTrait;
1520

16-
/** @var EdgeInterface[] */
17-
protected array $edges;
21+
/** @phpstan-var iterable<EdgeInterface<T>> */
22+
protected iterable $edges;
1823

1924
protected ?PageInfoInterface $pageInfo;
2025

2126
/** @var int|Promise|null Total count or promise that returns the total count */
2227
protected $totalCount = null;
2328

29+
/**
30+
* @param EdgeInterface<T>[] $edges
31+
*/
2432
public function __construct(array $edges = [], PageInfoInterface $pageInfo = null)
2533
{
2634
$this->edges = $edges;
@@ -30,7 +38,7 @@ public function __construct(array $edges = [], PageInfoInterface $pageInfo = nul
3038
/**
3139
* {@inheritdoc}
3240
*/
33-
public function getEdges(): array
41+
public function getEdges(): iterable
3442
{
3543
return $this->edges;
3644
}

src/Relay/Connection/Output/Edge.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@
66

77
use Overblog\GraphQLBundle\Relay\Connection\EdgeInterface;
88

9+
/**
10+
* @phpstan-template T
11+
*
12+
* @phpstan-implements EdgeInterface<T>
13+
*/
914
class Edge implements EdgeInterface
1015
{
1116
use DeprecatedPropertyPublicAccessTrait;
1217

1318
protected ?string $cursor;
1419

15-
/** @var mixed */
16-
protected $node;
20+
/** @phpstan-var T|null */
21+
protected mixed $node;
1722

1823
/**
1924
* @param mixed $node
25+
*
26+
* @phpstan-param T|null $node
2027
*/
2128
public function __construct(string $cursor = null, $node = null)
2229
{

0 commit comments

Comments
 (0)