Passed
Push — master ( a657bb...758483 )
by Vitaly
06:27
created

AbstractContainer::get()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 4
Bugs 0 Features 3
Metric Value
c 4
b 0
f 3
dl 0
loc 24
ccs 12
cts 12
cp 1
rs 8.5125
cc 6
eloc 12
nc 4
nop 1
crap 6
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 Interop\Container\ContainerInterface;
9
use samsonframework\di\exception\ContainerException;
10
use samsonframework\di\exception\NotFoundException;
11
use samsonphp\generator\Generator;
12
13
/**
14
 * Abstract dependency injection container.
15
 *
16
 * @package samsonframework\di
17
 */
18
abstract class AbstractContainer implements ContainerInterface
19
{
20
    /** Default logic function name */
21
    const LOGIC_FUNCTION_NAME = 'diContainer';
22
23
    /** @var array[string] Collection of alias => class name for alias resolving */
24
    protected $aliases = array();
25
26
    /** @var array[string] Collection of entity name resolving */
27
    protected $resolver = array();
28
29
    /** @var array[string] Collection of class name dependencies trees */
30
    protected $dependencies = array();
31
32
    /** @var ContainerInterface[] Collection of delegated containers */
33
    protected $delegates = array();
34
35
    /** @var array[string] Collection of dependency parameters */
36
    protected $parameters = array();
37
38
    /** @var \samsonphp\generator\Generator */
39
    protected $generator;
40
41
    /**
42
     * Container constructor.
43
     *
44
     * @param Generator $generator
45
     */
46 6
    public function __construct(Generator $generator)
47
    {
48 6
        $this->generator = $generator;
49 6
    }
50
51
    /**
52
     * Help container resolving interfaces and abstract classes or any entities to
53
     * different one.
54
     *
55
     * @param string $source      Source entity name
56
     * @param string $destination Destination entity name
57
     *
58
     * @return self Chaining
59
     */
60 6
    public function resolve($source, $destination)
61
    {
62 6
        $this->resolver[$source] = $destination;
63
64 6
        return $this;
65
    }
66
67
    /**
68
     * Internal logic handler. Calls generated logic function
69
     * for performing entity creation or search. This is encapsulated
70
     * method for further overriding.
71
     *
72
     * @param string $alias Entity alias
73
     *
74
     * @return mixed Created instance or null
75
     * @throws ContainerException
76
     */
77 3
    protected function logic($alias)
78
    {
79 3
        if (!function_exists(self::LOGIC_FUNCTION_NAME)) {
80 1
            throw new ContainerException('Logic function does not exists');
81
        }
82
83 2
        return call_user_func(self::LOGIC_FUNCTION_NAME, $alias);
84
    }
85
86
    /**
87
     * Finds an entry of the container by its identifier and returns it.
88
     *
89
     * @param string $alias Identifier of the entry to look for.
90
     *
91
     * @throws NotFoundException  No entry was found for this identifier.
92
     * @throws ContainerException Error while retrieving the entry.
93
     *
94
     * @return mixed Entry.
95
     */
96 3
    public function get($alias)
97
    {
98
        // Get pointer from logic
99 3
        $module = $this->logic($alias);
100
101
        // Try delegate lookup
102 2
        if (null === $module) {
103 1
            foreach ($this->delegates as $delegate) {
104
                try {
105 1
                    $module = $delegate->get($alias);
106 1
                } catch (ContainerException $e) {
107
                    // Catch all delegated exceptions
108 1
                } catch (NotFoundException $e) {
109
                    // Catch all delegated exceptions
110
                }
111 1
            }
112 1
        }
113
114 2
        if (null === $module) {
115 1
            throw new NotFoundException($alias);
116
        } else {
117 2
            return $module;
118
        }
119
    }
120
121
    /**
122
     * Implementing delegate lookup feature.
123
     * If current container cannot resolve entity dependency
124
     * resolving process is passed to delegated container.
125
     *
126
     * @param ContainerInterface $container Container for delegate lookup
127
     */
128 6
    public function delegate(ContainerInterface $container)
129
    {
130 6
        $this->delegates[] = $container;
131 6
    }
132
133
    /**
134
     * Returns true if the container can return an entry for the given identifier.
135
     * Returns false otherwise.
136
     *
137
     * @param string $alias Identifier of the entry to look for.
138
     *
139
     * @return boolean
140
     */
141 1
    public function has($alias)
142
    {
143 1
        $found = array_key_exists($alias, $this->dependencies)
144 1
            || in_array($alias, $this->aliases);
145
146 1
        if (!$found) {
147 1
            foreach ($this->delegates as $delegate) {
148 1
                if ($delegate->has($alias)) {
149 1
                    return true;
150
                }
151 1
            }
152 1
        }
153
154 1
        return $found;
155
    }
156
157
    /**
158
     * Generate logic conditions and their implementation for container and its delegates.
159
     *
160
     * @param string     $inputVariable Input condition parameter variable name
161
     * @param bool|false $started       Flag if condition branching has been started
162
     */
163 2
    public function generateConditions($inputVariable = '$alias', $started = false)
164
    {
165
        // Iterate all container dependencies
166 2
        foreach ($this->dependencies as $alias => $entity) {
167
            // Generate condition statement to define if this class is needed
168 2
            $conditionFunc = !$started ? 'defIfCondition' : 'defElseIfCondition';
169
170
            // Create condition branch
171 2
            $condition = $inputVariable . ' === \'' . $alias . '\'';
172
            // If we have an alias for this - add it to condition
173 2
            $condition .= array_key_exists($alias, $this->aliases)
174 2
                ? ' || ' . $inputVariable . ' === \'' . $this->aliases[$alias] . '\''
175 2
                : '';
176
            // Output condition branch
177 2
            $this->generator->$conditionFunc($condition);
178
179
            // Generate condition for each dependency
180 2
            $this->generateCondition($alias, $entity);
181
182
            // Set flag that condition is started
183 2
            $started = true;
184 2
        }
185
186
        /** @var self $delegate Iterate delegated container to get their conditions */
187 1
        foreach ($this->delegates as $delegate) {
188
            // Set current generator
189 1
            if ($delegate instanceof AbstractContainer) {
190 1
                $delegate->generator = $this->generator;
191 1
            }
192 1
            $delegate->generateConditions($inputVariable, $started);
193 1
        }
194 1
    }
195
196
    /**
197
     * Generate dependency injection logic function.
198
     *
199
     * @param string $functionName
200
     *
201
     * @return string PHP logic function code
202
     */
203 2
    public function generateFunction($functionName = self::LOGIC_FUNCTION_NAME)
204
    {
205 2
        $inputVariable = '$aliasOrClassName';
206 2
        $this->generator
207 2
            ->defFunction($functionName, array($inputVariable))
208 2
            ->defVar('static $services')
209 2
            ->defVar($inputVariable)
210 2
            ->newLine();
211
212
        // Generate all container and delegate conditions
213 2
        $this->generateConditions($inputVariable, false);
214
215
        // Add method not found
216 1
        return $this->generator
217 1
            ->endIfCondition()
218 1
            ->endFunction()
219 1
            ->flush();
220
    }
221
222
    /**
223
     * Set container dependency.
224
     *
225
     * @param mixed       $entity     Entity
226
     * @param string|null $alias      Entity alias for simplier finding
227
     * @param array       $parameters Collection of additional parameters
228
     *
229
     * @return self Chaining
230
     */
231
    abstract public function set($entity, $alias = null, array $parameters = array());
232
233
    /**
234
     * Generate container dependency condition code.
235
     *
236
     * @param string $alias  Entity alias
237
     * @param mixed  $entity Entity
238
     */
239
    abstract protected function generateCondition($alias, &$entity);
240
}
241