Completed
Push — master ( 16a769...5f2552 )
by Vitaly
03:04
created

Container::get()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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