Passed
Push — master ( 0b1876...128832 )
by Alexander
02:14
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 Yiisoft\Di\Helpers\TagHelper;
12
13
use function get_class;
14
use function gettype;
15
use function is_object;
16
use function is_string;
17
18
/**
19
 * A composite container for use with containers that support the delegate lookup feature.
20
 */
21
final class CompositeContainer implements ContainerInterface
22
{
23
    /**
24
     * Containers to look into starting from the beginning of the array.
25
     *
26
     * @var ContainerInterface[] The list of containers.
27
     */
28
    private array $containers = [];
29
30
    /**
31
     * @psalm-template T
32
     * @psalm-param string|class-string<T> $id
33
     * @psalm-return ($id is class-string ? T : mixed)
34
     */
35 32
    public function get($id)
36
    {
37
        /** @psalm-suppress TypeDoesNotContainType */
38 32
        if (!is_string($id)) {
39 1
            throw new InvalidArgumentException(
40 1
                sprintf(
41 1
                    'ID must be a string, %s given.',
42 1
                    $this->getVariableType($id)
43
                )
44
            );
45
        }
46
47 31
        if ($id === StateResetter::class) {
48 4
            $resetters = [];
49 4
            foreach ($this->containers as $container) {
50 4
                if ($container->has(StateResetter::class)) {
51 4
                    $resetters[] = $container->get(StateResetter::class);
52
                }
53
            }
54 4
            $stateResetter = new StateResetter($this);
55 4
            $stateResetter->setResetters($resetters);
56
57 4
            return $stateResetter;
58
        }
59
60 31
        if (TagHelper::isTagAlias($id)) {
61 3
            $tags = [];
62 3
            foreach ($this->containers as $container) {
63 3
                if (!$container instanceof Container) {
64 1
                    continue;
65
                }
66 3
                if ($container->has($id)) {
67
                    /** @psalm-suppress MixedArgument Container::get() always return array for tag */
68 3
                    $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

68
                    $tags = array_merge(/** @scrutinizer ignore-type */ $container->get($id), $tags);
Loading history...
69
                }
70
            }
71
72 3
            return $tags;
73
        }
74
75 28
        foreach ($this->containers as $container) {
76 28
            if ($container->has($id)) {
77
                /** @psalm-suppress MixedReturnStatement */
78 22
                return $container->get($id);
79
            }
80
        }
81
82
        // Collect details from containers
83 8
        $exceptions = [];
84 8
        foreach ($this->containers as $container) {
85 6
            $hasException = false;
86
            try {
87 6
                $container->get($id);
88 5
            } catch (Throwable $t) {
89 5
                $hasException = true;
90 5
                $exceptions[] = [$t, $container];
91 6
            } finally {
92 6
                if (!$hasException) {
93 1
                    $exceptions[] = [
94 1
                        new RuntimeException(
95 1
                            'Container "has()" returned false, but no exception was thrown from "get()".'
96
                        ),
97 6
                        $container,
98
                    ];
99
                }
100
            }
101
        }
102
103 8
        throw new CompositeNotFoundException($exceptions);
104
    }
105
106 27
    public function has($id): bool
107
    {
108 27
        foreach ($this->containers as $container) {
109 7
            if ($container->has($id)) {
110 7
                return true;
111
            }
112
        }
113 22
        return false;
114
    }
115
116
    /**
117
     * Attaches a container to the composite container.
118
     */
119 33
    public function attach(ContainerInterface $container): void
120
    {
121 33
        $this->containers[] = $container;
122 33
    }
123
124
    /**
125
     * Removes a container from the list of containers.
126
     */
127 2
    public function detach(ContainerInterface $container): void
128
    {
129 2
        foreach ($this->containers as $i => $c) {
130 2
            if ($container === $c) {
131 2
                unset($this->containers[$i]);
132
            }
133
        }
134 2
    }
135
136
    /**
137
     * @param mixed $variable
138
     */
139 1
    private function getVariableType($variable): string
140
    {
141 1
        return is_object($variable) ? get_class($variable) : gettype($variable);
142
    }
143
}
144