Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use Utopia\Http\Request;
use Utopia\Http\Response;
use Utopia\Http\Adapter\FPM\Server;

$container = new Container();
$resources = new Container();

Http::get('/hello-world') // Define Route
->inject('request')
Expand All @@ -46,7 +46,7 @@ Http::get('/hello-world') // Define Route

Http::setMode(Http::MODE_TYPE_PRODUCTION);

$http = new Http(new Server(), 'America/New_York', $container);
$http = new Http(new Server($resources), 'America/New_York');
$http->start();
```

Expand Down Expand Up @@ -74,7 +74,7 @@ use Utopia\Http\Http;
use Utopia\Http\Response;
use Utopia\Http\Adapter\FPM\Server;

$container = new Container();
$resources = new Container();

Http::get('/')
->inject('response')
Expand All @@ -84,7 +84,7 @@ Http::get('/')
}
);

$http = new Http(new Server(), 'America/New_York', $container);
$http = new Http(new Server($resources), 'America/New_York');
$http->start();
```

Expand All @@ -99,7 +99,7 @@ use Utopia\Http\Request;
use Utopia\Http\Response;
use Utopia\Http\Adapter\Swoole\Server;

$container = new Container();
$resources = new Container();

Http::get('/')
->inject('request')
Expand All @@ -110,7 +110,7 @@ Http::get('/')
}
);

$http = new Http(new Server('0.0.0.0', '80'), 'America/New_York', $container);
$http = new Http(new Server('0.0.0.0', '80', resources: $resources), 'America/New_York');
$http->start();
```

Expand Down Expand Up @@ -217,20 +217,20 @@ Groups are designed to be actions that run during the lifecycle of requests to e

### Resources

Resources allow you to prepare dependencies for requests such as database connections or shared services. Register application dependencies on the DI container with `set()`. Runtime values such as `request`, `response`, `route`, `error`, and `context` are scoped by `Http` for each request.
Resources allow you to prepare dependencies for requests such as database connections or shared services. Register application dependencies on the resources container with `set()`. Runtime values such as `request`, `response`, `route`, and `error` are registered on the per-request `context` container by `Http` for each request.

Define a dependency on the DI container:
Define a dependency on the resources container:

```php
$container->set('bootTime', function () {
$resources->set('bootTime', function () {
return \microtime(true);
});
```

Inject resource into endpoint action:

```php
$http = new Http(new Server(), 'America/New_York', $container);
$http = new Http(new Server($resources), 'America/New_York');

Http::get('/')
->inject('bootTime')
Expand Down
23 changes: 22 additions & 1 deletion src/Http/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,26 @@ abstract class Adapter
abstract public function onStart(callable $callback): void;
abstract public function onRequest(callable $callback): void;
abstract public function start(): void;
abstract public function getContainer(): Container;

/**
* Static resources container.
*
* Long-lived, shared across every request for the lifetime of the server.
* Use this for things wired up at boot (config, clients, services) that
* should be reused across requests. Available before any request begins,
* inside server start hooks, and as the parent of every request context.
*/
abstract public function resources(): Container;

/**
* Per-request context container.
*
* A fresh child container created for each incoming request and disposed
* when the request ends. Use it to register or read request-scoped values
* (request, response, route, error, ...). Lookups fall through to
* {@see self::resources()}, so static resources remain reachable from
* within request handlers. Outside of a request, this returns the static
* resources container.
*/
abstract public function context(): Container;
}
24 changes: 18 additions & 6 deletions src/Http/Adapter/FPM/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,39 @@

class Server extends Adapter
{
public function __construct(private Container $container) {}
private ?Container $context = null;

public function __construct(private Container $resources) {}

public function onRequest(callable $callback): void
{
$request = new Request();
$response = new Response();

$this->container->set('fpmRequest', fn() => $request);
$this->container->set('fpmResponse', fn() => $response);
$this->context = new Container($this->resources);
$this->context->set('fpmRequest', fn() => $request);
$this->context->set('fpmResponse', fn() => $response);

\call_user_func($callback, $request, $response);
try {
\call_user_func($callback, $request, $response);
} finally {
$this->context = null;
}
}

public function onStart(callable $callback): void
{
\call_user_func($callback, $this);
}

public function getContainer(): Container
public function resources(): Container
{
return $this->resources;
}

public function context(): Container
{
return $this->container;
return $this->context ?? $this->resources;
}

public function start(): void {}
Expand Down
32 changes: 20 additions & 12 deletions src/Http/Adapter/Swoole/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,47 @@
class Server extends Adapter
{
protected SwooleServer $server;
protected const string REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
protected Container $container;
protected const string CONTEXT_KEY = '__utopia__';
Comment thread
loks0n marked this conversation as resolved.

/**
* @param array<string, mixed> $settings
*/
public function __construct(string $host, ?string $port = null, array $settings = [], int $mode = SWOOLE_PROCESS, ?Container $container = null)
{
public function __construct(
string $host,
?string $port = null,
array $settings = [],
int $mode = SWOOLE_PROCESS,
protected Container $resources = new Container(),
) {
$this->server = new SwooleServer($host, (int) $port, $mode);
$this->server->set($settings);
$this->container = $container ?? new Container();
}

public function onRequest(callable $callback): void
{
$this->server->on('request', function (SwooleRequest $request, SwooleResponse $response) use ($callback) {
$requestContainer = new Container($this->container);
$requestContainer->set('swooleRequest', fn() => $request);
$requestContainer->set('swooleResponse', fn() => $response);
$context = new Container($this->resources);
$context->set('swooleRequest', fn() => $request);
$context->set('swooleResponse', fn() => $response);

Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
Coroutine::getContext()[self::CONTEXT_KEY] = $context;

\call_user_func($callback, new Request($request), new Response($response));
});
}

public function getContainer(): Container
public function resources(): Container
{
return $this->resources;
}

public function context(): Container
{
if (Coroutine::getCid() !== -1) {
return Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] ?? $this->container;
return Coroutine::getContext()[self::CONTEXT_KEY] ?? $this->resources;
}

return $this->container;
return $this->resources;
}

public function getServer(): SwooleServer
Expand Down
25 changes: 14 additions & 11 deletions src/Http/Adapter/SwooleCoroutine/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@

class Server extends Adapter
{
protected const string REQUEST_CONTAINER_CONTEXT_KEY = '__utopia_http_request_container';
protected const string CONTEXT_KEY = '__utopia__';

protected SwooleServer $server;
protected Container $container;

/** @var callable|null */
protected $onStartCallback;
Expand All @@ -26,33 +25,37 @@ public function __construct(
string $host,
?string $port = null,
array $settings = [],
?Container $container = null,
protected Container $resources = new Container(),
) {
$this->server = new SwooleServer($host, $port, false, true);
$this->server->set($settings);
$this->container = $container ?? new Container();
}

public function onRequest(callable $callback): void
{
$this->server->handle('/', function (SwooleRequest $request, SwooleResponse $response) use ($callback) {
$requestContainer = new Container($this->container);
$requestContainer->set('swooleRequest', fn() => $request);
$requestContainer->set('swooleResponse', fn() => $response);
$context = new Container($this->resources);
$context->set('swooleRequest', fn() => $request);
$context->set('swooleResponse', fn() => $response);

Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] = $requestContainer;
Coroutine::getContext()[self::CONTEXT_KEY] = $context;

try {
\call_user_func($callback, new Request($request), new Response($response));
} finally {
unset(Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY]);
unset(Coroutine::getContext()[self::CONTEXT_KEY]);
}
});
}

public function getContainer(): Container
public function resources(): Container
{
return Coroutine::getContext()[self::REQUEST_CONTAINER_CONTEXT_KEY] ?? $this->container;
return $this->resources;
}

public function context(): Container
{
return Coroutine::getContext()[self::CONTEXT_KEY] ?? $this->resources;
}

public function getServer(): SwooleServer
Expand Down
Loading