Dependency injection #4

Merged
c.fahner merged 3 commits from issues/2 into main 2026-05-01 20:41:32 +02:00
Owner

Implements slendium/framework#2.

Adds the InternalAssembler, which is the dependency injection implementation for this framework.

Implements slendium/framework#2. Adds the `InternalAssembler`, which is the dependency injection implementation for this framework.
c.fahner changed title from WIP: Issue 2 to WIP: 2 - Dependency injection 2026-04-25 19:02:59 +02:00
@ -0,0 +13,4 @@
* @copyright Slendium 2026
*/
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final readonly class ImplementedBy {
Author
Owner

Separate into ImplementedBy (requires a class-string) and MaybeImplementedBy (any string) to make explicit which implementations MUST exist and which ones MAY be provided by other libraries.

Separate into `ImplementedBy` (requires a `class-string`) and `MaybeImplementedBy` (any `string`) to make explicit which implementations MUST exist and which ones MAY be provided by other libraries.
Author
Owner

Alternatively, every module type could be annotated with a list of service interfaces mapped to their implementation types.

Pros:

  • Avoids having to awkwardly put service implementations in namespaces defined from other code. The framework's UserService interface could, for example, point to Slendium\Framework\Services\Users\UserServiceImpl, which would have to be added to the composer file of any implementing package.
  • Inactive modules cannot be accessed accidentally, as they are not analyzed

Cons:

  • Every module has to be reflected every request
  • All mappings have to be in-memory on every request
  • Bureaucracy of maintaining a list of provided services for every module

The ImplementedBy model is a bit awkward in regards to namespacing, but constructs implementations on-demand.

Alternatively, every module type could be annotated with a list of service interfaces mapped to their implementation types. Pros: * Avoids having to awkwardly put service implementations in namespaces defined from other code. The framework's `UserService` interface could, for example, point to `Slendium\Framework\Services\Users\UserServiceImpl`, which would have to be added to the composer file of any implementing package. * Inactive modules cannot be accessed accidentally, as they are not analyzed Cons: * Every module has to be reflected every request * All mappings have to be in-memory on every request * Bureaucracy of maintaining a list of provided services for every module The `ImplementedBy` model is a bit awkward in regards to namespacing, but constructs implementations on-demand.
Author
Owner

Going for the ImplementedBy model without the maybe-variant for now. It should be possible to provide DependencyResolver's from the configs, which will override any ImplementedBy attribute.

Additionally, the framework does not even have to provide a UserService if it simply provides the ability to intercept requests. An Auth module could be provided from the framework package itself which handles IP-address, origin and user validation. The default RequestInterceptor could be marked ImplementedBy for a class in this framework-provided module. If the module is inactive, this implementation won't be available and requests won't be intercepted. Using a custom RequestInterceptor would then require declaring an override using a resolver in the configs.

Going for the `ImplementedBy` model without the maybe-variant for now. It should be possible to provide `DependencyResolver`'s from the configs, which will override any `ImplementedBy` attribute. Additionally, the framework does not even have to provide a `UserService` if it simply provides the ability to intercept requests. An `Auth` module could be provided from the framework package itself which handles IP-address, origin and user validation. The default `RequestInterceptor` could be marked `ImplementedBy` for a class in this framework-provided module. If the module is inactive, this implementation won't be available and requests won't be intercepted. Using a custom `RequestInterceptor` would then require declaring an override using a resolver in the configs.
c.fahner marked this conversation as resolved
c.fahner added this to the v0.1 milestone 2026-05-01 15:57:30 +02:00
@ -0,0 +10,4 @@
interface DependencyResolver {
/**
* All dependent types satisfied by this resolver.
Author
Owner

"An intersection of types satisfied by this resolver."

"An intersection of types satisfied by this resolver."
c.fahner marked this conversation as resolved
@ -0,0 +16,4 @@
final readonly class SingletonResolver implements DependencyResolver {
#[Override]
public array $satisfies;
Author
Owner

Missing iterable value?

Edit: apparently this works on non-promoted properties only.

Missing iterable value? *Edit: apparently this works on non-promoted properties only.*
c.fahner marked this conversation as resolved
c.fahner stopped working 2026-05-01 17:49:12 +02:00
1 hour 51 minutes
c.fahner deleted spent time 2026-05-01 17:49:34 +02:00
- 1 hour 51 minutes
c.fahner added spent time 2026-05-01 17:49:51 +02:00
31 minutes
c.fahner changed title from WIP: 2 - Dependency injection to 2 - Dependency injection 2026-05-01 20:40:15 +02:00
c.fahner changed title from 2 - Dependency injection to Dependency injection 2026-05-01 20:40:56 +02:00
c.fahner deleted branch issues/2 2026-05-01 20:41:32 +02:00
c.fahner referenced this pull request from a commit 2026-05-01 20:41:33 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Total time spent: 31 minutes
c.fahner
31 minutes
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
slendium/framework!4
No description provided.