Ability to declare HTTP resources #3

Closed
opened 2026-04-25 15:03:10 +02:00 by c.fahner · 1 comment
Owner

Modules should be able to declare HTTP resources. Each resource can respond differently based on the HTTP method. Supported query or body arguments are declared as function parameters and filled in using reflection.

Resources exposed by modules exist in a fixed namespace called Resources relative to the Module implementation to avoid having to keep track of all possible routes in a big centralized map. The filesystem itself basically becomes the routing table. Every incoming request is mapped to a resource based on implementation-defined (or even user-defined) routing rules. A basic implementation could simply map the URLs' path to a class name.

Links to other HTTP resources on the system can be declared in a type-safe manner by referencing the class-string<Resource>. This has the additional benefit of auto-detecting broken internal links through static analysis of the code and makes switching between front-end (HTML-generating) code and back-end code as easy as just following the class reference.

Modules should be able to declare HTTP resources. Each resource can respond differently based on the HTTP method. Supported query or body arguments are declared as function parameters and filled in using reflection. Resources exposed by modules exist in a fixed namespace called `Resources` relative to the `Module` implementation to avoid having to keep track of all possible routes in a big centralized map. The filesystem itself basically becomes the routing table. Every incoming request is mapped to a resource based on implementation-defined (or even user-defined) routing rules. A basic implementation could simply map the URLs' path to a class name. Links to other HTTP resources on the system can be declared in a type-safe manner by referencing the `class-string<Resource>`. This has the additional benefit of auto-detecting broken internal links through static analysis of the code and makes switching between front-end (HTML-generating) code and back-end code as easy as just following the class reference.
c.fahner added this to the v0.1 milestone 2026-04-25 15:03:17 +02:00
c.fahner stopped working 2026-04-25 15:03:36 +02:00
5 seconds
c.fahner deleted spent time 2026-04-25 15:03:45 +02:00
- 5 seconds
c.fahner stopped working 2026-04-25 20:20:17 +02:00
47 minutes 19 seconds
c.fahner added spent time 2026-04-26 07:40:19 +02:00
1 hour
c.fahner canceled time tracking 2026-05-02 14:23:30 +02:00
c.fahner stopped working 2026-05-02 16:29:13 +02:00
1 hour 42 minutes
c.fahner added spent time 2026-05-03 08:30:20 +02:00
45 minutes
c.fahner stopped working 2026-05-03 09:15:57 +02:00
45 minutes 32 seconds
c.fahner added spent time 2026-05-06 19:08:46 +02:00
6 hours
c.fahner canceled time tracking 2026-05-09 09:46:29 +02:00
c.fahner added spent time 2026-05-09 09:46:45 +02:00
6 hours
Author
Owner

Doing a small refactor of the work so far, both to incorporate #14 and to introduce some more parts into the current design.

The current API has no ability to express response variants for the same resource method. This makes it difficult to declare the possible response status codes, content types and body schemas with something like OpenAPI.

The current plan is to remove the #[Method] annotation and turn it into an interface. Classes implementing this interface can then decorate their Response-returning methods with annotations, for example:

class CrudGet implements Method {

  #[Override]
  public function invoke(): Response {
    // logic to decide if respondOk or respondNotFound is going to be called
  }

  #[Content('application/json')] // implied status 200
  private function respondOk(): Response { }

  #[Status(Status::NOT_FOUND), Content('application/json')]
  private function respondNotFound(): Response { }

}

Both the #[Status] and #[Content] annotations should inherit from a common interface to allow for more custom descriptors. Having these descriptors for each possible "branch" of the method will also help with fuzzing. A potential 400 status code is implied for any method that requires at least one argument.

Additionally it should be possible to express that while a Resource implements a certain HTTP method, it might not be available at runtime for all contexts (due to a lack of permissions for example).

Example resource using the planned API design:

class my_resource implements Resource {

