-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c2d5e96
commit bfadd6c
Showing
15 changed files
with
143 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,18 +5,20 @@ | |
namespace BlackBonjour\ServiceManager\AbstractFactory; | ||
|
||
use BlackBonjour\ServiceManager\Exception\ContainerException; | ||
use BlackBonjour\ServiceManager\FactoryInterface; | ||
use Psr\Container\ContainerInterface; | ||
|
||
/** | ||
* @author Erick Dyck <[email protected]> | ||
* @since 18.09.2019 | ||
*/ | ||
class DynamicFactory implements AbstractFactoryInterface | ||
final class DynamicFactory implements AbstractFactoryInterface | ||
{ | ||
/** | ||
* @inheritDoc | ||
* @throws ContainerException | ||
*/ | ||
public function __invoke(ContainerInterface $container, string $service, ?array $options = null) | ||
public function __invoke(ContainerInterface $container, string $service, ?array $options = null): mixed | ||
{ | ||
if ($this->canCreate($container, $service) === false) { | ||
throw new ContainerException(sprintf('Cannot create service "%s"!', $service)); | ||
|
@@ -25,6 +27,11 @@ public function __invoke(ContainerInterface $container, string $service, ?array | |
$factoryClass = $service . 'Factory'; | ||
$factory = new $factoryClass(); | ||
|
||
assert( | ||
$factory instanceof FactoryInterface || is_callable($factory), | ||
sprintf('Dynamic factory "%s" for service "%s" is not callable!', $factoryClass, $service), | ||
); | ||
|
||
return $factory($container, $service, $options); | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ | |
* @author Erick Dyck <[email protected]> | ||
* @since 30.09.2019 | ||
*/ | ||
class ReflectionFactory implements AbstractFactoryInterface | ||
final class ReflectionFactory implements AbstractFactoryInterface | ||
{ | ||
/** | ||
* @inheritDoc | ||
|
@@ -28,19 +28,20 @@ class ReflectionFactory implements AbstractFactoryInterface | |
* @throws NotFoundExceptionInterface | ||
* @throws ReflectionException | ||
*/ | ||
public function __invoke(ContainerInterface $container, string $service, ?array $options = null) | ||
public function __invoke(ContainerInterface $container, string $service, ?array $options = null): mixed | ||
{ | ||
if ($this->canCreate($container, $service) === false) { | ||
throw new ContainerException(sprintf('Cannot create service "%s"!', $service)); | ||
} | ||
|
||
/** @var class-string $service */ | ||
$reflectionClass = new ReflectionClass($service); | ||
$parameters = $reflectionClass->getConstructor()?->getParameters() ?? []; | ||
|
||
if ($parameters) { | ||
$resolvedParameters = array_map( | ||
fn (ReflectionParameter $parameter) => $this->resolveParameter($parameter, $container, $service), | ||
$parameters | ||
fn(ReflectionParameter $parameter): mixed => $this->resolveParameter($parameter, $container, $service), | ||
$parameters, | ||
); | ||
|
||
return new $service(...$resolvedParameters); | ||
|
@@ -70,8 +71,11 @@ public function canCreate(ContainerInterface $container, string $service): bool | |
* @throws NotFoundExceptionInterface | ||
* @throws ReflectionException | ||
*/ | ||
private function resolveParameter(ReflectionParameter $parameter, ContainerInterface $container, string $service) | ||
{ | ||
private function resolveParameter( | ||
ReflectionParameter $parameter, | ||
ContainerInterface $container, | ||
string $service, | ||
): mixed { | ||
$reflectionType = $parameter->getType(); | ||
$type = $reflectionType instanceof ReflectionNamedType | ||
? $reflectionType->getName() | ||
|
@@ -90,8 +94,8 @@ private function resolveParameter(ReflectionParameter $parameter, ContainerInter | |
sprintf( | ||
'Unable to create service "%s": Cannot resolve parameter "%s" to a class or interface!', | ||
$service, | ||
$parameter->getName() | ||
) | ||
$parameter->getName(), | ||
), | ||
); | ||
} | ||
|
||
|
@@ -111,8 +115,8 @@ private function resolveParameter(ReflectionParameter $parameter, ContainerInter | |
'Unable to create service "%s": Cannot resolve parameter "%s" using type hint "%s"!', | ||
$service, | ||
$parameter->getName(), | ||
$type | ||
) | ||
$type, | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,4 @@ | |
* @author Erick Dyck <[email protected]> | ||
* @since 13.05.2019 | ||
*/ | ||
class ContainerException extends Error implements ContainerExceptionInterface | ||
{ | ||
} | ||
class ContainerException extends Error implements ContainerExceptionInterface {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,4 @@ | |
* @author Erick Dyck <[email protected]> | ||
* @since 13.05.2019 | ||
*/ | ||
class NotFoundException extends Error implements NotFoundExceptionInterface | ||
{ | ||
} | ||
class NotFoundException extends Error implements NotFoundExceptionInterface {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,7 +11,7 @@ | |
* @author Erick Dyck <[email protected]> | ||
* @since 23.02.2023 | ||
*/ | ||
class InvokableFactory implements FactoryInterface | ||
final class InvokableFactory implements FactoryInterface | ||
{ | ||
/** | ||
* @inheritDoc | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,63 +19,123 @@ | |
/** | ||
* @author Erick Dyck <[email protected]> | ||
* @since 13.05.2019 | ||
* | ||
* @implements ArrayAccess<string, mixed> | ||
*/ | ||
class ServiceManager implements ArrayAccess, ContainerInterface | ||
{ | ||
/** @var array<string, string> */ | ||
/** @var array<AbstractFactoryInterface> */ | ||
private array $abstractFactories; | ||
|
||
/** @var array<string, FactoryInterface|callable|class-string> */ | ||
private array $factories; | ||
|
||
/** @var array<class-string, class-string> */ | ||
private array $invokables; | ||
|
||
/** @var FactoryInterface[]|callable[] */ | ||
/** @var array<string, FactoryInterface|callable> */ | ||
private array $resolvedFactories = []; | ||
|
||
/** @var array<string, mixed> */ | ||
private array $resolvedServices = []; | ||
|
||
/** @var array<string, mixed> */ | ||
private array $services; | ||
|
||
/** | ||
* @param FactoryInterface[]|callable[]|string[] $factories | ||
* @param AbstractFactoryInterface[] $abstractFactories | ||
* @param string[] $invokables | ||
* @param array<string, mixed> $services | ||
* @param array<string, FactoryInterface|callable|class-string> $factories | ||
* @param array<AbstractFactoryInterface> $abstractFactories | ||
* @param array<string|int, class-string> $invokables | ||
*/ | ||
public function __construct( | ||
private array $services = [], | ||
private array $factories = [], | ||
private array $abstractFactories = [], | ||
array $services = [], | ||
array $factories = [], | ||
array $abstractFactories = [], | ||
array $invokables = [], | ||
) { | ||
$this->invokables = array_combine($invokables, $invokables); | ||
// Validate services | ||
foreach (array_keys($services) as $id) { | ||
assert(is_string($id), sprintf('Service ID must be a string, "%s" given!', $id)); | ||
} | ||
|
||
// Validate factories | ||
foreach ($factories as $id => $factory) { | ||
assert(is_string($id), sprintf('Service ID must be a string, "%s" given!', $id)); | ||
assert( | ||
$factory instanceof FactoryInterface | ||
|| is_callable($factory) | ||
|| (is_string($factory) && class_exists($factory)), | ||
sprintf('Invalid factory provided for service "%s"!', $id), | ||
); | ||
} | ||
|
||
// Validate abstract factories | ||
foreach ($abstractFactories as $abstractFactory) { | ||
assert( | ||
$abstractFactory instanceof AbstractFactoryInterface, | ||
sprintf('Abstract factories must implement %s!', AbstractFactoryInterface::class), | ||
); | ||
} | ||
|
||
// Validate invokable classes | ||
foreach ($invokables as $invokable) { | ||
assert( | ||
is_string($invokable) && class_exists($invokable), | ||
sprintf('Invokable class "%s" does not exist!', $invokable), | ||
); | ||
} | ||
|
||
// Set properties | ||
$this->abstractFactories = $abstractFactories; | ||
$this->factories = $factories; | ||
$this->invokables = array_combine($invokables, $invokables); | ||
$this->services = $services; | ||
} | ||
|
||
public function addAbstractFactory(AbstractFactoryInterface $abstractFactory): void | ||
{ | ||
$this->abstractFactories[] = $abstractFactory; | ||
} | ||
|
||
public function addFactory(string $id, string|callable $factory): void | ||
public function addFactory(string $id, FactoryInterface|callable|string $factory): void | ||
{ | ||
if (is_string($factory) && class_exists($factory) === false) { | ||
throw new ContainerException(sprintf('Factory "%s" does not exist!', $factory)); | ||
} | ||
|
||
$this->factories[$id] = $factory; | ||
} | ||
|
||
public function addInvokable(string $id): void | ||
{ | ||
$this->invokables[$id] ??= $id; | ||
if (class_exists($id) === false) { | ||
throw new ContainerException(sprintf('Class "%s" does not exist!', $id)); | ||
} | ||
|
||
$this->invokables[$id] = $id; | ||
} | ||
|
||
public function addService(string $id, $service): void | ||
public function addService(string $id, mixed $service): void | ||
{ | ||
$this->services[$id] = $service; | ||
} | ||
|
||
/** | ||
* @param array<string|int, mixed>|null $options | ||
* | ||
* @throws ContainerException | ||
*/ | ||
public function createService(string $id, ?array $options = null) | ||
public function createService(string $id, ?array $options = null): mixed | ||
{ | ||
try { | ||
return $this->getFactory($id)($this, $id, $options); | ||
} catch (Throwable $t) { | ||
throw new ContainerException(sprintf('Service "%s" could not be created!', $id), 0, $t); | ||
throw new ContainerException(sprintf('Service "%s" could not be created!', $id), previous: $t); | ||
} | ||
} | ||
|
||
public function get(string $id) | ||
public function get(string $id): mixed | ||
{ | ||
if (array_key_exists($id, $this->services)) { | ||
return $this->services[$id]; | ||
|
@@ -108,6 +168,8 @@ public function has(string $id): bool | |
|
||
public function offsetExists(mixed $offset): bool | ||
{ | ||
assert(is_string($offset), 'Service ID must be of type string!'); | ||
|
||
return $this->has($offset); | ||
} | ||
|
||
|
@@ -117,16 +179,22 @@ public function offsetExists(mixed $offset): bool | |
*/ | ||
public function offsetGet(mixed $offset): mixed | ||
{ | ||
assert(is_string($offset), 'Service ID must be of type string!'); | ||
|
||
return $this->get($offset); | ||
} | ||
|
||
public function offsetSet(mixed $offset, mixed $value): void | ||
{ | ||
assert(is_string($offset), 'Service ID must be of type string!'); | ||
|
||
$this->addService($offset, $value); | ||
} | ||
|
||
public function offsetUnset(mixed $offset): void | ||
{ | ||
assert(is_string($offset), 'Service ID must be of type string!'); | ||
|
||
$this->removeService($offset); | ||
} | ||
|
||
|
@@ -137,7 +205,7 @@ public function removeService(string $id): void | |
$this->invokables[$id], | ||
$this->resolvedFactories[$id], | ||
$this->resolvedServices[$id], | ||
$this->services[$id] | ||
$this->services[$id], | ||
); | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,12 +8,11 @@ | |
* @author Erick Dyck <[email protected]> | ||
* @since 30.09.2019 | ||
*/ | ||
class ClassWithoutFactory | ||
final readonly class ClassWithoutFactory | ||
{ | ||
public function __construct( | ||
public readonly FooBar $foo, | ||
public readonly array $bar, | ||
public readonly int $baz = 123, | ||
) { | ||
} | ||
public FooBar $foo, | ||
public array $bar, | ||
public int $baz = 123, | ||
) {} | ||
} |
Oops, something went wrong.