Completed
Push — master ( 2724fa...ad7342 )
by Vitaly
05:00
created

Container::getServices()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 8.125

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 4
cts 8
cp 0.5
rs 8.8571
cc 5
eloc 9
nc 4
nop 1
crap 8.125
1
<?php declare(strict_types=1);
2
/**
3
 * Created by Vitaly Iegorov <[email protected]>.
4
 * on 26.01.16 at 15:11
5
 */
6
namespace samsonframework\di;
7
8
use samsonframework\container\ContainerInterface;
9
use samsonframework\di\exception\ClassNotFoundException;
10
use samsonframework\di\exception\ContainerException;
11
12
/**
13
 * Dependency container.
14
 *
15
 * @author Vitaly Iegorov <[email protected]>
16
 */
17
class Container implements ContainerInterface
18
{
19
    /** @var array Collection of instantiated service instances */
20
    protected $serviceInstances = [];
21
22
    /** @var array[string] Collection of loaded services */
23
    protected $services = [];
24
25
    /** @var array[string] Collection of alias => class name for alias resolving */
26
    protected $aliases = [];
27
28
    /** @var array[string] Collection of class name dependencies trees */
29
    protected $dependencies = [];
30
31
    /** @var ContainerInterface[] Collection of delegated containers */
32
    protected $delegates = [];
33
34
    /** @var callable Dependency resolving function callable */
35
    protected $logicCallable;
36
37
    /** @var array Collection of scope => [alias => class_name] */
38
    protected $scopes = [];
39
40
    /**
41
     * Wrapper for calling dependency resolving function.
42
     *
43
     * @param string $dependency Dependency name
44
     *
45
     * @return mixed Created instance or null
46
     * @throws ContainerException
47
     */
48 5
    protected function logic($dependency)
49
    {
50 5
        if (!is_callable($this->logicCallable)) {
51 2
            throw new ContainerException('Logic function is not callable');
52
        }
53
54 4
        return call_user_func($this->logicCallable, $dependency);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     *
60
     * @throws \samsonframework\di\exception\ContainerException
61
     * @throws \samsonframework\di\exception\ClassNotFoundException
62
     */
63 5
    public function get($dependency)
64
    {
65
        // Get pointer from logic
66 5
        $module = $this->logic($dependency) ?? $this->getFromDelegate($dependency);
67
68 4
        if (null === $module) {
69 1
            throw new ClassNotFoundException($dependency);
70
        } else {
71 3
            return $module;
72
        }
73
    }
74
75
    /**
76
     * Try to find dependency in delegate container.
77
     *
78
     * @param string $dependency Dependency identifier
79
     *
80
     * @return mixed Delegate found dependency
81
     *
82
     * @throws \Interop\Container\Exception\ContainerException
83
     */
84 2
    protected function getFromDelegate(string $dependency)
85
    {
86
        // Try delegate lookup
87 2
        foreach ($this->delegates as $delegate) {
88
            try {
89 2
                return $delegate->get($dependency);
90 1
            } catch (ContainerException $e) {
91
                // Catch all delegated exceptions
92 1
            } catch (ClassNotFoundException $e) {
93
                // Catch all delegated exceptions
94
            }
95
        }
96
97 1
        return null;
98
    }
99
100
    /**
101
     * Implementing delegate lookup feature.
102
     * If current container cannot resolve entity dependency
103
     * resolving process is passed to delegated container.
104
     *
105
     * @param ContainerInterface $container Container for delegate lookup
106
     */
107 3
    public function delegate(ContainerInterface $container)
108
    {
109 3
        $this->delegates[] = $container;
110 3
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 2
    public function has($dependency) : bool
116
    {
117 2
        $found = array_key_exists($dependency, $this->dependencies)
118 2
            || in_array($dependency, $this->aliases, true);
119
120
        // Return true if found or try delegate containers
121 2
        return $found ?: $this->hasDelegate($dependency);
122
    }
123
124
    /**
125
     * Define if delegate containers have dependency.
126
     *
127
     * @param string $dependency Dependency identifier
128
     *
129
     * @return bool True if delegate containers have dependency
130
     */
131 2
    protected function hasDelegate(string $dependency) : bool
132
    {
133 2
        foreach ($this->delegates as $delegate) {
134 1
            if ($delegate->has($dependency)) {
135 1
                return true;
136
            }
137
        }
138
139 2
        return false;
140
    }
141
142
    /**
143
     * Set service dependency. Upon first creation of this class instance
144
     * it would be used everywhere where this dependency is needed.
145
     *
146
     * @param string $className  Fully qualified class name
147
     * @param array  $parameters Collection of parameters needed for dependency creation
148
     * @param string $alias      Dependency name
149
     *
150
     * @return ContainerInterface Chaining
151
     */
152 9
    public function service($className, array $parameters = [], string $alias = null) : ContainerInterface
153
    {
154 9
        $this->services[$className] = $className;
155
156 9
        return $this->set($className, $parameters, $alias);
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162 9
    public function set($className, array $dependencies = [], string $alias = null) : ContainerInterface
163
    {
164
        // Create dependencies collection for class name
165 9
        if (!array_key_exists($className, $this->dependencies)) {
166 9
            $this->dependencies[$className] = [];
167
        }
168
        
169
        // Merge other class constructor parameters
170 9
        $this->dependencies[$className] = array_merge($this->dependencies[$className], $dependencies);
171
172
        // Store alias for this class name
173 9
        $this->aliases[$className] = $alias;
174
175 9
        return $this;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181 1
    public function getServices(string $filterScope = null) : array
182
    {
183 1
        $filtered = [];
184 1
        if ($filterScope !== null && array_key_exists($filterScope, $this->scopes)) {
185
            foreach ($this->scopes[$filterScope] as $alias => $className) {
186
                if (array_key_exists($alias, $this->serviceInstances)) {
187
                    $filtered[$alias] = $this->serviceInstances[$alias];
188
                }
189
            }
190
            return $filtered;
191
        } else {
192 1
            return $this->serviceInstances;
193
        }
194
    }
195
}
196