Completed
Push — master ( 37ac0c...650578 )
by Vitaly
07:20
created

Container::buildDependenciesTree()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 34
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6.0052

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 34
ccs 18
cts 19
cp 0.9474
rs 8.439
cc 6
eloc 16
nc 4
nop 2
crap 6.0052
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
    {
42 1
        preg_match('/\[\s\<\w+?>\s(?<class>[\w\\\\]+)/', (string)$param, $matches);
43 1
        return array_key_exists('class', $matches) && $matches['class'] !== 'array'
44 1
            ? '\\' . ltrim($matches[1], '\\')
45 1
            : null;
46
    }
47
48
    /**
49
     * Recursively build class constructor dependencies tree.
50
     * TODO: Analyze recurrent dependencies and throw an error
51
     *
52
     * @param string $className    Current class name for analyzing
53
     * @param array  $dependencies Reference to tree for filling up
54
     *
55
     * @return array [string] Multidimensional array as dependency tree
56
     * @throws ClassNotFoundException
57
     */
58 1
    protected function buildDependenciesTree($className, array &$dependencies)
59
    {
60
        // We need this class to exists to use reflections, it will try to autoload it also
61 1
        if (class_exists($className)) {
62 1
            $class = new \ReflectionClass($className);
63
            // We can build dependency tree only from constructor dependencies
64 1
            $constructor = $class->getConstructor();
65 1
            if (null !== $constructor) {
66
                // Iterate all dependencies
67 1
                foreach ($constructor->getParameters() as $parameter) {
68
                    // Ignore optional parameters
69 1
                    if (!$parameter->isOptional()) {
70
                        // Read dependency class name
71 1
                        $dependencyClass = $this->getClassName($parameter);
72
73
                        // If we have found dependency class
74 1
                        if ($dependencyClass !== null) {
75
                            // Point dependency class name
76 1
                            $dependencies[$className][$parameter->getName()] = $dependencyClass;
77
                            // Go deeper in recursion and pass new branch there
78 1
                            $this->buildDependenciesTree($dependencyClass, $dependencies);
79 1
                        }
80
81 1
                    } else { // Stop iterating as first optional parameter is met
82 1
                        break;
83
                    }
84 1
                }
85 1
            }
86 1
        } else { // Something went wrong and class is not auto loaded and missing
87
            throw new ClassNotFoundException($className);
88
        }
89
90 1
        return $dependencies;
91
    }
92
93
    /**
94
     * Finds an entry of the container by its identifier and returns it.
95
     *
96
     * @param string $alias Identifier of the entry to look for.
97
     *
98
     * @throws NotFoundException  No entry was found for this identifier.
99
     * @throws ContainerException Error while retrieving the entry.
100
     *
101
     * @return mixed Entry.
102
     */
103
    public function get($alias)
104
    {
105
        // Set pointer to module
106
        $module = &$this->services[$alias];
107
108
        if (null === $module) {
109
            throw new NotFoundException($alias);
110
        } else {
111
            if (!is_object($module)) {
112
                throw new ContainerException($alias);
113
            } else {
114
                return $module;
115
            }
116
        }
117
    }
118
119
    /**
120
     * Returns true if the container can return an entry for the given identifier.
121
     * Returns false otherwise.
122
     *
123
     * @param string $alias Identifier of the entry to look for.
124
     *
125
     * @return boolean
126
     */
127
    public function has($alias)
128
    {
129
        return array_key_exists($alias, $this->services)
130
        || array_key_exists($alias, $this->aliases);
131
    }
132
133
    /**
134
     * Set dependency alias with callback function.
135
     *
136
     * @param callable $callable Callable to return dependency
137
     * @param string   $alias    Dependency name
138
     *
139
     * @return self Chaining
140
     */
141
    public function callback($callable, $alias = null)
142
    {
143
        // TODO: Implement callback() method.
144
    }
145
146
    /**
147
     * Set service dependency. Upon first creation of this class instance
148
     * it would be used everywhere where this dependency is needed.
149
     *
150
     * @param string $className  Fully qualified class name
151
     * @param string $alias      Dependency name
152
     * @param array  $parameters Collection of parameters needed for dependency creation
153
     *
154
     * @return self Chaining
155
     */
156
    public function service($className, $alias = null, array $parameters = array())
157
    {
158
        // TODO: Implement service() method.
159
    }
160
161
    /**
162
     * Set service dependency by passing object instance.
163
     *
164
     * @param mixed  $instance   Instance that needs to be return by this dependency
165
     * @param string $alias      Dependency name
166
     * @param array  $parameters Collection of parameters needed for dependency creation
167
     *
168
     * @return self Chaining
169
     */
170
    public function instance(&$instance, $alias = null, array $parameters = array())
171
    {
172
173
        // TODO: Implement instance() method.
174
    }
175
176
    /**
177
     * Set dependency.
178
     *
179
     * @param string $className  Fully qualified class name
180
     * @param string $alias      Dependency name
181
     * @param array  $parameters Collection of parameters needed for dependency creation
182
     *
183
     * @return ContainerInterface Chaining
184
     */
185 1
    public function set($className, $alias = null, array $parameters = array())
186
    {
187
        // Add this class dependencies to dependency tree
188 1
        $this->dependencies = array_merge(
189 1
            $this->dependencies,
190 1
            $this->buildDependenciesTree($className, $this->dependencies)
191 1
        );
192
193
        // Merge other class constructor parameters
194 1
        $this->dependencies[$className] = array_merge($this->dependencies[$className], $parameters);
195
196
        // Store alias for this class name
197 1
        $this->aliases[$alias] = $className;
198
199 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...
200 1
    }
201
}
202