Test Failed
Push — upgrade-psr ( 3d23e3...7af4e6 )
by Sergei
17:47 queued 14:42
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 25
    /**
24
     * Containers to look into starting from the beginning of the array.
25
     *
26 25
     * @var ContainerInterface[] The list of containers.
27
     */
28
    private array $containers = [];
29
30 25
    /**
31 2
     * @psalm-template T
32 2
     * @psalm-param string|class-string<T> $id
33 2
     * @psalm-return ($id is class-string ? T : mixed)
34 2
     */
35
    public function get($id)
36
    {
37 2
        /** @psalm-suppress TypeDoesNotContainType */
38 2
        if (!is_string($id)) {
39
            throw new InvalidArgumentException(
40 2
                sprintf(
41
                    'ID must be a string, %s given.',
42
                    $this->getVariableType($id)
43 25
                )
44 2
            );
45 2
        }
46 2
47
        if ($id === StateResetter::class) {
48
            $resetters = [];
49 2
            foreach ($this->containers as $container) {
50 2
                if ($container->has(StateResetter::class)) {
51
                    $resetters[] = $container->get(StateResetter::class);
52
                }
53
            }
54 2
            $stateResetter = new StateResetter($this);
55
            $stateResetter->setResetters($resetters);
56
57 23
            return $stateResetter;
58 23
        }
59 20
60
        if (TagHelper::isTagAlias($id)) {
61
            $tags = [];
62
            foreach ($this->containers as $container) {
63 5
                if (!$container instanceof Container) {
64
                    continue;
65
                }
66 18
                if ($container->has($id)) {
67
                    /** @psalm-suppress MixedArgument Container::get() always return array for tag */
68 18
                    array_unshift($tags, $container->get($id));
69 5
                }
70 5
            }
71
72
            /** @psalm-suppress MixedArgument Container::get() always return array for tag */
73 13
            return array_merge(...$tags);
74
        }
75
76
        foreach ($this->containers as $container) {
77
            if ($container->has($id)) {
78
                /** @psalm-suppress MixedReturnStatement */
79
                return $container->get($id);
80
            }
81 27
        }
82
83 27
        // Collect details from containers
84 27
        $exceptions = [];
85
        foreach ($this->containers as $container) {
86
            $hasException = false;
87
            try {
88
                $container->get($id);
89
            } catch (Throwable $t) {
90
                $hasException = true;
91 2
                $exceptions[] = [$t, $container];
92
            } finally {
93 2
                if (!$hasException) {
94 2
                    $exceptions[] = [
95 2
                        new RuntimeException(
96
                            'Container "has()" returned false, but no exception was thrown from "get()".'
97
                        ),
98 2
                        $container,
99
                    ];
100 25
                }
101
            }
102 25
        }
103
104
        throw new CompositeNotFoundException($exceptions);
105
    }
106
107
    public function has($id): bool
108
    {
109
        foreach ($this->containers as $container) {
110
            if ($container->has($id)) {
111
                return true;
112
            }
113
        }
114
        return false;
115
    }
116
117
    /**
118
     * Attaches a container to the composite container.
119
     */
120
    public function attach(ContainerInterface $container): void
121
    {
122
        $this->containers[] = $container;
123
    }
124
125
    /**
126
     * Removes a container from the list of containers.
127
     */
128
    public function detach(ContainerInterface $container): void
129
    {
130
        foreach ($this->containers as $i => $c) {
131
            if ($container === $c) {
132
                unset($this->containers[$i]);
133
            }
134
        }
135
    }
136
137
    /**
138
     * @param mixed $variable
139
     */
140
    private function getVariableType($variable): string
141
    {
142
        return is_object($variable) ? get_class($variable) : gettype($variable);
143
    }
144
}
145