Files
scoutiq/app/Core/Container.php
2026-06-05 02:05:17 +03:00

118 lines
3.1 KiB
PHP

<?php
namespace App\Core;
use Psr\Container\ContainerInterface;
use Exception;
class Container implements ContainerInterface
{
private array $bindings = [];
private array $instances = [];
/**
* Bind a dependency to the container.
*/
public function bind(string $id, callable|string $concrete, bool $singleton = false): void
{
$this->bindings[$id] = [
'concrete' => $concrete,
'singleton' => $singleton
];
}
/**
* Bind a singleton dependency.
*/
public function singleton(string $id, callable|string $concrete): void
{
$this->bind($id, $concrete, true);
}
/**
* Get a dependency from the container.
*/
public function get(string $id): mixed
{
if (isset($this->instances[$id])) {
return $this->instances[$id];
}
if (isset($this->bindings[$id])) {
$binding = $this->bindings[$id];
$concrete = $binding['concrete'];
$object = $this->resolve($concrete);
if ($binding['singleton']) {
$this->instances[$id] = $object;
}
return $object;
}
// Not bound, auto-resolve using Reflection
return $this->resolve($id);
}
/**
* Check if a dependency is bound.
*/
public function has(string $id): bool
{
return isset($this->bindings[$id]) || isset($this->instances[$id]) || class_exists($id);
}
/**
* Resolve a concrete class or binding.
*/
private function resolve(mixed $concrete): mixed
{
if ($concrete instanceof \Closure || is_callable($concrete)) {
return $concrete($this);
}
if (is_string($concrete)) {
if (!class_exists($concrete)) {
return $concrete;
}
$reflector = new \ReflectionClass($concrete);
if (!$reflector->isInstantiable()) {
throw new Exception("Class {$concrete} is not instantiable.");
}
$constructor = $reflector->getConstructor();
if (null === $constructor) {
return new $concrete();
}
$parameters = $constructor->getParameters();
$dependencies = [];
foreach ($parameters as $parameter) {
$type = $parameter->getType();
if (!$type) {
if ($parameter->isDefaultValueAvailable()) {
$dependencies[] = $parameter->getDefaultValue();
} else {
throw new Exception("Cannot resolve parameter {$parameter->getName()} in class {$concrete}");
}
} elseif ($type instanceof \ReflectionUnionType) {
throw new Exception("Union types in constructor injection not supported for class {$concrete}");
} else {
$typeName = $type->getName();
$dependencies[] = $this->get($typeName);
}
}
return $reflector->newInstanceArgs($dependencies);
}
return $concrete;
}
}