  // valid methods are: public + return type is either Method, ?Method or Response
  public function get(string $id): Method {
    return new CrudGet($this->injectedService, $id);
  }

  // this method may be unavailable depending on the context
  // a future version could filter it out of the OpenAPI generator based on an attribute or by implementing an interface
  public function post(#[FromBody] array $values): ?Method {
    return $this->injectedService->canInsert()
      ? new CrudPost($this->injectedService, $values)
      : null;
  }

}
Doing a small refactor of the work so far, both to incorporate #14 and to introduce some more parts into the current design. The current API has no ability to express response variants for the same resource method. This makes it difficult to declare the possible response status codes, content types and body schemas with something like OpenAPI. The current plan is to remove the `#[Method]` annotation and turn it into an interface. Classes implementing this interface can then decorate their `Response`-returning methods with annotations, for example: ```php class CrudGet implements Method { #[Override] public function invoke(): Response { // logic to decide if respondOk or respondNotFound is going to be called } #[Content('application/json')] // implied status 200 private function respondOk(): Response { } #[Status(Status::NOT_FOUND), Content('application/json')] private function respondNotFound(): Response { } } ``` Both the `#[Status]` and `#[Content]` annotations should inherit from a common interface to allow for more custom descriptors. Having these descriptors for each possible "branch" of the method will also help with fuzzing. A potential 400 status code is implied for any method that requires at least one argument. Additionally it should be possible to express that while a `Resource` implements a certain HTTP method, it might not be available at runtime for all contexts (due to a lack of permissions for example). Example resource using the planned API design: ```php class my_resource implements Resource { // valid methods are: public + return type is either Method, ?Method or Response public function get(string $id): Method { return new CrudGet($this->injectedService, $id); } // this method may be unavailable depending on the context // a future version could filter it out of the OpenAPI generator based on an attribute or by implementing an interface public function post(#[FromBody] array $values): ?Method { return $this->injectedService->canInsert() ? new CrudPost($this->injectedService, $values) : null; } } ```
c.fahner stopped working 2026-05-28 08:01:13 +02:00
16 minutes 29 seconds
c.fahner added spent time 2026-05-28 20:43:54 +02:00
2 hours
c.fahner stopped working 2026-05-29 08:14:33 +02:00
32 minutes 31 seconds
c.fahner canceled time tracking 2026-05-30 09:52:11 +02:00
c.fahner added spent time 2026-05-30 09:52:18 +02:00
1 hour
c.fahner canceled time tracking 2026-05-30 19:28:40 +02:00
c.fahner stopped working 2026-05-31 11:06:39 +02:00
2 hours 18 minutes
c.fahner stopped working 2026-05-31 13:59:33 +02:00
1 hour 20 minutes
c.fahner stopped working 2026-05-31 16:47:50 +02:00
2 hours
c.fahner deleted spent time 2026-06-01 19:18:21 +02:00
- 2 hours
c.fahner canceled time tracking 2026-06-01 19:18:33 +02:00
c.fahner added spent time 2026-06-01 19:18:50 +02:00
3 hours 40 minutes
c.fahner stopped working 2026-06-01 21:14:13 +02:00
1 hour 55 minutes
c.fahner added spent time 2026-06-02 19:29:26 +02:00
1 hour 10 minutes
c.fahner added spent time 2026-06-03 18:15:43 +02:00
35 minutes
c.fahner stopped working 2026-06-03 18:54:21 +02:00
38 minutes 35 seconds
c.fahner added spent time 2026-06-04 18:11:07 +02:00
45 minutes
c.fahner stopped working 2026-06-04 19:50:12 +02:00
1 hour 31 minutes
c.fahner added spent time 2026-06-05 18:38:34 +02:00
1 hour 5 minutes
c.fahner stopped working 2026-06-05 19:32:53 +02:00
39 minutes 57 seconds
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Total time spent: 1 day 12 hours
c.fahner
1 day 12 hours
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
slendium/framework#3
No description provided.