Test Failed
Pull Request — master (#37)
by Divine Niiquaye
03:19
created

ContextContainer::attach()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of DivineNii opensource projects.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2021 DivineNii (https://divinenii.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Rade\DI;
19
20
use Psr\Container\ContainerInterface;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Rade\DI\ContainerInterface. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
use Rade\DI\Exceptions\{ContainerResolutionException, NotFoundServiceException};
22
use Symfony\Contracts\Service\ResetInterface;
23
24
/**
25
 * A container supporting multiple PSR-11 containers.
26
 *
27
 * @author Divine Niiquaye Ibok <[email protected]>
28
 */
29
final class ContextContainer implements ContainerInterface, ResetInterface
30
{
31
    /** @var ContainerInterface[] A list of PSR-11 containers */
32
    private array $containers = [];
33
34
    /**
35
     * {@inheritdoc}
36
     */
37
    public function get(string $id)
38
    {
39
        if (\in_array($id, [ContainerInterface::class, __CLASS__, AbstractContainer::SERVICE_CONTAINER], true)) {
40
            return $this;
41
        }
42
43
        foreach ($this->containers as $container) {
44
            if ($container->has($id)) {
45
                return $container->get($id);
46
            }
47
48
            if ($container instanceof Container && $container->typed($id)) {
49
                try {
50
                    return $container->autowired($id, true);
51
                } catch (ContainerResolutionException $e) {
52
                    // Skip this exception.
53
                }
54
            }
55
        }
56
57
        throw new NotFoundServiceException(\sprintf('Requested service "%s" was not found in any container. Did you forget to set it?', $id));
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function has(string $id): bool
64
    {
65
        foreach ($this->containers as $container) {
66
            if ($container->has($id)) {
67
                return true;
68
            }
69
        }
70
71
        return false;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function reset(): void
78
    {
79
        // A container such as Symfony DI support reset ...
80
        foreach ($this->containers as $container) {
81
            if ($container instanceof ResetInterface) {
82
                $container->reset();
83
            }
84
        }
85
86
        $this->containers = [];
87
    }
88
89
    /**
90
     * Attaches a container to the composite container.
91
     */
92
    public function attach(ContainerInterface $container): void
93
    {
94
        $this->containers[] = $container;
95
    }
96
97
    /**
98
     * Removes a container from the list of containers.
99
     */
100
    public function detach(ContainerInterface $container): void
101
    {
102
        foreach ($this->containers as $i => $c) {
103
            if ($container === $c) {
104
                unset($this->containers[$i]);
105
106
                break;
107
            }
108
        }
109
    }
110
}
111