mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
100 lines
2.4 KiB
PHP
100 lines
2.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Pest\Support;
|
|
|
|
use Pest\Exceptions\ShouldNotHappen;
|
|
use ReflectionClass;
|
|
use ReflectionParameter;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
final class Container
|
|
{
|
|
/**
|
|
* @var self
|
|
*/
|
|
private static $instance;
|
|
|
|
/**
|
|
* @var array<string, mixed>
|
|
*/
|
|
private $instances = [];
|
|
|
|
/**
|
|
* Gets a new or already existing container.
|
|
*/
|
|
public static function getInstance(): self
|
|
{
|
|
if (static::$instance === null) {
|
|
static::$instance = new static();
|
|
}
|
|
|
|
return static::$instance;
|
|
}
|
|
|
|
/**
|
|
* 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));
|
|
}
|
|
}
|