Skip to content

Commit 7c15b9b

Browse files
authored
Merge pull request #187 from patchlevel/improve-no-more-middleware-exception
improve no more middleware exception
2 parents 85cb452 + 0668f18 commit 7c15b9b

10 files changed

Lines changed: 120 additions & 16 deletions

phpstan-baseline.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ parameters:
186186
count: 1
187187
path: src/Normalizer/ReflectionTypeUtil.php
188188

189+
-
190+
message: '#^Parameter \#1 \$middlewares of class Patchlevel\\Hydrator\\Middleware\\Stack constructor expects non\-empty\-list\<Patchlevel\\Hydrator\\Middleware\\Middleware\>, list\<Patchlevel\\Hydrator\\Middleware\\Middleware\> given\.$#'
191+
identifier: argument.type
192+
count: 4
193+
path: src/StackHydrator.php
194+
189195
-
190196
message: '#^Property Patchlevel\\Hydrator\\Tests\\Unit\\Extension\\Cryptography\\Fixture\\ChildWithSensitiveDataWithIdentifierDto\:\:\$email is never read, only written\.$#'
191197
identifier: property.onlyWritten

src/Middleware/NoMoreMiddleware.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@
77
use Patchlevel\Hydrator\HydratorException;
88
use RuntimeException;
99

10+
use function array_map;
11+
use function count;
12+
use function implode;
13+
use function sprintf;
14+
1015
/** @experimental */
1116
final class NoMoreMiddleware extends RuntimeException implements HydratorException
1217
{
13-
public function __construct()
18+
/** @param non-empty-list<Middleware> $middlewares */
19+
public function __construct(array $middlewares)
1420
{
15-
parent::__construct('no more middlewares');
21+
parent::__construct(
22+
sprintf(
23+
'The next middleware in %s was requested, but no further middleware exists. The following middlewares were executed: %s',
24+
$middlewares[count($middlewares) - 1]::class,
25+
implode(', ', array_map(static fn (Middleware $middleware): string => $middleware::class, $middlewares)),
26+
),
27+
);
1628
}
1729
}

src/Middleware/Stack.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ final class Stack
99
{
1010
private int $index = 0;
1111

12-
/** @param list<Middleware> $middlewares */
12+
/** @param non-empty-list<Middleware> $middlewares */
1313
public function __construct(
1414
private readonly array $middlewares,
1515
) {
@@ -20,7 +20,7 @@ public function next(): Middleware
2020
$next = $this->middlewares[$this->index] ?? null;
2121

2222
if ($next === null) {
23-
throw new NoMoreMiddleware();
23+
throw new NoMoreMiddleware($this->middlewares);
2424
}
2525

2626
$this->index++;

src/MissingMiddlewares.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\Hydrator;
6+
7+
use RuntimeException;
8+
9+
/** @experimental */
10+
final class MissingMiddlewares extends RuntimeException implements HydratorException
11+
{
12+
public function __construct()
13+
{
14+
parent::__construct('Missing middlewares.');
15+
}
16+
}

src/StackHydrator.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public function __construct(
3030
private readonly array $middlewares = [new TransformMiddleware()],
3131
private readonly bool $defaultLazy = false,
3232
) {
33+
if ($middlewares === []) {
34+
throw new MissingMiddlewares();
35+
}
3336
}
3437

3538
/**
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\Hydrator\Tests\Unit\Fixture;
6+
7+
use Patchlevel\Hydrator\Metadata\ClassMetadata;
8+
use Patchlevel\Hydrator\Middleware\Middleware;
9+
use Patchlevel\Hydrator\Middleware\Stack;
10+
11+
final class DummyMiddleware implements Middleware
12+
{
13+
/**
14+
* @param ClassMetadata<T> $metadata
15+
* @param array<string, mixed> $data
16+
* @param array<string, mixed> $context
17+
*
18+
* @return T
19+
*
20+
* @template T of object
21+
*/
22+
public function hydrate(ClassMetadata $metadata, array $data, array $context, Stack $stack): object
23+
{
24+
return $stack->next()->hydrate($metadata, $data, $context, $stack);
25+
}
26+
27+
/**
28+
* @param array<string, mixed> $context
29+
*
30+
* @return array<string, mixed>
31+
*/
32+
public function extract(ClassMetadata $metadata, object $object, array $context, Stack $stack): array
33+
{
34+
return $stack->next()->extract($metadata, $object, $context, $stack);
35+
}
36+
}

tests/Unit/Middleware/StackTest.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,41 @@
44

55
namespace Patchlevel\Hydrator\Tests\Unit\Middleware;
66

7-
use Patchlevel\Hydrator\Middleware\Middleware;
87
use Patchlevel\Hydrator\Middleware\NoMoreMiddleware;
98
use Patchlevel\Hydrator\Middleware\Stack;
9+
use Patchlevel\Hydrator\Tests\Unit\Fixture\DummyMiddleware;
1010
use PHPUnit\Framework\Attributes\CoversClass;
1111
use PHPUnit\Framework\TestCase;
1212

1313
#[CoversClass(Stack::class)]
1414
final class StackTest extends TestCase
1515
{
16-
public function testEmptyStack(): void
16+
public function testStack(): void
1717
{
18-
$this->expectException(NoMoreMiddleware::class);
18+
$middleware1 = new DummyMiddleware();
19+
$middleware2 = new DummyMiddleware();
1920

20-
$stack = new Stack([]);
21-
$stack->next();
21+
$stack = new Stack([$middleware1, $middleware2]);
22+
23+
self::assertSame($middleware1, $stack->next());
24+
self::assertSame($middleware2, $stack->next());
2225
}
2326

24-
public function testStack(): void
27+
public function testStackThrowsExceptionWhenNoMoreMiddlewareIsAvailable(): void
2528
{
26-
$middleware1 = $this->createStub(Middleware::class);
27-
$middleware2 = $this->createStub(Middleware::class);
29+
$middleware1 = new DummyMiddleware();
30+
$middleware2 = new DummyMiddleware();
2831

2932
$stack = new Stack([$middleware1, $middleware2]);
3033

31-
self::assertSame($middleware1, $stack->next());
32-
self::assertSame($middleware2, $stack->next());
34+
$stack->next();
35+
$stack->next();
36+
37+
$this->expectException(NoMoreMiddleware::class);
38+
$this->expectExceptionMessage(
39+
'The next middleware in Patchlevel\Hydrator\Tests\Unit\Fixture\DummyMiddleware was requested, but no further middleware exists. The following middlewares were executed: Patchlevel\Hydrator\Tests\Unit\Fixture\DummyMiddleware, Patchlevel\Hydrator\Tests\Unit\Fixture\DummyMiddleware',
40+
);
41+
42+
$stack->next();
3343
}
3444
}

tests/Unit/Middleware/TransformerMiddlewareTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function testHydrate(): void
3030
$this->classMetadata(ProfileCreated::class),
3131
['profileId' => '1', 'email' => 'info@patchlevel.de'],
3232
[],
33-
new Stack([]),
33+
new Stack([$middleware]),
3434
);
3535

3636
self::assertEquals($expected, $event);
@@ -49,7 +49,7 @@ public function testExtract(): void
4949
Email::fromString('info@patchlevel.de'),
5050
),
5151
[],
52-
new Stack([]),
52+
new Stack([$middleware]),
5353
);
5454

5555
self::assertEquals($expected, $data);

tests/Unit/StackHydratorBuilderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public function testAddMetadataEnricherWithPriority(): void
5151
$builder->addMetadataEnricher($enricher1, 10);
5252
$builder->addMetadataEnricher($enricher2, 20);
5353

54+
$builder->addMiddleware($this->createMock(Middleware::class));
55+
5456
$hydrator = $builder->build();
5557

5658
$reflection = new ReflectionProperty(StackHydrator::class, 'metadataFactory');
@@ -74,6 +76,8 @@ public function testAddGuesserWithPriority(): void
7476
$builder->addGuesser($guesser1, 10);
7577
$builder->addGuesser($guesser2, 20);
7678

79+
$builder->addMiddleware($this->createMock(Middleware::class));
80+
7781
$hydrator = $builder->build();
7882

7983
$reflection = new ReflectionProperty(StackHydrator::class, 'metadataFactory');
@@ -103,6 +107,8 @@ public function testEnableDefaultLazy(): void
103107
$builder = new StackHydratorBuilder();
104108
$builder->enableDefaultLazy();
105109

110+
$builder->addMiddleware($this->createMock(Middleware::class));
111+
106112
$hydrator = $builder->build();
107113

108114
$reflection = new ReflectionProperty(StackHydrator::class, 'defaultLazy');
@@ -128,6 +134,7 @@ public function testCachePsr6(): void
128134

129135
$builder = new StackHydratorBuilder();
130136
$builder->setCache($cache);
137+
$builder->addMiddleware($this->createMock(Middleware::class));
131138

132139
$hydrator = $builder->build();
133140

@@ -143,6 +150,7 @@ public function testCachePsr16(): void
143150

144151
$builder = new StackHydratorBuilder();
145152
$builder->setCache($cache);
153+
$builder->addMiddleware($this->createMock(Middleware::class));
146154

147155
$hydrator = $builder->build();
148156

tests/Unit/StackHydratorTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
use Patchlevel\Hydrator\ClassNotSupported;
1212
use Patchlevel\Hydrator\CoreExtension;
1313
use Patchlevel\Hydrator\DenormalizationFailure;
14+
use Patchlevel\Hydrator\Metadata\AttributeMetadataFactory;
1415
use Patchlevel\Hydrator\Metadata\ClassMetadata;
1516
use Patchlevel\Hydrator\Middleware\Middleware;
1617
use Patchlevel\Hydrator\Middleware\Stack;
1718
use Patchlevel\Hydrator\Middleware\TransformMiddleware;
19+
use Patchlevel\Hydrator\MissingMiddlewares;
1820
use Patchlevel\Hydrator\NormalizationFailure;
1921
use Patchlevel\Hydrator\Normalizer\HydratorAwareNormalizer;
2022
use Patchlevel\Hydrator\StackHydrator;
@@ -58,6 +60,17 @@ public function setUp(): void
5860
$this->hydrator = new StackHydrator();
5961
}
6062

63+
public function testMissingMiddlewares(): void
64+
{
65+
$this->expectException(MissingMiddlewares::class);
66+
$this->expectExceptionMessage('Missing middlewares.');
67+
68+
new StackHydrator(
69+
new AttributeMetadataFactory(),
70+
[],
71+
);
72+
}
73+
6174
public function testExtract(): void
6275
{
6376
$event = new ProfileCreated(

0 commit comments

Comments
 (0)