Skip to content

Instantly share code, notes, and snippets.

@audinue
Created April 3, 2025 21:56
Show Gist options
  • Save audinue/5c66a31b455d50a2ee5cea449fe58ff5 to your computer and use it in GitHub Desktop.
Save audinue/5c66a31b455d50a2ee5cea449fe58ff5 to your computer and use it in GitHub Desktop.
<?php
class Container
{
/**
* @var array<string, mixed>
*/
private $values;
/**
* @var array<string, callable(Container $container): mixed>
*/
private $moduleFactories;
/**
* @var array<string, mixed>
*/
private $moduleCache;
/**
* @var array<string, callable(Container $container, mixed ...$args): mixed>
*/
private $objectFactories;
/**
* @var array<string, callable(Container $container, mixed ...$args): mixed>
*/
private $objectFactoryCache;
/**
* @param string $key
* @return mixed
* @throws \Exception When the value is undefined.
*/
function getValue($key)
{
if (!isset($this->values[$key])) {
throw new \Exception("Undefined value for $key");
}
return $this->values[$key];
}
/**
* @param string $key
* @param mixed $value
* @return self
*/
function setValue($key, $value)
{
$this->values[$key] = $value;
return $this;
}
/**
* @template T
* @param class-string<T> $key
* @return callable(Container $container): T
* @throws \Exception When the module factory is undefined.
*/
function getModuleFactory($key)
{
foreach ($this->moduleFactories as $moduleFactoryKey => $moduleFactory) {
if (is_a($moduleFactoryKey, $key, true)) {
return $moduleFactory;
}
}
throw new \Exception("Undefined module factory for $key");
}
/**
* @template T
* @param class-string<T> $key
* @param callable(Container $container): T $moduleFactory
* @return self
*/
function setModuleFactory($key, $moduleFactory)
{
$this->moduleFactories[$key] = $moduleFactory;
return $this;
}
/**
* @template T
* @param class-string<T> $key
* @return callable(Container $container, mixed ...$args): T
* @throws \Exception When the object factory is undefined.
*/
function getObjectFactory($key)
{
foreach ($this->objectFactories as $objectFactoryKey => $objectFactory) {
if (is_a($objectFactoryKey, $key, true)) {
return $objectFactory;
}
}
throw new \Exception("Undefined object factory for $key");
}
/**
* @template T
* @param class-string<T> $key
* @param callable(Container $container, mixed ...$args): T $objectFactory
* @return self
*/
function setObjectFactory($key, $objectFactory)
{
$this->objectFactories[$key] = $objectFactory;
return $this;
}
/**
* @template T
* @param class-string<T> $key
* @return T
* @throws \Exception When the module factory is undefined.
*/
function getModule($key)
{
$module = $this->moduleCache[$key] ?? null;
if ($module == null) {
$moduleFactory = $this->getModuleFactory($key);
$module = $moduleFactory($this);
$this->moduleCache[$key] = $module;
}
return $module;
}
/**
* @template T
* @param class-string<T> $key
* @param mixed... $args
* @return T
* @throws \Exception When the object factory is undefined.
*/
function createObject($key, ...$args)
{
$objectFactory = $this->objectFactoryCache[$key] ?? null;
if ($objectFactory == null) {
$objectFactory = $this->getObjectFactory($key);
$this->objectFactoryCache[$key] = $objectFactory;
}
return $objectFactory($this, ...$args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment