#27 - Simple Dependency Injection in PHP
Date: 2018-09-22 12:00 - PHP
Simple class to create instances of classes dynamically with dependency injection.
<?php
class Services {
private static $resolvers = array();
private static $servicesInstances = array();
private static $resolvingNow = array();
public static function createInstance($class) {
if (isset(self::$resolvers[$class]))
return (self::$resolvers[$class])();
return self::createInstanceClass($class);
}
public static function register($class, $instance) {
if (is_callable($instance))
self::$servicesInstances[$class] = $instance();
else
self::$servicesInstances[$class] = $instance;
}
private static function createInstanceClass($class) {
$reflection_class = new ReflectionClass($class);
$constructor = $reflection_class->getConstructor();
if ($constructor != null) {
$values = array_map(function(ReflectionParameter $parameter) use($class) {
$type = $parameter->getType();
if ($type == null) {
$name = $parameter->getName();
trigger_error("Cannot construct service '$class', because '$name' is of unknown type. Please implement custom resolver or specify a type.", E_USER_ERROR);
}
if ($type->isBuiltin()) {
$name = $parameter->getName();
trigger_error("Cannot construct service '$class', because '$name' is a built-in type. Please implement a custom resolver.", E_USER_ERROR);
}
if (!is_a($type, 'ReflectionNamedType')) {
$name = $parameter->getName();
trigger_error("Cannot construct service '$class', because '$name' is not of a named type. Please implement a custom resolver or update PHP version to 7.1 if your type is named.", E_USER_ERROR);
}
return self::resolve($type->getName());
}, $constructor->getParameters());
return new $class(...$values);
} else
return new $class();
}
private static function createInstanceService($class) {
if (isset(self::$resolvers[$class]))
return (self::$resolvers[$class])();
$instance = self::createInstanceClass($class);
return self::$servicesInstances[$class] = $instance;
}
public static function resolve($class) {
if (in_array($class, self::$resolvingNow))
trigger_error('Cannot handle cyclic dependency injection. Cycle: ' . join(' -> ', array_merge(self::$resolvingNow, [$class])), E_USER_ERROR);
array_push(self::$resolvingNow, $class);
if (isset(self::$servicesInstances[$class]))
return self::$servicesInstances[$class];
if (isset(self::$resolvers[$class]))
return (self::$resolvers[$class])();
if (strpos($class, __SERVICES_NAMESPACE__ . '\\') !== 0)
trigger_error("Cannot resolve '$class' automatically. Please make sure it is a service in the '" . __SERVICES_NAMESPACE__ . "' namespace, or implement a custom resolver.", E_USER_ERROR);
$service_name = str_replace('\\', '/', substr($class, strlen(__SERVICES_NAMESPACE__ . '\\'))) . '.php';
$service = __SERVICES_DIR__ . "/$service_name";
if (!file_exists($service))
trigger_error("File '$service' does not exist. Cannot include service automatically.", E_USER_ERROR);
include_once $service;
if (!class_exists($class))
trigger_error("Service '$class' was not declared in the '$service' file.", E_USER_ERROR);
$instance = self::createInstanceService($class);
array_pop(self::$resolvingNow);
return $instance;
}
}