Completed
Push — master ( 515c2c...37ac0c )
by Vitaly
04:46
created

Container   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 64.44%

Importance

Changes 13
Bugs 0 Features 7
Metric Value
wmc 18
c 13
b 0
f 7
lcom 2
cbo 3
dl 0
loc 174
ccs 29
cts 45
cp 0.6444
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getClassName() 0 4 3
B buildDependenciesTree() 0 34 6
A get() 0 15 3
A has() 0 4 2
A callback() 0 4 1
A service() 0 4 1
A instance() 0 5 1
A set() 0 13 1
1
<?php
2
/**
3
 * Created by Vitaly Iegorov <[email protected]>.
4
 * on 22.01.16 at 23:53
5
 */
6
namespace samsonframework\di;
7
8
use samsonframework\di\exception\ClassNotFoundException;
9
use samsonframework\di\exception\ContainerException;
10
use samsonframework\di\exception\NotFoundException;
11
12
//TODO: caching
13
//TODO: Interface & abstract class resolving
14
//TODO: Other parameter types(not hintable) resolving
15
//TODO: Lazy creation by default
16
17
/**
18
 * Class Container
19
 * @package samsonframework\di
20
 */
21
class Container implements ContainerInterface
22
{
23
    /** @var array[string] Collection of loaded services */
24
    protected $services = array();
25
26
    /** @var array[string] Collection of alias => class name for alias resolving*/
27
    protected $aliases = array();
28
29
    /** @var array[string] Collection of class name dependencies trees */
30
    protected $dependencies = array();
31
32
    /**
33
     * Get reflection paramater class name type hint if present without
34
     * autoloading and throwing exceptions.
35
     *
36
     * @param \ReflectionParameter $param Parameter for parsing
37
     *
38
     * @return string|null Class name typehint or null
39
     */
40 1
    protected function getClassName(\ReflectionParameter $param) {
41 1
        preg_match('/\[\s\<\w+?>\s([\w\\\\]+)/s', $param->__toString(), $matches);
42 1
        return isset($matches[1]) && $matches[1] !== 'array' ? '\\' . ltrim($matches[1], '\\') : null;
43
    }
44
45
    /**
46
     * Recursively build class constructor dependencies tree.
47
     * TODO: Analyze recurrent dependencies and throw an error
48
     *
49
     * @param string $className    Current class name for analyzing
50
     * @param array  $dependencies Reference to tree for filling up
51
     *
52
     * @return array [string] Multidimensional array as dependency tree
53
     * @throws ClassNotFoundException
54
     */
55 1
    protected function buildDependenciesTree($className, array &$dependencies = array())
56
    {
57
        // We need this class to exists to use reflections, it will try to autoload it also
58 1
        if (class_exists($className)) {
59 1
            $class = new \ReflectionClass($className);
60
            // We can build dependency tree only from constructor dependencies
61 1
            $constructor = $class->getConstructor();
62 1
            if (null !== $constructor) {
63
                // Iterate all dependencies
64 1
                foreach ($constructor->getParameters() as $parameter) {
65
                    // Ignore optional parameters
66 1
                    if (!$parameter->isOptional()) {
67
                        // Read dependency class name
68 1
                        $dependencyClass = $this->getClassName($parameter);
69
70
                        // If we have found dependency class
71 1
                        if ($dependencyClass !== null) {
72
                            // Point dependency class name
73 1
                            $dependencies[$className][$parameter->getName()] = $dependencyClass;
74
                            // Go deeper in recursion and pass new branch there
75 1
                            $this->buildDependenciesTree($dependencyClass, $dependencies);
76 1
                        }
77
78 1
                    } else { // Stop iterating as first optional parameter is met
79 1
                        break;
80
                    }
81 1
                }
82 1
            }
83 1
        } else { // Something went wrong and class is not auto loaded and missing
84
            throw new ClassNotFoundException($className);
85
        }
86
87 1
        return $dependencies;
88
    }
89
90
    /**
91
     * Finds an entry of the container by its identifier and returns it.
92
     *
93
     * @param string $id Identifier of the entry to look for.
94
     *
95
     * @throws NotFoundException  No entry was found for this identifier.
96
     * @throws ContainerException Error while retrieving the entry.
97
     *
98
     * @return mixed Entry.
99
     */
100
    public function get($id)
101
    {
102
        // Set pointer to module
103
        $module = &$this->services[$id];
104
105
        if (null === $module) {
106
            throw new NotFoundException($id);
107
        } else {
108
            if (!is_object($module)) {
109
                throw new ContainerException($id);
110
            } else {
111
                return $module;
112
            }
113
        }
114
    }
115
116
    /**
117
     * Returns true if the container can return an entry for the given identifier.
118
     * Returns false otherwise.
119
     *
120
     * @param string $id Identifier of the entry to look for.
121
     *
122
     * @return boolean
123
     */
124
    public function has($id)
125
    {
126
        return isset($this->services[$id]) || isset($this->aliases[$id]);
127
    }
128
129
    /**
130
     * Set dependency alias with callback function.
131
     *
132
     * @param callable $callable Callable to return dependency
133
     * @param string   $alias    Dependency name
134
     *
135
     * @return self Chaining
136
     */
137
    public function callback($callable, $alias = null)
138
    {
139
        // TODO: Implement callback() method.
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 string $alias      Dependency name
148
     * @param array  $parameters Collection of parameters needed for dependency creation
149
     *
150
     * @return self Chaining
151
     */
152
    public function service($className, $alias = null, array $parameters = array())
153
    {
154
        // TODO: Implement service() method.
155
    }
156
157
    /**
158
     * Set service dependency by passing object instance.
159
     *
160
     * @param mixed  $instance   Instance that needs to be return by this dependency
161
     * @param string $alias      Dependency name
162
     * @param array  $parameters Collection of parameters needed for dependency creation
163
     *
164
     * @return self Chaining
165
     */
166
    public function instance(&$instance, $alias = null, array $parameters = array())
167
    {
168
169
        // TODO: Implement instance() method.
170
    }
171
172
    /**
173
     * Set dependency.
174
     *
175
     * @param string $className  Fully qualified class name
176
     * @param string $alias      Dependency name
177
     * @param array  $parameters Collection of parameters needed for dependency creation
178
     *
179
     * @return ContainerInterface Chaining
180
     */
181 1
    public function set($className, $alias = null, array $parameters = array())
182
    {
183
        // Add this class dependencies to dependency tree
184 1
        $this->dependencies = array_merge(
185 1
            $this->dependencies,
186 1
            $this->buildDependenciesTree($className, $this->dependencies)
187 1
        );
188
189
        // Merge other class constructor parameters
190 1
        $this->dependencies[$className] = array_merge($this->dependencies[$className], $parameters);
191
192 1
        var_dump($this->dependencies);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($this->dependencies); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
193 1
    }
194
}
195