| Server IP : 172.67.71.254 / Your IP : 216.73.216.4 Web Server : nginx/1.27.1 System : Linux in-5 5.15.0-143-generic #153-Ubuntu SMP Fri Jun 13 19:10:45 UTC 2025 x86_64 User : arabianexpress ( 1872) PHP Version : 8.0.30 Disable Function : exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /storage/v9321/leapdubai/public_html/wp-content/plugins/kirki/libraries/framework/ |
Upload File : |
<?php
/**
* The IoC (Inversion of Control) service container for the framework.
* Centralizes registration, resolution, and lifecycle management of services
* with automatic dependency injection, singleton binding, and aliasing.
* Acts as the backbone for dependency management across the application.
*
* @package Framework
* @since 1.0.0
*/
namespace Kirki\Framework;
\defined('ABSPATH') || exit;
use Closure;
use Exception;
use ReflectionClass;
use ReflectionParameter;
use TInstance;
use Kirki\Framework\Contracts\Container as ContainerContract;
use LogicException;
use ReflectionNamedType;
class Container implements ContainerContract
{
/**
* The instance of the container.
*
* @var static|null
*
* @since 1.0.0
*/
protected static $instance = null;
/**
* The bindings of the container.
*
* @var array
*
* @since 1.0.0
*/
protected array $bindings = [];
/**
* The instances of the container (singletons).
*
* @var array
*
* @since 1.0.0
*/
protected array $instances = [];
/**
* The aliases for services.
*
* @var array
*
* @since 1.0.0
*/
protected array $aliases = [];
/**
* Services currently being resolved (for circular dependency detection).
*
* @var array
*
* @since 1.0.0
*/
protected array $resolved = [];
/**
* Tagged services.
*
* @var array
*
* @since 1.0.0
*/
protected array $tags = [];
/**
* The base path of the application.
*
* @var string
*
* @since 1.0.0
*/
protected $base_path;
/**
* Create a new container instance.
*
* @param string|null $base_path The base path of the application.
*
* @return void
*
* @since 1.0.0
*/
public function __construct(?string $base_path = null)
{
$this->base_path = $base_path;
}
/**
* Bind a class to the container.
*
* @param string $name The name of the service.
* @param Closure|null $resolver The resolver for the service.
*
* @return void
*
* @since 1.0.0
*/
public function bind(string $name, ?Closure $resolver = null)
{
if (\is_null($resolver)) {
$resolver = function (Container $app, $params) use($name) {
return $app->autowire($name, $params);
};
}
$this->bindings[$name] = ['resolver' => $resolver, 'singleton' => \false];
}
/**
* Bind a class to the container as a singleton (lazy loaded).
*
* @param string $name The name of the service.
* @param Closure|null $resolver The resolver for the service.
*
* @return void
*
* @since 1.0.0
*/
public function singleton(string $name, ?Closure $resolver = null)
{
if (\is_null($resolver)) {
$resolver = function (Container $app, $params) use($name) {
return $app->autowire($name, $params);
};
}
$this->bindings[$name] = ['resolver' => $resolver, 'singleton' => \true];
}
/**
* Bind an existing instance to the container.
*
* @template TInstance of mixed
*
* @param string $abstract The abstract name of the service.
* @param TInstance $instance The instance of the service.
*
* @return TInstance
*
* @since 1.0.0
*/
public function instance(string $abstract, $instance)
{
$bounded = $this->bound($abstract);
unset($this->aliases[$abstract]);
$this->instances[$abstract] = $instance;
if ($bounded) {
$this->rebound($abstract);
}
return $instance;
}
/**
* Create an alias for a service.
*
* @param string $alias The alias name of the service.
* @param string $abstract The abstract name of the service.
*
* @return void
*
* @throws \LogicException
*
* @since 1.0.0
*/
public function alias(string $alias, string $abstract)
{
if ($alias === $abstract) {
throw new LogicException(\sprintf('[%s] is aliased to itself.', $abstract));
}
$this->aliases[$alias] = $abstract;
}
/**
* Tag services for grouped resolution.
*
* @param array $services The services to tag.
* @param string $tag The tag to add the services to.
*
* @return void
*
* @since 1.0.0
*/
public function tag(array $services, string $tag)
{
foreach ($services as $service) {
$this->tags[$tag][] = $service;
}
}
/**
* Get all services with a given tag.
*
* @param string $tag The tag to get the services from.
*
* @return array
*
* @since 1.0.0
*/
public function tagged(string $tag) : array
{
if (!isset($this->tags[$tag])) {
return [];
}
return \array_map([$this, 'make'], $this->tags[$tag]);
}
/**
* Check if a class has been resolved.
*
* @param string $name The name of the service.
*
* @return bool
*
* @since 1.0.0
*/
protected function resolved(string $name) : bool
{
return \in_array($name, $this->resolved, \true);
}
/**
* Make a class from the container.
*
* @param string $name The name of the service.
* @param array $parameters The parameters to pass to the service.
*
* @return mixed
*
* @since 1.0.0
*/
public function make(string $name, array $parameters = [])
{
return $this->resolve($name, $parameters);
}
/**
* Resolve a class from the container.
*
* @param string $name The name of the service.
* @param array $parameters The parameters to pass to the service.
*
* @return mixed
*
* @since 1.0.0
*/
protected function resolve(string $name, array $parameters = [])
{
// Resolve aliases
$name = $this->get_alias($name);
// Return existing singleton instance
if (isset($this->instances[$name])) {
return $this->instances[$name];
}
// Use manual binding if available
if (isset($this->bindings[$name])) {
return $this->resolve_binding($name, $parameters);
}
// Attempt automatic resolution
return $this->autowire($name, $parameters);
}
/**
* Resolve a manually bound service.
*
* @param string $name The name of the service.
* @param array $parameters The parameters to pass to the service.
*
* @return mixed
*
* @since 1.0.0
*/
protected function resolve_binding(string $name, array $parameters = [])
{
$binding = $this->bindings[$name];
$instance = $binding['resolver']($this, $parameters);
// Store singleton instances
if (!empty($binding['singleton'])) {
$this->instances[$name] = $instance;
}
return $instance;
}
/**
* Automatically wire dependencies using reflection.
*
* @param string $class The class to autowire.
* @param array $parameters The parameters to pass to the class.
*
* @return mixed
*
* @throws \Exception
*
* @since 1.0.0
*/
protected function autowire(string $class, array $parameters = [])
{
// Check for circular dependencies
if ($this->resolved($class)) {
$chain = \implode(' → ', $this->resolved) . " → {$class}";
throw new Exception(\sprintf('Circular dependency detected: %s', $chain));
}
$this->resolved[] = $class;
try {
$reflector = new ReflectionClass($class);
if (!$reflector->isInstantiable()) {
throw new Exception(\sprintf('Class "%s" is not instantiable.', $class));
}
$constructor = $reflector->getConstructor();
if (!$constructor) {
return new $class();
}
$dependencies = $this->resolve_dependencies($constructor->getParameters(), $parameters);
return $reflector->newInstanceArgs($dependencies);
} finally {
\array_pop($this->resolved);
}
}
/**
* Resolve constructor dependencies.
*
* @param array $parameters The parameters to pass to the dependencies.
* @param array $primitives The primitives to pass to the dependencies.
*
* @return array
*
* @since 1.0.0
*/
protected function resolve_dependencies(array $parameters, array $primitives = []) : array
{
$dependencies = [];
foreach ($parameters as $parameter) {
$dependency = $parameter->getType();
if ($dependency === null || $dependency->isBuiltin()) {
// Handle primitive parameters
$dependencies[] = $this->resolve_primitive($parameter, $primitives);
} else {
// Handle class parameters
$dependency_name = $dependency instanceof ReflectionNamedType ? $dependency->getName() : (string) $dependency;
$dependencies[] = $this->make($dependency_name);
}
}
return $dependencies;
}
/**
* Resolve primitive parameter.
*
* @param ReflectionParameter $parameter The parameter to resolve.
* @param array $primitives The primitives to pass to the dependencies.
*
* @return mixed
*
* @throws \Exception
*
* @since 1.0.0
*/
protected function resolve_primitive(ReflectionParameter $parameter, array $primitives)
{
$param_name = $parameter->getName();
if (\array_key_exists($param_name, $primitives)) {
return $primitives[$param_name];
}
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
}
throw new Exception(\sprintf('Unable to resolve primitive parameter "%s" in class "%s".', $param_name, $parameter->getDeclaringClass()->getName()));
}
/**
* Get the alias for a service.
*
* @param string $name The name of the service.
*
* @return string
*
* @since 1.0.0
*/
protected function get_alias(string $name) : string
{
return $this->aliases[$name] ?? $name;
}
/**
* Check if a class exists in the container.
*
* @param string $name The name of the service.
*
* @return bool
*
* @since 1.0.0
*/
public function has(string $name) : bool
{
return $this->bound($name);
}
/**
* Check if a name is an alias.
*
* @param string $name The name of the service.
*
* @return bool
*
* @since 1.0.0
*/
public function is_alias(string $name) : bool
{
return isset($this->aliases[$name]);
}
/**
* Check if a name is bound in the container.
*
* @param string $abstract The abstract name of the service.
*
* @return bool
*
* @since 1.0.0
*/
public function bound($abstract)
{
return isset($this->bindings[$abstract]) || isset($this->instances[$abstract]) || $this->is_alias($abstract);
}
/**
* Rebind a name in the container.
*
* @param string $abstract The abstract name of the service.
*
* @return void
*
* @since 1.0.0
*/
public function rebound($abstract)
{
$this->make($abstract);
}
/**
* Forget an instance from the container.
*
* @param string $abstract The abstract name of the service.
*
* @return void
*
* @since 1.0.0
*/
public function forget_instance($abstract)
{
unset($this->instances[$abstract]);
}
/**
* Forget all instances from the container.
*
* @return void
*
* @since 1.0.0
*/
public function forget_instances()
{
$this->instances = [];
}
/**
* Get the instance of the container.
*
* @param string|null $base_path The base path of the application.
*
* @return static
*
* @since 1.0.0
*/
public static function get_instance(?string $base_path = null)
{
if (\is_null(static::$instance)) {
static::$instance = new static($base_path);
}
return static::$instance;
}
/**
* Flush all bindings and instances.
*
* @return void
*
* @since 1.0.0
*/
public function flush()
{
$this->bindings = [];
$this->instances = [];
$this->aliases = [];
$this->resolved = [];
$this->tags = [];
}
}