Passed
Push — master ( 3b8ce4...b46cbf )
by Alexander
03:01
created

CompositeContainer::isTagAlias()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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 RuntimeException;
10
use Throwable;
11
use function get_class;
12
use function gettype;
13
use function is_object;
14
use function is_string;
15
16
/**
17
 * A composite container for use with containers that support the delegate lookup feature.
18
 */
19
final class CompositeContainer implements ContainerInterface
20
{
21
    /**
22
     * Containers to look into starting from the beginning of the array.
23
     *
24
     * @var ContainerInterface[] The list of containers.
25
     */
26
    private array $containers = [];
27
28 27
    public function get($id)
29
    {
30
        /** @psalm-suppress TypeDoesNotContainType */
31 27
        if (!is_string($id)) {
32
            throw new InvalidArgumentException("Id must be a string, {$this->getVariableType($id)} given.");
33
        }
34
35 27
        if ($id === StateResetter::class) {
36 2
            $resetters = [];
37 2
            foreach ($this->containers as $container) {
38 2
                if ($container->has(StateResetter::class)) {
39 2
                    $resetters[] = $container->get(StateResetter::class);
40
                }
41
            }
42 2
            $stateResetter = new StateResetter($this);
43 2
            $stateResetter->setResetters($resetters);
44
45 2
            return $stateResetter;
46
        }
47
48 27
        if ($this->isTagAlias($id)) {
49 2
            $tags = [];
50 2
            foreach ($this->containers as $container) {
51 2
                if (!$container instanceof Container) {
52
                    continue;
53
                }
54 2
                if ($container->has($id)) {
55 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

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