Passed
Pull Request — master (#274)
by Alexander
03:15
created

CompositeContainer::detach()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 3
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Di;
6
7
use InvalidArgumentException;
8
use Psr\Container\ContainerInterface;
9
use Throwable;
10
use function get_class;
11
use function gettype;
12
use function is_object;
13
use function is_string;
14
15
/**
16
 * A composite container for use with containers that support the delegate lookup feature.
17
 */
18
final class CompositeContainer implements ContainerInterface
19
{
20
    /**
21
     * Containers to look into starting from the beginning of the array.
22
     *
23
     * @var ContainerInterface[] The list of containers.
24
     */
25
    private array $containers = [];
26
27 27
    public function get($id)
28
    {
29
        /** @psalm-suppress TypeDoesNotContainType */
30 27
        if (!is_string($id)) {
31
            throw new InvalidArgumentException("Id must be a string, {$this->getVariableType($id)} given.");
32
        }
33
34 27
        if ($id === StateResetter::class) {
35 2
            $resetters = [];
36 2
            foreach ($this->containers as $container) {
37 2
                if ($container->has(StateResetter::class)) {
38 2
                    $resetters[] = $container->get(StateResetter::class);
39
                }
40
            }
41 2
            $stateResetter = new StateResetter($this);
42 2
            $stateResetter->setResetters($resetters);
43
44 2
            return $stateResetter;
45
        }
46
47 27
        if ($this->isTagAlias($id)) {
48 2
            $tags = [];
49 2
            foreach ($this->containers as $container) {
50 2
                if (!$container instanceof Container) {
51
                    continue;
52
                }
53 2
                if ($container->has($id)) {
54 2
                    $tags = array_merge($container->get($id), $tags);
0 ignored issues
show
Bug introduced by
It seems like $container->get($id) can also be of type object; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

54
                    $tags = array_merge(/** @scrutinizer ignore-type */ $container->get($id), $tags);
Loading history...
55
                }
56
            }
57
58 2
            return $tags;
59
        }
60
61 25
        foreach ($this->containers as $container) {
62 25
            if ($container->has($id)) {
63 20
                return $container->get($id);
64
            }
65
        }
66
67
68
        // Collect details from containers
69 7
        $exceptions = [];
70 7
        foreach ($this->containers as $i => $container) {
71 5
            $hasException = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $hasException is dead and can be removed.
Loading history...
72
            try {
73 5
                $hasException = true;
74 5
                $container->get($id);
75 5
            } catch (Throwable $t) {
76 5
                $exceptions[] = $t;
77 5
            } finally {
78 5
                if (!$hasException) {
79 5
                    $exceptions[] = new \RuntimeException("Container $i has() returned false but no exception was thrown from get().");
80
                }
81
            }
82
        }
83
84 7
        throw new CompositeNotFoundException($exceptions);
85
    }
86
87 20
    public function has($id): bool
88
    {
89 20
        foreach ($this->containers as $container) {
90 5
            if ($container->has($id)) {
91 5
                return true;
92
            }
93
        }
94 15
        return false;
95
    }
96
97
    /**
98
     * Attaches a container to the composite container.
99
     *
100
     * @param ContainerInterface $container
101
     */
102 29
    public function attach(ContainerInterface $container): void
103
    {
104 29
        $this->containers[] = $container;
105 29
    }
106
107
    /**
108
     * Removes a container from the list of containers.
109
     *
110
     * @param ContainerInterface $container
111
     */
112 2
    public function detach(ContainerInterface $container): void
113
    {
114 2
        foreach ($this->containers as $i => $c) {
115 2
            if ($container === $c) {
116 2
                unset($this->containers[$i]);
117
            }
118
        }
119 2
    }
120
121 27
    private function isTagAlias(string $id): bool
122
    {
123 27
        return strncmp($id, 'tag@', 4) === 0;
124
    }
125
126
    /**
127
     * @param mixed $variable
128
     */
129
    private function getVariableType($variable): string
130
    {
131
        return is_object($variable) ? get_class($variable) : gettype($variable);
132
    }
133
}
134