Skip to content

Commit 5205bd8

Browse files
committed
Merge branch 'develop'
* develop: specify next release mention the async server is not meant to be used in production highlight notes add shortcut to reference a service to handle a route add short syntax to declare routes
2 parents 917dfe0 + 766d354 commit 5205bd8

11 files changed

Lines changed: 258 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 1.3.0 - 2023-05-01
4+
5+
### Added
6+
7+
- `Innmind\Framework\Application::route()`
8+
- `Innmind\Framework\Http\To`
9+
310
## 1.2.0 - 2023-04-29
411

512
### Added

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Take a look at the [documentation](docs/) for a more in-depth understanding of t
2424

2525
The first step is to create the index file that will be exposed via a webserver (for example `public/index.php`). Then you need to specify the routes you want to handle.
2626

27-
**Note**: if you don't configure any route it will respond with `404 Not Found` with an empty body.
27+
> **Note** if you don't configure any route it will respond with `404 Not Found` with an empty body.
2828
2929
```php
3030
<?php
@@ -80,7 +80,7 @@ You can run this script via `cd public && php -S localhost:8080`. If you open yo
8080

8181
The entrypoint of your cli tools will look something like this.
8282

83-
**Note**: by default if you don't configure any command it will always display `Hello world`.
83+
> **Note** by default if you don't configure any command it will always display `Hello world`.
8484
8585
```php
8686
<?php

docs/experimental/async-server.md

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

33
The framework comes with an HTTP server entirely built in PHP allowing you to serve your app without extra dependencies in ther earlist stages of your project.
44

5-
**Note**: This feature is optional, to use it you must before run `composer require innmind/async-http-server`.
5+
> **Note** This feature is optional, to use it you must before run `composer require innmind/async-http-server`.
66
77
To use it is similar to the standard [http](../http.md) handler, the first difference is the namespace of the main entrypoint:
88

@@ -29,4 +29,6 @@ Note the namespace is `Main\Async\Http` instead of `Main\Http`. The other differ
2929

3030
All the configuration of the `Application` object is identical to the other contexts.
3131

32-
**Note**: The server currently does have limitations, streamed requests (via `Transfer-Encoding`) are not supported and multipart requests are not parsed.
32+
> **Note** The server currently does have limitations, streamed requests (via `Transfer-Encoding`) are not supported and multipart requests are not parsed.
33+
34+
> **Warning** This server was built to showcase in a conference talk the ability to switch between synchronous code and asynchronous code without changing the app code. Do **NOT** use this server in production.

docs/http.md

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,43 @@ new class extends Http {
9494

9595
For simple apps having the whole behaviour next to the route can be ok. But like in this case it can be repetitive, for such case we can specify our behaviours elsewhere: [services](#Services).
9696

97+
## Short syntax
98+
99+
The previous shows the default way to declare routes, but for very simple apps it can be a bit verbose. The framework provides a shorter syntax to handle routes:
100+
101+
```php
102+
use Innmind\Framework\{
103+
Main\Http,
104+
Application,
105+
};
106+
use Innmind\Router\Route\Variables;
107+
use Innmind\Http\Message\{
108+
ServerRequest,
109+
Response\Response,
110+
StatusCode,
111+
};
112+
use Innmind\Filesystem\File\Content;
113+
114+
new class extends Http {
115+
protected function configure(Application $app): Application
116+
{
117+
return $app
118+
->route('GET /', static fn(ServerRequest $request) => new Response(
119+
StatusCode::ok,
120+
$request->protocolVersion(),
121+
null,
122+
Content\Lines::ofContent('Hello world!'),
123+
))
124+
->route('GET /{name}', static fn(ServerRequest $request, Variables $variables) => new Response(
125+
StatusCode::ok,
126+
$request->protocolVersion(),
127+
null,
128+
Content\Lines::ofContent("Hello {$variables->get('name')}!"),
129+
));
130+
}
131+
};
132+
```
133+
97134
## Services
98135

99136
Services are any object that are referenced by a string in a [`Container`](https://github.com/Innmind/DI). For example let's take the route handler from the previous section and move them inside services.
@@ -104,6 +141,7 @@ use Innmind\Framework\{
104141
Application,
105142
Http\Routes,
106143
Http\Service,
144+
Http\To,
107145
};
108146
use Innmind\DI\Container;
109147
use Innmind\Router\{
@@ -151,16 +189,16 @@ new class extends Http {
151189
)
152190
->appendRoutes(
153191
static fn(Routes $routes, Container $container) => $routes
154-
->add(Route::literal('GET /')->handle(Service::of($container, 'hello-word')))
155-
->add(Route::literal('GET /{name}')->handle(Service::of($container, 'hello-name'))),
156-
);
192+
->add(Route::literal('GET /')->handle(Service::of($container, 'hello-word'))),
193+
)
194+
->route('GET /{name}', To::service('hello-name'));
157195
}
158196
};
159197
```
160198

161199
Here the services are invokable anonymous classes to conform to the callable expected for a `Route` but you can create dedicated classes for each one.
162200

163-
**Note**: Head to the [services topic](services.md) for a more in-depth look of what's possible.
201+
> **Note** Head to the [services topic](services.md) for a more in-depth look of what's possible.
164202
165203
## Executing code on any route
166204

@@ -216,7 +254,7 @@ This example will refuse any request that doesn't have an `Authorization` header
216254

217255
You can have multiple calls to `mapRequestHandler` to compose behaviours like an onion.
218256

219-
**Note**: the default request handler is the inner router of the framework, this means that you can completely change the default behaviour of the framework by returning a new request handler that never uses the default one.
257+
> **Note** the default request handler is the inner router of the framework, this means that you can completely change the default behaviour of the framework by returning a new request handler that never uses the default one.
220258
221259
## Handling unknown routes
222260

docs/middlewares.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Middlewares are a way to regroup all the configuration you've seen in other topics under a name. This means that you can either group part of your own application undeer a middleware or expose a package for other to use via Packagist.
44

5-
**Note**: you can search for [`innmind/framework-middlewares` on Packagist](https://packagist.org/providers/innmind/framework-middlewares) for middlewares published by others.
5+
> **Note** you can search for [`innmind/framework-middlewares` on Packagist](https://packagist.org/providers/innmind/framework-middlewares) for middlewares published by others.
66
77
Let's say you have an application that sends emails you could have a middleware that looks like this:
88

docs/services.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
For both [HTTP](http.md) and [CLI](cli.md) applications a service is an object referenced by a name in a [`Container`](https://github.com/Innmind/DI).
44

5-
**Note**: since a container only deals with objects Psalm will complain of type mismatches, so you'll have to suppress those errors (for now).
5+
> **Note** since a container only deals with objects Psalm will complain of type mismatches, so you'll have to suppress those errors (for now).
66
77
## Defining a service
88

@@ -35,7 +35,7 @@ new class extends Http|Cli {
3535

3636
This example defines a single service named `amqp-client` that relies on the `OperatingSystem` in order to work.
3737

38-
**Note**: this example uses [`innmind/amqp`](https://github.com/innmind/amqp)
38+
> **Note** this example uses [`innmind/amqp`](https://github.com/innmind/amqp)
3939
4040
## Configure via environment variables
4141

src/Application.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
ServerRequest,
1818
Response,
1919
};
20+
use Innmind\Router\{
21+
Route,
22+
Route\Variables,
23+
};
2024

2125
final class Application
2226
{
@@ -123,6 +127,24 @@ public function mapCommand(callable $map): self
123127
return $this;
124128
}
125129

130+
/**
131+
* @psalm-mutation-free
132+
*
133+
* @param literal-string $pattern
134+
* @param callable(ServerRequest, Variables, Container, OperatingSystem, Environment): Response $handle
135+
*/
136+
public function route(string $pattern, callable $handle): self
137+
{
138+
if (
139+
$this->app instanceof Application\Http ||
140+
$this->app instanceof Application\Async\Http
141+
) {
142+
return new self($this->app->route($pattern, $handle));
143+
}
144+
145+
return $this;
146+
}
147+
126148
/**
127149
* @psalm-mutation-free
128150
*

src/Application/Async/Http.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
ServerRequest,
2727
Response,
2828
};
29+
use Innmind\Router\{
30+
Route,
31+
Route\Variables,
32+
};
2933
use Innmind\Immutable\Maybe;
3034

3135
/**
@@ -153,6 +157,27 @@ public function service(string $name, callable $definition): self
153157
);
154158
}
155159

160+
/**
161+
* @psalm-mutation-free
162+
*
163+
* @param literal-string $pattern
164+
* @param callable(ServerRequest, Variables, Container, OperatingSystem, Environment): Response $handle
165+
*/
166+
public function route(string $pattern, callable $handle): self
167+
{
168+
return $this->appendRoutes(
169+
static fn($routes, $container, $os, $env) => $routes->add(
170+
Route::literal($pattern)->handle(static fn($request, $variables) => $handle(
171+
$request,
172+
$variables,
173+
$container,
174+
$os,
175+
$env,
176+
)),
177+
),
178+
);
179+
}
180+
156181
/**
157182
* @psalm-mutation-free
158183
*

src/Application/Http.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
ServerRequest,
1919
Response,
2020
};
21+
use Innmind\Router\{
22+
Route,
23+
Route\Variables,
24+
};
2125
use Innmind\Immutable\Maybe;
2226

2327
final class Http
@@ -132,6 +136,27 @@ public function service(string $name, callable $definition): self
132136
);
133137
}
134138

139+
/**
140+
* @psalm-mutation-free
141+
*
142+
* @param literal-string $pattern
143+
* @param callable(ServerRequest, Variables, Container, OperatingSystem, Environment): Response $handle
144+
*/
145+
public function route(string $pattern, callable $handle): self
146+
{
147+
return $this->appendRoutes(
148+
static fn($routes, $container, $os, $env) => $routes->add(
149+
Route::literal($pattern)->handle(static fn($request, $variables) => $handle(
150+
$request,
151+
$variables,
152+
$container,
153+
$os,
154+
$env,
155+
)),
156+
),
157+
);
158+
}
159+
135160
/**
136161
* @psalm-mutation-free
137162
*

src/Http/To.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Innmind\Framework\Http;
5+
6+
use Innmind\Http\Message\{
7+
ServerRequest,
8+
Response,
9+
};
10+
use Innmind\DI\Container;
11+
use Innmind\Router\Route\Variables;
12+
13+
final class To
14+
{
15+
private string $service;
16+
17+
private function __construct(string $service)
18+
{
19+
$this->service = $service;
20+
}
21+
22+
public function __invoke(
23+
ServerRequest $request,
24+
Variables $variables,
25+
Container $container,
26+
): Response {
27+
/**
28+
* @psalm-suppress InvalidFunctionCall If it fails here then the service doesn't conform to the signature callable(ServerRequest, Variables): Response
29+
* @var Response
30+
*/
31+
return $container($this->service)($request, $variables);
32+
}
33+
34+
public static function service(string $service): self
35+
{
36+
return new self($service);
37+
}
38+
}

0 commit comments

Comments
 (0)