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
46 changes: 15 additions & 31 deletions src/Di/DiContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@
*/
class DiContainer
{
/**
* @var array<string, class-string>
*/
private array $dependencies = [];
private DiRegistry $registry;

/**
* @var array<string, object>
Expand All @@ -49,18 +46,19 @@ class DiContainer
*/
private array $resolving = [];

public function __construct(?DiRegistry $registry = null)
{
$this->registry = $registry ?? new DiRegistry();
}

/**
* Register dependencies
* @param array<string, mixed> $dependencies
* @throws DiException
*/
public function registerDependencies(array $dependencies): void
{
foreach ($dependencies as $abstract => $concrete) {
if (!$this->isRegistered($abstract)) {
$this->register($concrete, $abstract);
}
}
$this->registry->registerDependencies($dependencies);
}

/**
Expand All @@ -69,29 +67,15 @@ public function registerDependencies(array $dependencies): void
*/
public function register(string $concrete, ?string $abstract = null): void
{
$key = $abstract ?? $concrete;

if (isset($this->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);
}

$this->dependencies[$key] = $concrete;
$this->registry->register($concrete, $abstract);
}

/**
* Checks if a dependency registered
*/
public function isRegistered(string $abstract): bool
{
return isset($this->dependencies[$abstract]);
return $this->registry->isRegistered($abstract);
}

/**
Expand Down Expand Up @@ -123,12 +107,12 @@ public function set(string $abstract, object $instance, bool $override = true):
throw DiException::dependencyAlreadyRegistered($abstract);
}

if (!$override && isset($this->dependencies[$abstract])) {
if (!$override && $this->registry->isRegistered($abstract)) {
throw DiException::dependencyAlreadyRegistered($abstract);
}

if (!isset($this->dependencies[$abstract])) {
$this->dependencies[$abstract] = get_class($instance);
if (!$this->registry->isRegistered($abstract)) {
$this->registry->register(get_class($instance), $abstract);
}

$this->container[$abstract] = $instance;
Expand Down Expand Up @@ -190,7 +174,7 @@ public function autowire(callable $entry, array $args = []): array

public function reset(): void
{
$this->dependencies = [];
$this->registry->reset();
$this->resetContainer();
}

Expand All @@ -212,7 +196,7 @@ private function resolve(string $abstract, array $args = [], bool $singleton = t
$this->resolving[$abstract] = true;

try {
$concrete = $this->dependencies[$abstract];
$concrete = $this->registry->getConcrete($abstract);

if ($singleton) {
if (!isset($this->container[$abstract])) {
Expand Down Expand Up @@ -279,7 +263,7 @@ private function resolveParameter(ReflectionParameter $param, array &$args = [])
$type = $param->getType()->getName();
}

if ($type !== null && isset($this->dependencies[$type])) {
if ($type !== null && $this->registry->isRegistered($type)) {
/** @var class-string $type */
return $this->get($type);
}
Expand Down
102 changes: 102 additions & 0 deletions src/Di/DiRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

/**
* Quantum PHP Framework
*
* An open source software development framework for PHP
*
* @package Quantum
* @author Arman Ag. <arman.ag@softberg.org>
* @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
* @link http://quantum.softberg.org/
* @since 3.0.0
*/

namespace Quantum\Di;

use Quantum\Di\Exceptions\DiException;
use ReflectionClass;

/**
* DiRegistry Class
*
* Stores dependency bindings for the container.
*
* @package Quantum/Di
*/
class DiRegistry
{
/**
* @var array<string, class-string>
*/
private array $dependencies = [];

/**
* Register dependencies
* @param array<string, mixed> $dependencies
* @throws DiException
*/
public function registerDependencies(array $dependencies): void
{
foreach ($dependencies as $abstract => $concrete) {
if (!$this->isRegistered($abstract)) {
$this->register($concrete, $abstract);
}
}
Comment thread
armanist marked this conversation as resolved.
}

/**
* Registers new dependency
* @throws DiException
*/
public function register(string $concrete, ?string $abstract = null): void
{
$key = $abstract ?? $concrete;

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

if (!class_exists($concrete)) {
throw DiException::dependencyNotInstantiable($concrete);
}
Comment thread
armanist marked this conversation as resolved.

if (!(new ReflectionClass($concrete))->isInstantiable()) {
throw DiException::dependencyNotInstantiable($concrete);
}

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

$this->dependencies[$key] = $concrete;
}

/**
* Checks if a dependency registered
*/
public function isRegistered(string $abstract): bool
{
return isset($this->dependencies[$abstract]);
}

/**
* @throws DiException
* @return class-string
*/
public function getConcrete(string $abstract): string
{
if (!$this->isRegistered($abstract)) {
throw DiException::dependencyNotRegistered($abstract);
}

return $this->dependencies[$abstract];
}

public function reset(): void
{
$this->dependencies = [];
}
}
91 changes: 91 additions & 0 deletions tests/Unit/Di/DiRegistryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

namespace Quantum\Tests\Unit\Di;

use Quantum\Di\Exceptions\DiException;
use Quantum\Di\DiRegistry;
use Quantum\Tests\Unit\AppTestCase;
use Quantum\Loader\Setup;
use Quantum\Http\Request;

class DiRegistryTest extends AppTestCase
{
private DiRegistry $registry;

public function setUp(): void
{
parent::setUp();
$this->registry = new DiRegistry();
}

public function testRegisterDependency(): void
{
$this->registry->register(Setup::class);

$this->assertTrue($this->registry->isRegistered(Setup::class));
$this->assertSame(Setup::class, $this->registry->getConcrete(Setup::class));
}

public function testRegisterDependencies(): void
{
$this->registry->registerDependencies([
\ArrayAccess::class => \ArrayObject::class,
Setup::class => Setup::class,
]);

$this->assertSame(\ArrayObject::class, $this->registry->getConcrete(\ArrayAccess::class));
$this->assertSame(Setup::class, $this->registry->getConcrete(Setup::class));
}

public function testAttemptingToRegisterAlreadyRegisteredDependency(): void
{
$this->expectException(DiException::class);
$this->expectExceptionMessage('The dependency `Quantum\Loader\Setup` is already registered.');

$this->registry->register(Setup::class);
$this->registry->register(Setup::class);
}

public function testAttemptingToRegisterNonExistentClass(): void
{
$this->expectException(DiException::class);
$this->expectExceptionMessage('The dependency `NonExistentClass` is not instantiable.');

$this->registry->register('NonExistentClass');
}
Comment thread
armanist marked this conversation as resolved.

public function testAttemptingToRegisterAbstractClass(): void
{
$this->expectException(DiException::class);
$this->expectExceptionMessage('The dependency `Quantum\\App\\Exceptions\\BaseException` is not instantiable.');

$this->registry->register(\Quantum\App\Exceptions\BaseException::class);
}

public function testAttemptingToRegisterNonExistentAbstract(): void
{
$this->expectException(DiException::class);
$this->expectExceptionMessage('The dependency `NonExistentInterface` is not valid abstract class.');

$this->registry->register(Request::class, 'NonExistentInterface');
}

public function testGetConcreteThrowsForUnregisteredDependency(): void
{
$this->expectException(DiException::class);
$this->expectExceptionMessage('The dependency `Quantum\Loader\Setup` is not registered.');

$this->registry->getConcrete(Setup::class);
}

public function testResetClearsDependencies(): void
{
$this->registry->register(Setup::class);

$this->assertTrue($this->registry->isRegistered(Setup::class));

$this->registry->reset();

$this->assertFalse($this->registry->isRegistered(Setup::class));
}
}
Loading