Skip to content

Instantly share code, notes, and snippets.

@diloabininyeri
Created January 21, 2025 22:14
Show Gist options
  • Save diloabininyeri/2c8a9d8e5a43a1211fa8729a0bb7b634 to your computer and use it in GitHub Desktop.
Save diloabininyeri/2c8a9d8e5a43a1211fa8729a0bb7b634 to your computer and use it in GitHub Desktop.
php conxtual container
<?php
class Container
{
/**
* @var array
*/
private array $bindings = [];
/**
* @var array
*/
private array $context = [];
/**
* @param string $name
* @param callable|string $concrete
* @return void
*/
public function bind(string $name, callable|string $concrete): void
{
if (!is_callable($concrete)) {
$concrete = static fn(Container $container) => $container->resolve($concrete);
}
$this->bindings[$name] = $concrete;
}
/**
* @param string $name
* @return mixed
* @throws ReflectionException
*/
public function make(string $name): mixed
{
if (!$this->has($name)) {
return $this->resolve($name);
}
return $this->bindings[$name]($this);
}
/**
* @param string $name
* @return bool
*/
public function has(string $name): bool
{
return isset($this->bindings[$name]);
}
/**
* @param string $scope
* @param string $implementation
* @param string $concrete
* @return void
*/
public function addContext(string $scope, string $implementation, string $concrete): void
{
$this->context[$scope][$implementation] = static fn(Container $container) => $container->resolve($concrete);
}
/***
* @param string $class
* @return object
* @throws ReflectionException
*/
public function resolve(string $class): object
{
$reflectionClass = new ReflectionClass($class);
$constructor = $reflectionClass->getConstructor();
if ($constructor === null) {
return new $class();
}
return $reflectionClass->newInstanceArgs(
array_map(
fn(ReflectionParameter $parameter) => $this->resolveParameter($parameter, $class),
$constructor->getParameters()
)
);
}
/**
* @param string $class
* @param string $interface
* @return bool
*/
private function hasContext(string $class, string $interface): bool
{
return isset($this->context[$class][$interface]);
}
/**
* @param string $class
* @param string $interface
* @return callable
*/
private function getContext(string $class, string $interface): callable
{
return $this->context[$class][$interface];
}
/**
* @param ReflectionParameter $parameter
* @param string $class
* @return mixed
* @throws ReflectionException
*/
private function resolveParameter(ReflectionParameter $parameter, string $class): mixed
{
$interface = $parameter->getType()?->getName();
if ($this->hasContext($class, $interface)) {
return $this->getContext($class, $interface)($this);
}
return $this->make($interface);
}
}
/**
*
*/
class User implements UserInterface
{
}
/**
*
*/
interface UserInterface
{
}
/**
*
*/
class Book implements BookInterface
{
/**
* @param UserInterface $user
*/
public function __construct(private UserInterface $user)
{
}
}
/**
*
*/
interface BookInterface
{
}
/**
*
*/
class Author
{
/**
* @param BookInterface $book
*/
public function __construct(private BookInterface $book)
{
}
}
/**
*
*/
interface LoggerInterface
{
/**
* @return void
*/
public function log(): void;
}
/**
*
*/
class FileLogger implements LoggerInterface
{
/**
* @return void
*/
public function log(): void
{
echo __CLASS__ . PHP_EOL;
}
}
/**
*
*/
class DatabaseLogger implements LoggerInterface
{
/**
* @return void
*/
public function log(): void
{
echo __CLASS__ . PHP_EOL;
}
}
/**
*
*/
readonly class PhpBook
{
/**
* @param LoggerInterface $logger
* @param UserInterface $user
*/
public function __construct(private LoggerInterface $logger, private UserInterface $user)
{
$this->logger->log();
var_dump($this->user);
}
}
/**
*
*/
class Test
{
/**
* @param LoggerInterface $logger
*/
public function __construct(private LoggerInterface $logger)
{
$this->logger->log();
}
}
$container = new Container();
$container->bind(UserInterface::class, User::class);
$container->bind(BookInterface::class, Book::class);
$container->bind(LoggerInterface::class, FileLogger::class);
$container->addContext(PhpBook::class, LoggerInterface::class, DatabaseLogger::class);
$container->addContext(Test::class, LoggerInterface::class, FileLogger::class);
$book = $container->make(PhpBook::class);
$test = $container->make(Test::class);
var_dump($book);
var_dump($test);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment