Completed
Push — master ( b0867d...8ab030 )
by Alexander
24s queued 11s
created

CompositeContextContainer::getContextContainer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
4
namespace yii\di;
5
6
use Psr\Container\ContainerInterface;
7
use Psr\Container\NotFoundExceptionInterface;
8
use yii\di\exceptions\NotFoundException;
9
10
/**
11
 * This class implements a composite container with support for context switching.
12
 * The intended use is to allow for custom configurations of (nested) modules.
13
 */
14
class CompositeContextContainer implements ContainerInterface
15
{
16
    /**
17
     * Containers to look into.
18
     * The first level of this array is sorted by the length of the key, from long to short.
19
     * Longer key means container is more specific and is checked first.
20
     * @var array The lists of containers indexed by contexts
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
                // ignore
34
            }
35
        }
36
        throw new NotFoundException("No definition for $id");
37
    }
38
39
    /**
40
     * @param string $context
41
     * @return ContainerInterface[] All containers in the context specified
42
     */
43
    private function getContainers(string $context): iterable
44
    {
45
        foreach ($this->containers as $prefix => $containers) {
46
            if (strncmp($prefix, $context, strlen($prefix)) !== 0) {
47
                continue;
48
            }
49
            yield from $containers;
0 ignored issues
show
Bug Best Practice introduced by
The expression YieldFromNode returns the type Generator which is incompatible with the documented return type Psr\Container\ContainerInterface[].
Loading history...
50
        }
51
    }
52
53
    public function has($id)
54
    {
55
        foreach ($this->getContainers($this->currentContext) as $container) {
56
            if ($container->has($id)) {
57
                return true;
58
            }
59
        }
60
        return false;
61
    }
62
63
    /**
64
     * Attaches a container to the composite container.
65
     * @param string $context The context for the new container.
66
     * @param ContainerInterface $container
67
     */
68
    public function attach(ContainerInterface $container, string $context = ''): void
69
    {
70
        if (isset($this->containers[$context])) {
71
            array_unshift($this->containers[$context], $container);
72
        } else {
73
            // If the context is new we reorder the containers array.
74
            $this->containers[$context] = [
75
                $container
76
            ];
77
            uksort($this->containers, static function ($a, $b) {
78
                return mb_strlen($b) <=> mb_strlen($a);
79
            });
80
        }
81
    }
82
83
    /**
84
     * Removes a container from the list of containers.
85
     * @param ContainerInterface $container
86
     */
87
    public function detach(ContainerInterface $container): void
88
    {
89
        foreach ($this->containers as $prefix => $containers) {
90
            foreach ($containers as $i => $c) {
91
                if ($container === $c) {
92
                    unset($this->containers[$prefix][$i]);
93
                }
94
            }
95
            if (empty($this->containers[$prefix])) {
96
                unset($this->containers[$prefix]);
97
            }
98
        }
99
    }
100
101
    /**
102
     * Gets a service from the container in the context.
103
     *
104
     * @param string $id Name of the service, not typehinted to remain compatible with PSR-11 `get()`
105
     * @param string $context
106
     * @throws NotFoundException
107
     */
108
    public function getFromContext($id, string $context)
109
    {
110
        foreach ($this->getContainers($context) as $container) {
111
            try {
112
                return $container->get($id);
113
            } catch (NotFoundExceptionInterface $e) {
114
                // ignore
115
            }
116
        }
117
        throw new NotFoundException("No definition for $id");
118
    }
119
120
    /**
121
     * Checks if we have a definition for a service in the given context
122
     * @param string $id Name of the service, not typehinted to remain compatible with PSR-11 `has()`
123
     * @param string $context The context to use
124
     * @return bool
125
     */
126
    public function hasInContext($id, string $context): bool
127
    {
128
        foreach ($this->getContainers($context) as $container) {
129
            if ($container->has($id)) {
130
                return true;
131
            }
132
        }
133
        return false;
134
    }
135
136
    /**
137
     * This will return a container that only resolves services from a specific context.
138
     * @param string $context
139
     * @return ContainerInterface
140
     */
141
    public function getContextContainer(string $context): ContainerInterface
142
    {
143
        return new ContextContainer($this, $context);
144
    }
145
}
146