Completed
Push — master ( c65609...2724fa )
by Vitaly
11:34
created

Container   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 90.91%

Importance

Changes 44
Bugs 2 Features 19
Metric Value
wmc 22
c 44
b 2
f 19
lcom 2
cbo 3
dl 0
loc 176
ccs 40
cts 44
cp 0.9091
rs 10

9 Methods

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