Add basic container implementation

This commit is contained in:
johannes.pichler
2020-06-03 22:08:25 +02:00
parent e8d426f574
commit b20f208b55
4 changed files with 170 additions and 0 deletions

82
src/Support/Container.php Normal file
View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Pest\Support;
use Pest\Exceptions\ShouldNotHappen;
use ReflectionClass;
use ReflectionParameter;
/**
* @internal
*/
final class Container
{
/**
* @var array<string, mixed>
*/
private $instances = [];
/**
* Gets a dependency from the container.
*
* @return object
*/
public function get(string $id)
{
if (array_key_exists($id, $this->instances)) {
return $this->instances[$id];
}
$this->instances[$id] = $this->build($id);
return $this->instances[$id];
}
/**
* Adds the given instance to the container.
*
* @param mixed $instance
*/
public function add(string $id, $instance): void
{
$this->instances[$id] = $instance;
}
/**
* Tries to build the given instance.
*/
private function build(string $id): object
{
/** @phpstan-ignore-next-line */
$reflectionClass = new ReflectionClass($id);
if ($reflectionClass->isInstantiable()) {
$constructor = $reflectionClass->getConstructor();
if ($constructor !== null) {
$params = array_map(
function (ReflectionParameter $param) use ($id) {
$candidate = null;
if ($param->getType() !== null && $param->getType()->isBuiltin()) {
$candidate = $param->getName();
} elseif ($param->getClass() !== null) {
$candidate = $param->getClass()->getName();
} else {
throw ShouldNotHappen::fromMessage(sprintf('The type of `$%s` in `%s` cannot be determined.', $id, $param->getName()));
}
return $this->get($candidate);
},
$constructor->getParameters()
);
return $reflectionClass->newInstanceArgs($params);
}
}
throw ShouldNotHappen::fromMessage(sprintf('A dependency with the name `%s` cannot be resolved.', $id));
}
}