Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
build:
environment:
php:
version: 7.4
dependencies:
before:
- curl -sS https://getcomposer.org/installer | php -- --2
- mv composer.phar composer
- export PATH="$PWD:$PATH"
tests:
override:
- php-scrutinizer-run
114 changes: 74 additions & 40 deletions src/Di/Di.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @author Arman Ag. <arman.ag@softberg.org>
* @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
* @link http://quantum.softberg.org/
* @since 2.9.7
* @since 2.9.8
*/

namespace Quantum\Di;
Expand Down Expand Up @@ -39,47 +39,56 @@ class Di
*/
private static $container = [];

/**
* @var array
*/
private static $resolving = [];

/**
* Loads dependency definitions
*/
public static function registerDependencies()
{
foreach (self::coreDependencies() as $dependency) {
if (!self::isRegistered($dependency)) {
self::register($dependency);
}
}

foreach (self::userDependencies() as $dependency) {
if (!self::isRegistered($dependency)) {
self::register($dependency);
foreach (self::coreDependencies() + self::userDependencies() as $abstract => $concrete) {
if (!self::isRegistered($abstract)) {
self::register($concrete, $abstract);
}
}
}

/**
* Registers new dependency
* @param string $dependency
* @return bool
* @param string $concrete
* @param string|null $abstract
* @throws DiException
*/
public static function register(string $dependency): bool
public static function register(string $concrete, ?string $abstract = null)
{
if (!in_array($dependency, self::$dependencies) && class_exists($dependency)) {
self::$dependencies[] = $dependency;
return true;
$key = $abstract ?? $concrete;

if (isset(self::$dependencies[$key])) {
throw DiException::dependencyAlreadyRegistered($key);
}

if (!class_exists($concrete)) {
throw DiException::dependencyNotInstantiable($concrete);
}

if ($abstract !== null && !class_exists($abstract) && !interface_exists($abstract)) {
throw DiException::invalidAbstractDependency($abstract);
}

return false;
self::$dependencies[$key] = $concrete;
}

/**
* Checks if a dependency registered
* @param string $dependency
* @param string $abstract
* @return bool
*/
public static function isRegistered(string $dependency): bool
public static function isRegistered(string $abstract): bool
{
return in_array($dependency, self::$dependencies);
return isset(self::$dependencies[$abstract]);
}

/**
Expand Down Expand Up @@ -140,47 +149,57 @@ public static function reset(): void
{
self::$dependencies = [];
self::$container = [];
self::$resolving = [];
}

/**
* Resolves the dependency
* @param string $dependency
* @param string $abstract
* @param array $args
* @param bool $singleton
* @return mixed
* @throws DiException
* @throws ReflectionException
*/
private static function resolve(string $dependency, array $args = [], bool $singleton = true)
private static function resolve(string $abstract, array $args = [], bool $singleton = true)
{
if ($singleton) {
if (!isset(self::$container[$dependency])) {
self::$container[$dependency] = self::instantiate($dependency, $args);
self::checkCircularDependency($abstract);

self::$resolving[$abstract] = true;

try {
$concrete = self::$dependencies[$abstract];

if ($singleton) {
if (!isset(self::$container[$abstract])) {
self::$container[$abstract] = self::instantiate($concrete, $args);
}
return self::$container[$abstract];
}

return self::$container[$dependency];
return self::instantiate($concrete, $args);
} finally {
unset(self::$resolving[$abstract]);
}

return self::instantiate($dependency, $args);
}

/**
* Instantiates the dependency
* @param string $dependency
* @param string $concrete
* @param array $args
* @return mixed
* @throws DiException
* @throws ReflectionException
*/
private static function instantiate(string $dependency, array $args = [])
private static function instantiate(string $concrete, array $args = [])
{
$class = new ReflectionClass($dependency);
$class = new ReflectionClass($concrete);

$constructor = $class->getConstructor();

$params = $constructor ? self::resolveParameters($constructor->getParameters(), $args) : [];

return new $dependency(...$params);
return new $concrete(...$params);
}

/**
Expand Down Expand Up @@ -214,8 +233,10 @@ private static function resolveParameter(ReflectionParameter $param, ?array &$ar
{
$type = $param->getType() ? $param->getType()->getName() : null;

if ($type && self::instantiable($type)) {
return self::create($type);
$concrete = self::$dependencies[$type] ?? $type;

if ($concrete && self::instantiable($concrete)) {
return self::create($concrete);
}

if ($type === 'array') {
Expand All @@ -231,12 +252,25 @@ private static function resolveParameter(ReflectionParameter $param, ?array &$ar

/**
* Checks if the class is instantiable
* @param string $type
* @param string $class
* @return bool
*/
protected static function instantiable(string $type): bool
protected static function instantiable(string $class): bool
{
return class_exists($type) && (new ReflectionClass($type))->isInstantiable();
return class_exists($class) && (new ReflectionClass($class))->isInstantiable();
}

/**
* @param string $abstract
* @return void
* @throws DiException
*/
private static function checkCircularDependency(string $abstract): void
{
if (isset(self::$resolving[$abstract])) {
$chain = implode(' -> ', array_keys(self::$resolving)) . ' -> ' . $abstract;
throw DiException::circularDependency($chain);
}
}

/**
Expand All @@ -261,9 +295,9 @@ private static function userDependencies(): array
private static function coreDependencies(): array
{
return [
\Quantum\Loader\Loader::class,
\Quantum\Http\Request::class,
\Quantum\Http\Response::class,
\Quantum\Loader\Loader::class => \Quantum\Loader\Loader::class,
\Quantum\Http\Request::class => \Quantum\Http\Request::class,
\Quantum\Http\Response::class => \Quantum\Http\Response::class,
];
}
}
38 changes: 37 additions & 1 deletion src/Di/Exceptions/DiException.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* @author Arman Ag. <arman.ag@softberg.org>
* @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
* @link http://quantum.softberg.org/
* @since 2.9.7
* @since 2.9.8
*/

namespace Quantum\Di\Exceptions;
Expand All @@ -30,4 +30,40 @@ public static function dependencyNotRegistered(string $name): DiException
{
return new self(t('exception.dependency_not_registered', $name), E_ERROR);
}

/**
* @param string $name
* @return DiException
*/
public static function dependencyAlreadyRegistered(string $name): DiException
{
return new self(t('exception.dependency_already_registered', $name), E_ERROR);
}

/**
* @param string $name
* @return DiException
*/
public static function dependencyNotInstantiable(string $name): DiException
{
return new self(t('exception.dependency_not_instantiable', $name), E_ERROR);
}

/**
* @param string $name
* @return DiException
*/
public static function invalidAbstractDependency(string $name): DiException
{
return new self(t('exception.invalid_abstract_dependency', $name), E_ERROR);
}

/**
* @param string $chain
* @return DiException
*/
public static function circularDependency(string $chain): DiException
{
return new self("Circular dependency detected: $chain", 0);
}
}
Loading