Skip to content

Instantly share code, notes, and snippets.

@mindplay-dk
Created March 23, 2024 12:12
Show Gist options
  • Save mindplay-dk/6118034336e32376c62c6ca5f28b9470 to your computer and use it in GitHub Desktop.
Save mindplay-dk/6118034336e32376c62c6ca5f28b9470 to your computer and use it in GitHub Desktop.
Declarative service providers
<?php
// API:
/**
* Defines a service instance to be created by calling a constructor.
*/
class Service
{
public function __construct(
/**
* @var string $className the class name of the service.
*/
public readonly string $className,
/**
* @var array<string> $params keys of constructor parameter service names or values.
*/
public readonly array $params = []
) {}
}
/**
* Defines a service instance to be created by calling a factory method of a service.
*/
class Factory
{
public function __construct(
/**
* @var string $key the key of the factory service to call
*/
public readonly Closure $factory,
/**
* @var string $methodName the method name to call on the factory service.
*/
public readonly string $methodName,
/**
* @var array<string> $params keys of method parameter service names or values.
*/
public readonly array $params = []
) {}
}
/**
* Defines a service instance as a serializable value.
*/
class Value
{
public function __construct(
/**
* @var string|bool|int|float|null|array<string|bool|int|float|null> $value the value to use.
*/
public readonly string|bool|int|float|null|array $value
) {}
}
/**
* Defines a service as a list of references to other services, factories, values or lists.
*/
class Refs
{
public function __construct(
/**
* @var array<Service|Factory|Value|Refs> $items the list of services.
*/
public readonly array $items
) {}
}
/**
* An extension takes an existing `Service|Factory|Value` definition and returns a replacement.
*/
class Extension
{
public function __construct(
/**
* @var Closure(Service|Factory|Value):Service|Factory|Value $extend a function that takes a service and returns a new service.
*/
public readonly Closure $extend
) {}
}
interface ServiceProviderInterface
{
/**
* Returns an array of services to be registered in the container.
*
* @return array<string, Service|Factory|Value>
*/
public function getServices(): array;
/**
* Returns an array of extensions to be registered in the container.
*
* @return array<string, Extension>
*/
public function getExtensions(): array;
}
// EXAMPLE:
class UserProvider implements ServiceProviderInterface
{
public function __construct(
private string $cache_path
) {}
public function getServices(): array
{
return [
"cache_path" => Value($this->cache_path),
CacheProvider::class => new Service(FileCache::class, ["cache_path"]),
UserRepository::class => new Service(UserRepository::class, [CacheProvider::class]),
"example" => new Value(1),
"loggers" => new Refs([
new Service(Logger1::class),
new Service(Logger2::class),
])
];
}
public function getExtensions(): array
{
return [
"example" => new Extension(fn(Value $service) => new Value($service->value + 1)),
"loggers" => new Extension(fn(Refs $refs) => new Refs([...$refs->items, new Service(Logger3::class)]))
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment