mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
127 lines
3.4 KiB
PHP
127 lines
3.4 KiB
PHP
|
|
<?php
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Fendx\Core\Container;
|
||
|
|
|
||
|
|
use Fendx\Common\Exception\BusinessException;
|
||
|
|
use ReflectionClass;
|
||
|
|
use ReflectionException;
|
||
|
|
|
||
|
|
final class Container
|
||
|
|
{
|
||
|
|
private static ?Container $instance = null;
|
||
|
|
private array $bindings = [];
|
||
|
|
private array $instances = [];
|
||
|
|
private array $singletons = [];
|
||
|
|
|
||
|
|
private function __construct()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
public static function getInstance(): self
|
||
|
|
{
|
||
|
|
if (self::$instance === null) {
|
||
|
|
self::$instance = new self();
|
||
|
|
}
|
||
|
|
return self::$instance;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function bind(string $abstract, mixed $concrete = null, bool $singleton = false): void
|
||
|
|
{
|
||
|
|
if ($concrete === null) {
|
||
|
|
$concrete = $abstract;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->bindings[$abstract] = $concrete;
|
||
|
|
$this->singletons[$abstract] = $singleton;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function singleton(string $abstract, mixed $concrete = null): void
|
||
|
|
{
|
||
|
|
$this->bind($abstract, $concrete, true);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function make(string $abstract, array $parameters = []): mixed
|
||
|
|
{
|
||
|
|
// 如果已存在实例且为单例
|
||
|
|
if (isset($this->instances[$abstract])) {
|
||
|
|
return $this->instances[$abstract];
|
||
|
|
}
|
||
|
|
|
||
|
|
$concrete = $this->bindings[$abstract] ?? $abstract;
|
||
|
|
|
||
|
|
// 如果是闭包
|
||
|
|
if ($concrete instanceof \Closure) {
|
||
|
|
$object = $concrete($this, $parameters);
|
||
|
|
} else {
|
||
|
|
$object = $this->build($concrete, $parameters);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果是单例,缓存实例
|
||
|
|
if (isset($this->singletons[$abstract]) && $this->singletons[$abstract]) {
|
||
|
|
$this->instances[$abstract] = $object;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $object;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function build(string $concrete, array $parameters): mixed
|
||
|
|
{
|
||
|
|
try {
|
||
|
|
$reflector = new ReflectionClass($concrete);
|
||
|
|
} catch (ReflectionException $e) {
|
||
|
|
throw new BusinessException(500, 'CLASS_NOT_FOUND', ['class' => $concrete]);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!$reflector->isInstantiable()) {
|
||
|
|
throw new BusinessException(500, 'CLASS_NOT_INSTANTIABLE', ['class' => $concrete]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$constructor = $reflector->getConstructor();
|
||
|
|
|
||
|
|
if ($constructor === null) {
|
||
|
|
return new $concrete();
|
||
|
|
}
|
||
|
|
|
||
|
|
$dependencies = $constructor->getParameters();
|
||
|
|
$instances = $this->getDependencies($dependencies, $parameters);
|
||
|
|
|
||
|
|
return $reflector->newInstanceArgs($instances);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function getDependencies(array $parameters, array $primitives): array
|
||
|
|
{
|
||
|
|
$dependencies = [];
|
||
|
|
|
||
|
|
foreach ($parameters as $parameter) {
|
||
|
|
$type = $parameter->getType();
|
||
|
|
|
||
|
|
if ($type === null || $type->isBuiltin()) {
|
||
|
|
$name = $parameter->getName();
|
||
|
|
$dependencies[] = $primitives[$name] ?? $parameter->getDefaultValue();
|
||
|
|
} else {
|
||
|
|
$dependencies[] = $this->make($type->getName());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $dependencies;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function has(string $abstract): bool
|
||
|
|
{
|
||
|
|
return isset($this->bindings[$abstract]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function forget(string $abstract): void
|
||
|
|
{
|
||
|
|
unset($this->bindings[$abstract], $this->instances[$abstract], $this->singletons[$abstract]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function flush(): void
|
||
|
|
{
|
||
|
|
$this->bindings = [];
|
||
|
|
$this->instances = [];
|
||
|
|
$this->singletons = [];
|
||
|
|
}
|
||
|
|
}
|