Completed
Push — master ( ca19c6...55567b )
by Jonathan
02:54
created

AbstractContainer::has()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 3
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 3
nc 3
nop 1
crap 12
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Caridea
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
 * use this file except in compliance with the License. You may obtain a copy of
8
 * the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
 * License for the specific language governing permissions and limitations under
16
 * the License.
17
 *
18
 * @copyright 2015-2018 LibreWorks contributors
19
 * @license   Apache-2.0
20
 */
21
namespace Caridea\Container;
22
23
/**
24
 * Abstract dependency injection container.
25
 *
26
 * @copyright 2015-2018 LibreWorks contributors
27
 * @license   Apache-2.0
28
 */
29
abstract class AbstractContainer implements Container
30
{
31
    /**
32
     * @var Container The parent container
33
     */
34
    protected $parent;
35
    /**
36
     * @var string[] with string keys
37
     */
38
    protected $types = [];
39
    /**
40
     * @var string[] the list of PHP native types
41
     */
42
    protected static $primitives = ['array', 'bool', 'float', 'int', 'resource', 'string'];
43
44
    /**
45
     * Creates a new AbstractContainer.
46
     *
47
     * @param string[] $types with string keys
48
     * @param \Caridea\Container\Container $parent The parent container
49
     */
50 5
    protected function __construct(array $types, Container $parent = null)
51
    {
52 5
        $this->types = $types;
53 5
        $this->parent = $parent;
54 5
    }
55
56
    /**
57
     * {@inheritDoc}
58
     */
59 1
    public function contains(string $name): bool
60
    {
61 1
        return $this->has($name);
62
    }
63
64
    /**
65
     * {@inheritDoc}
66
     */
67 1
    public function containsType(string $type): bool
68
    {
69 1
        $isObject = !in_array($type, self::$primitives, true);
70 1
        foreach ($this->types as $ctype) {
71 1
            if ($type === $ctype || ($isObject && is_a($ctype, $type, true))) {
72 1
                return true;
73
            }
74
        }
75 1
        return $this->parent ? $this->parent->containsType($type) : false;
76
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81 5
    public function get($id)
82
    {
83 5
        $entry = isset($this->types[$id]) ? $this->doGet($id) :
84 3
            ($this->parent ? $this->parent->get($id) : null);
85 3
        if ($entry === null) {
86 1
            throw new Exception\Missing("No container entry found for key: $id");
87
        }
88 2
        return $entry;
89
    }
90
91
    /**
92
     * {@inheritDoc}
93
     */
94 1
    public function getByType(string $type): array
95
    {
96 1
        $components = $this->parent ? $this->parent->getByType($type) : [];
97 1
        $isObject = !in_array($type, self::$primitives, true);
98 1
        foreach ($this->types as $name => $ctype) {
99 1
            if ($type === $ctype || ($isObject && is_a($ctype, $type, true))) {
100 1
                $components[$name] = $this->doGet($name);
101
            }
102
        }
103 1
        return $components;
104
    }
105
106
    /**
107
     * {@inheritDoc}
108
     */
109 1
    public function getFirst(string $type)
110
    {
111 1
        $isObject = !in_array($type, self::$primitives, true);
112 1
        foreach ($this->types as $name => $ctype) {
113 1
            if ($type === $ctype || ($isObject && is_a($ctype, $type, true))) {
114 1
                return $this->doGet($name);
115
            }
116
        }
117 1
        return $this->parent ? $this->parent->getFirst($type) : null;
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123
    public function has($name): bool
124
    {
125
        return isset($this->types[$name]) ||
126
            ($this->parent ? $this->parent->has($name) : false);
127
    }
128
129
    /**
130
     * Retrieves the value
131
     *
132
     * @param string $name The value name
133
     */
134
    abstract protected function doGet(string $name);
135
136
    /**
137
     * Gets all registered component names (excluding any in the parent container).
138
     *
139
     * @return array of strings
140
     */
141 1
    public function getNames(): array
142
    {
143 1
        return array_keys($this->types);
144
    }
145
146
    /**
147
     * Gets the parent container.
148
     *
149
     * @return Container
150
     */
151 1
    public function getParent()
152
    {
153 1
        return $this->parent;
154
    }
155
156
    /**
157
     * Gets the type of component with the given name.
158
     *
159
     * If this container doesn't have a value for that name, it will delegate to
160
     * its parent.
161
     *
162
     * @param string $name The component name
163
     * @return string The component type, either a class name or one of PHP's language types
164
     *     (i.e. bool, int, float, string, array, resource)
165
     */
166 1
    public function getType(string $name)
167
    {
168 1
        return isset($this->types[$name]) ? $this->types[$name] :
169 1
            ($this->parent ? $this->parent->getType($name) : null);
170
    }
171
172
    /**
173
     * Gets a component by name and ensures its type.
174
     *
175
     * If this container doesn't have a value for that name, it will delegate to
176
     * its parent.
177
     *
178
     * If the value isn't an instance of the type provided, an exception is
179
     * thrown, including when the value is `null`.
180
     *
181
     * @param string $name The component name
182
     * @param string $type The expected type
183
     * @return mixed The type-checked component
184
     * @throws \UnexpectedValueException if the `$type` doesn't match the value
185
     */
186 4
    public function named(string $name, string $type)
187
    {
188 4
        if (isset($this->types[$name])) {
189 3
            $ctype = $this->types[$name];
190 3
            $isObject = !in_array($type, self::$primitives, true);
191 3
            if ($type !== $ctype && (!$isObject || !is_a($ctype, $type, true))) {
192 2
                throw new \UnexpectedValueException("A $type was requested, but a $ctype was found");
193
            }
194 1
            return $this->doGet($name);
195 3
        } elseif ($this->parent !== null) {
196 3
            return $this->parent->named($name, $type);
197
        }
198 1
        throw new \UnexpectedValueException("A $type was requested, but null was found");
199
    }
200
}
201