Skip to content

Commit 865e5e5

Browse files
committed
turn RoutesServiceFactory into a startup hook to avoid container compilation problems
1 parent 1a29095 commit 865e5e5

9 files changed

+72
-53
lines changed

Readme.md

+32-26
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ composer require wondernetwork/slim-kernel
1818
```php
1919
use WonderNetwork\SlimKernel\KernelBuilder;
2020
use WonderNetwork\SlimKernel\ServiceFactory\SymfonyConsoleServiceFactory;
21+
use WonderNetwork\SlimKernel\StartupHook\RoutesStartupHook;
2122

2223
// configure the container:
2324
$container = KernelBuilder::start(
@@ -43,6 +44,10 @@ $container = KernelBuilder::start(
4344
__DIR__.'/../src/Cli/**/*Command.php',
4445
),
4546
)
47+
->onStartup(
48+
// automatically add routes
49+
new RoutesStartupHook(__DIR__.'/../app/routes/*.php'),
50+
)
4651
// only pass a path for prod environments, null otherwise
4752
->useCache(__DIR__.'/../.cache')
4853
->build();
@@ -89,6 +94,33 @@ of a CLI command. This is best place to setup some global error handlers, boot s
8994
static properties, etc. To do so, pass an object implementing `StartupHook` to the
9095
`onStartup()` method
9196

97+
### Routes Startup Hook
98+
99+
`WonderNetwork\SlimKernel\StartupHook\RouteStartupHook`
100+
101+
If you would like to get your routes registered in a declarative manner,
102+
use this startup hook to point to files containing your route definitions.
103+
Each of the files matched by the glob pattern **needs to return** a closure,
104+
which takes `Slim\App`, or more precisely `Slim\Interfaces\RouteCollectorProxyInterface`.
105+
This means you can use it for more than routes: for example global middlewares.
106+
The closures will be evaluated on a Slim Application fetched from the container.
107+
108+
```php
109+
$kernelBuilder->onStartup(
110+
new \WonderNetwork\SlimKernel\StartupHook\RoutesStartupHook(
111+
__DIR__.'/../routes/*.php',
112+
),
113+
);
114+
// app/routes/some.php
115+
use Psr\Http\Message\ResponseInterface;
116+
return static function (Slim\App $app) {
117+
$app->get('/hello/{message}', function (ResponseInterface $response, string $message) {
118+
return $response->withHeader("X-Hello", $message);
119+
});
120+
}
121+
```
122+
123+
92124
## Error handling
93125

94126
The default error handling middleware is added. It’s configured to silently log
@@ -147,29 +179,3 @@ return new WonderNetwork\SlimKernel\ServiceFactory\SymfonyConsoleServiceFactory(
147179
'acme v1.0',
148180
);
149181
```
150-
151-
### Routes Service Factory
152-
153-
`WonderNetwork\SlimKernel\ServiceFactory\RouteServiceFactory`
154-
155-
If you would like to get your routes registered in a declarative manner,
156-
use this service factory to point to files containing your route definitions.
157-
Each of the files matched by the glob pattern **needs to return** a closure,
158-
which takes `Slim\App`, or more precisely `Slim\Interfaces\RouteCollectorProxyInterface`.
159-
This means you can use it for more than routes: for example global middlewares.
160-
The closures will be evaluated whenever the Slim Application is fetched from
161-
the container.
162-
163-
```php
164-
// app/services/routes.php
165-
return new WonderNetwork\SlimKernel\ServiceFactory\RoutesServiceFactory(
166-
__DIR__.'/../routes/*.php',
167-
);
168-
// app/routes/some.php
169-
use Psr\Http\Message\ResponseInterface;
170-
return static function (Slim\App $app) {
171-
$app->get('/hello/{message}', function (ResponseInterface $response, string $message) {
172-
return $response->withHeader("X-Hello", $message);
173-
});
174-
}
175-
```

