* @license http://www.opensource.org/licenses/mit-license.html MIT License * @version 5.2 * @link https://git.timshome.page/timw4mail/HummingBirdAnimeClient */ namespace Aviat\Ion\Di; use Aviat\Ion\Di\Exception\{ContainerException, NotFoundException}; use Psr\Log\LoggerInterface; /** * Dependency container */ class Container implements ContainerInterface { /** * Array of object instances */ protected array $instances = []; /** * Map of logger instances */ protected array $loggers = []; /** * Constructor * * @param (callable)[] $container (optional) */ public function __construct( /** * Array of container Generator functions */ protected array $container = [] ) { $this->loggers = []; } /** * Finds an entry of the container by its identifier and returns it. * * @param string $id - Identifier of the entry to look for. * * @throws ContainerException - Error while retrieving the entry. * @throws NotFoundException - No entry was found for this identifier. * * @return mixed Entry. */ public function get(string $id): mixed { if ($this->has($id)) { // Return an object instance, if it already exists if (array_key_exists($id, $this->instances)) { return $this->instances[$id]; } // If there isn't already an instance, create one $obj = $this->getNew($id); $this->instances[$id] = $obj; return $obj; } throw new NotFoundException("Item '{$id}' does not exist in container."); } /** * Get a new instance of the specified item * * @param string $id - Identifier of the entry to look for. * @param array|null $args - Optional arguments for the factory callable * @throws ContainerException - Error while retrieving the entry. * @throws NotFoundException - No entry was found for this identifier. */ public function getNew(string $id, ?array $args = NULL): mixed { if ($this->has($id)) { // By default, call a factory with the Container $args = \is_array($args) ? $args : [$this]; $obj = ($this->container[$id])(...$args); // Check for container interface, and apply the container to the object // if applicable return $this->applyContainer($obj); } throw new NotFoundException("Item '{$id}' does not exist in container."); } /** * Add a factory to the container * * @param callable $value - a factory callable for the item */ public function set(string $id, callable $value): ContainerInterface { $this->container[$id] = $value; return $this; } /** * Set a specific instance in the container for an existing factory * * @throws NotFoundException - No entry was found for this identifier. */ public function setInstance(string $id, mixed $value): ContainerInterface { if ( ! $this->has($id)) { throw new NotFoundException("Factory '{$id}' does not exist in container. Set that first."); } $this->instances[$id] = $value; return $this; } /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * @param string $id Identifier of the entry to look for. */ public function has(string $id): bool { return array_key_exists($id, $this->container); } /** * Determine whether a logger channel is registered * * @param string $id The logger channel */ public function hasLogger(string $id = 'default'): bool { return array_key_exists($id, $this->loggers); } /** * Add a logger to the Container * * @param string $id The logger 'channel' */ public function setLogger(LoggerInterface $logger, string $id = 'default'): ContainerInterface { $this->loggers[$id] = $logger; return $this; } /** * Retrieve a logger for the selected channel * * @param string $id The logger to retrieve */ public function getLogger(string $id = 'default'): ?LoggerInterface { return $this->hasLogger($id) ? $this->loggers[$id] : NULL; } /** * Check if object implements ContainerAwareInterface * or uses ContainerAware trait, and if so, apply the container * to that object */ private function applyContainer(mixed $obj): mixed { $traitName = ContainerAware::class; $interfaceName = ContainerAwareInterface::class; $traits = class_uses($obj); $traitsUsed = (is_array($traits)) ? $traits : []; $usesTrait = in_array($traitName, $traitsUsed, TRUE); $interfaces = class_implements($obj); $implemented = (is_array($interfaces)) ? $interfaces : []; $implementsInterface = in_array($interfaceName, $implemented, TRUE); if ($usesTrait || $implementsInterface) { $obj->setContainer($this); } return $obj; } } // End of Container.php