Completed
Push — sam-rework ( 1696e6 )
by Andrii
38:13 queued 23:11
created

CompositeContextContainer::detach()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 12
rs 9.6111
c 0
b 0
f 0
cc 5
nc 7
nop 1
1
<?php
2
3
4
namespace yii\di;
5
6
use phpDocumentor\Reflection\Types\Context;
7
use Psr\Container\ContainerInterface;
8
use Psr\Container\NotFoundExceptionInterface;
9
use yii\di\exceptions\NotFoundException;
10
11
/**
12
 * This class implements a composite container with support for context switching.
13
 * The intended use is to allow for custom configurations of (nested) modules.
14
 */
15
class CompositeContextContainer implements ContainerInterface
16
{
17
    /**
18
     * We use a simple array since order matters
19
     * The first level of this array is sorted by the length of the key, from long to short.
20
     * @var ContainerInterface[string][int] The list of containers
0 ignored issues
show
Documentation Bug introduced by
The doc comment ContainerInterface[string][int] at position 1 could not be parsed: Expected ']' at position 1, but found '['.
Loading history...
21
     */
22
    private $containers = [];
23
24
    private $currentContext = '';
25
26
    /** @inheritdoc */
27
    public function get($id)
28
    {
29
        foreach ($this->getContainers($this->currentContext) as $container) {
30
            try {
31
                return $container->get($id);
32
            } catch (NotFoundExceptionInterface $e) {
33
                continue;
34
            }
35
        }
36
        throw new NotFoundException();
37
    }
38
39
    /**
40
     * @return ContainerInterface[] All containers in the current context
41
     */
42
    private function getContainers(string $context): iterable
43
    {
44
        foreach($this->containers as $prefix => $containers) {
45
            if (strncmp($prefix, $context, mb_strlen($prefix)) !== 0) {
46
                continue;
47
            }
48
            foreach($containers as $container) {
49
                yield $container;
0 ignored issues
show
Bug Best Practice introduced by
The expression yield $container returns the type Generator which is incompatible with the documented return type Psr\Container\ContainerInterface[].
Loading history...
50
            }
51
        }
52
    }
53
54
    /** @inheritdoc */
55
    public function has($id)
56
    {
57
        foreach ($this->getContainers($this->currentContext) as $container) {
58
            if ($container->has($id)) {
59
                return true;
60
            }
61
        }
62
        return false;
63
    }
64
65
    /**
66
     * Attaches a container to the composite container.
67
     * @param string The context for the new container.
0 ignored issues
show
Bug introduced by
The type yii\di\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
68
     * @param ContainerInterface $container
69
     */
70
    public function attach(ContainerInterface $container, string $context = '')
71
    {
72
        if (isset($this->containers[$context])) {
73
            array_unshift($this->containers[$context], $container);
74
        } else {
75
            // If the context is new we reorder the containers array.
76
            $this->containers[$context] = [
77
                $container
78
            ];
79
            uksort($this->containers, function ($a, $b) {
80
                return mb_strlen($b) <=> mb_strlen($a);
81
            });
82
        }
83
    }
84
85
    /**
86
     * Removes a container from the list of containers.
87
     * @param ContainerInterface $container
88
     */
89
    public function detach(ContainerInterface $container)
90
    {
91
        foreach ($this->containers as $prefix => $containers) {
92
            foreach($containers as $i => $c) {
93
                if ($container === $c) {
94
                    unset($this->containers[$prefix][$i]);
95
                }
96
            }
97
            if (!empty($this->containers[$prefix])) {
98
                $this->containers[$prefix] = array_values($this->containers[$prefix]);
99
            } else {
100
                unset($this->containers[$prefix]);
101
            }
102
        }
103
    }
104
105
    /**
106
     * Gets a service from the container in the context.
107
     *
108
     * @param string $id Name of the service, not typehinted to remain compatible with PSR-11 `get()`
109
     * @param string $context
110
     */
111
    public function getFromContext($id, string $context)
112
    {
113
        foreach ($this->getContainers($context) as $container) {
114
            try {
115
                return $container->get($id);
116
            } catch (NotFoundExceptionInterface $e) {
117
                continue;
118
            }
119
        }
120
        throw new NotFoundException();
121
    }
122
123
    /**
124
     * Checks if we have a definition for a service in the given context
125
     * @param string $id Name of the service, not typehinted to remain compatible with PSR-11 `has()`
126
     * @param string $context The context to use
127
     * @return bool
128
     */
129
    public function hasInContext($id, string $context): bool
130
    {
131
        foreach ($this->getContainers($context) as $container) {
132
            if ($container->has($id)) {
133
                return true;
134
            }
135
        }
136
        return false;
137
    }
138
139
    /**
140
     * This will return a container that only resolves services from a specific context.
141
     * @param string $context
142
     * @return ContainerInterface
143
     */
144
    public function getContextContainer(string $context): ContainerInterface
145
    {
146
        return new ContextContainer($this, $context);
147
    }
148
}
149