src/HookCollection.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ final class HookCollection {
1111
public function __construct() {
1212
}
1313

14-
public function add(StartupHook $hook): void {
15-
$this->hooks[] = $hook;
14+
public function add(StartupHook ...$hooks): void {
15+
foreach ($hooks as $hook) {
16+
$this->hooks[] = $hook;
17+
}
1618
}
1719

18-
public function boot(ContainerInterface $container): ContainerInterface {
20+
public function boot(ServicesBuilder $builder, ContainerInterface $container): ContainerInterface {
1921
foreach ($this->hooks as $hook) {
20-
$hook($container);
22+
$hook($builder, $container);
2123
}
2224
return $container;
2325
}

src/KernelBuilder.php

+13-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use DI\ContainerBuilder;
77
use Exception;
88
use Psr\Container\ContainerInterface;
9+
use function WonderNetwork\SlimKernel\Collection\map;
910
use function WonderNetwork\SlimKernel\Collection\toArray;
1011

1112
final class KernelBuilder {
@@ -22,7 +23,6 @@ private function __construct(ServicesBuilder $servicesBuilder) {
2223
$this->builder = new ContainerBuilder();
2324
$this->startupHook = new HookCollection();
2425
$this->register(new ServiceFactory\SlimServiceFactory());
25-
$this->onStartup(new StartupHook\ErrorHandlingHook());
2626
}
2727

2828
/** @param array<string|mixed> $definitions */
@@ -31,13 +31,19 @@ public function add(array $definitions): self {
3131
return $this;
3232
}
3333

34-
public function register(ServiceFactory $serviceFactory): self {
35-
$this->builder->addDefinitions(toArray($serviceFactory($this->servicesBuilder)));
34+
public function register(ServiceFactory ...$serviceFactories): self {
35+
$this->builder->addDefinitions(
36+
...
37+
map(
38+
$serviceFactories,
39+
fn (ServiceFactory $serviceFactory) => toArray($serviceFactory($this->servicesBuilder)),
40+
),
41+
);
3642
return $this;
3743
}
3844

39-
public function onStartup(StartupHook $hook): self {
40-
$this->startupHook->add($hook);
45+
public function onStartup(StartupHook ...$hooks): self {
46+
$this->startupHook->add(...$hooks);
4147
return $this;
4248
}
4349

@@ -59,6 +65,7 @@ public function useCache(?string $path): self {
5965
* @throws Exception
6066
*/
6167
public function build(): ContainerInterface {
62-
return $this->startupHook->boot($this->builder->build());
68+
$this->onStartup(new StartupHook\ErrorHandlingHook());
69+
return $this->startupHook->boot($this->servicesBuilder, $this->builder->build());
6370
}
6471
}

src/StartupHook.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
use Psr\Container\ContainerInterface;
77

88
interface StartupHook {
9-
public function __invoke(ContainerInterface $container): void;
9+
public function __invoke(ServicesBuilder $builder, ContainerInterface $container): void;
1010
}

src/StartupHook/ErrorHandlingHook.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
use Psr\Container\ContainerExceptionInterface;
77
use Psr\Container\ContainerInterface;
88
use Psr\Container\NotFoundExceptionInterface;
9-
use RuntimeException;
109
use Slim\App;
1110
use Slim\Middleware\ErrorMiddleware;
11+
use WonderNetwork\SlimKernel\ServicesBuilder;
1212
use WonderNetwork\SlimKernel\StartupHook;
1313

1414
final class ErrorHandlingHook implements StartupHook {
1515
/**
1616
* @throws ContainerExceptionInterface
1717
* @throws NotFoundExceptionInterface
1818
*/
19-
public function __invoke(ContainerInterface $container): void {
19+
public function __invoke(ServicesBuilder $builder, ContainerInterface $container): void {
2020
$container->get(App::class)->add($container->get(ErrorMiddleware::class));
2121
}
2222
}
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
<?php
22
declare(strict_types=1);
33

4-
namespace WonderNetwork\SlimKernel\ServiceFactory;
4+
namespace WonderNetwork\SlimKernel\StartupHook;
55

6+
use Psr\Container\ContainerInterface;
67
use Slim\App;
7-
use WonderNetwork\SlimKernel\ServiceFactory;
88
use WonderNetwork\SlimKernel\ServicesBuilder;
99
use WonderNetwork\SlimKernel\SlimExtension\SlimClosuresCollection;
10-
use function DI\decorate;
10+
use WonderNetwork\SlimKernel\StartupHook;
1111

12-
final class RoutesServiceFactory implements ServiceFactory {
12+
final class RoutesStartupHook implements StartupHook {
1313
private string $path;
1414

1515
public function __construct(string $path) {
1616
$this->path = $path;
1717
}
1818

19-
public function __invoke(ServicesBuilder $builder): iterable {
19+
public function __invoke(ServicesBuilder $builder, ContainerInterface $container): void {
2020
$closures = SlimClosuresCollection::of(...$builder->files()->glob($this->path));
2121

22-
yield App::class => decorate(static fn (App $previous) => $closures->applyTo($previous));
22+
$app = $container->get(App::class);
23+
$closures->applyTo($app);
2324
}
2425
}

tests/StartupHook/ErrorHandlingHookTest.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
use Slim\Psr7\Factory\ServerRequestFactory;
1010
use Throwable;
1111
use WonderNetwork\SlimKernel\KernelBuilder;
12-
use WonderNetwork\SlimKernel\ServiceFactory\RoutesServiceFactory;
1312

1413
class ErrorHandlingHookTest extends TestCase {
1514
public function testCustomMiddlewaresTakePrecedence() {
1615
$container = KernelBuilder::start(__DIR__.'/../Resources/ErrorMiddleware')
1716
->glob('app/services/*.php')
18-
->register(new RoutesServiceFactory('app/middlewares/*.php'))
19-
->register(new RoutesServiceFactory('app/routes/*.php'))
17+
->onStartup(
18+
new RoutesStartupHook('app/middlewares/*.php'),
19+
new RoutesStartupHook('app/routes/*.php'),
20+
)
2021
->build();
2122

2223
/** @var App $app */

tests/ServiceFactory/RoutesServiceFactoryTest.php tests/StartupHook/RoutesStartupHookTest.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
<?php
22
declare(strict_types=1);
33

4-
namespace WonderNetwork\SlimKernel\ServiceFactory;
4+
namespace WonderNetwork\SlimKernel\StartupHook;
55

66
use PHPUnit\Framework\TestCase;
77
use Slim\App;
88
use Slim\Psr7\Factory\ServerRequestFactory;
99
use WonderNetwork\SlimKernel\KernelBuilder;
1010

11-
class RoutesServiceFactoryTest extends TestCase {
11+
class RoutesStartupHookTest extends TestCase {
1212
public function test(): void {
1313
/** @var App $app */
1414
$app = KernelBuilder::start(__DIR__.'/../Resources/App')
1515
->glob('app/services/*.php')
16-
->register(new RoutesServiceFactory('app/routes/*.php'))
17-
->register(new RoutesServiceFactory('app/middlewares/*.php'))
16+
->onStartup(
17+
new RoutesStartupHook('app/routes/*.php'),
18+
new RoutesStartupHook('app/middlewares/*.php'),
19+
)
1820
->build()
1921
->get(App::class);
2022
$factory = new ServerRequestFactory();

tests/StartupHookSpy.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
final class StartupHookSpy implements StartupHook {
99
public ?ContainerInterface $container = null;
1010

11-
public function __invoke(ContainerInterface $container): void {
11+
public function __invoke(ServicesBuilder $builder, ContainerInterface $container): void {
1212
$this->container = $container;
1313
}
1414
}

0 commit comments

Comments
 (0)