Completed
Push — master ( 1a10c9...f32a6f )
by Vitaly
02:49
created

AbstractContainer::resolve()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
ccs 0
cts 3
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
crap 2
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 9
    public function __construct(Generator $generator)
47
    {
48 9
        $this->generator = $generator;
49 9
    }
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
    public function resolve($source, $destination)
61
    {
62
        $this->resolver[$source] = $destination;
63
64
        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
    protected function logic($alias)
78
    {
79
        if (!function_exists(self::LOGIC_FUNCTION_NAME)) {
80
            throw new ContainerException('Logic function does not exists');
81
        }
82
83
        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
    public function get($alias)
97
    {
98
        // Get pointer from logic
99
        $module = $this->logic($alias);
100
101
        // Try delegate lookup
102
        if (null === $module) {
103
            foreach ($this->delegates as $delegate) {
104
                try {
105
                    $module = $delegate->get($alias);
106
                } catch (ContainerException $e) {
107
                    // Catch all delegated exceptions
108
                } catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class samsonframework\di\exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
109
                    // Catch all delegated exceptions
110
                }
111
            }
112
        }
113
114
        if (null === $module) {
115
            throw new NotFoundException($alias);
116
        } else {
117
            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
    public function delegate(ContainerInterface $container)
129
    {
130
        $this->delegates[] = $container;
131
    }
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
    public function has($alias)
142
    {
143
        $found = array_key_exists($alias, $this->dependencies)
144
            || in_array($alias, $this->aliases);
145
146
        if (!$found) {
147
            foreach ($this->delegates as $delegate) {
148
                if ($delegate->has($alias)) {
149
                    return true;
150
                }
151
            }
152
        }
153
154
        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
    public function generateConditions($inputVariable = '$alias', $started = false)
164
    {
165
        // Iterate all container dependencies
166
        foreach ($this->dependencies as $alias => $entity) {
167
            // Generate condition statement to define if this class is needed
168
            $conditionFunc = !$started ? 'defIfCondition' : 'defElseIfCondition';
169
170
            // Create condition branch
171
            $condition = $inputVariable . ' === \'' . $alias . '\'';
172
            // If we have an alias for this - add it to condition
173
            $condition .= array_key_exists($alias, $this->aliases)
174
                ? ' || ' . $inputVariable . ' === \'' . $this->aliases[$alias] . '\''
175
                : '';
176
            // Output condition branch
177
            $this->generator->$conditionFunc($condition);
178
179
            // Generate condition for each dependency
180
            $this->generateCondition($alias, $entity);
181
182
            // Set flag that condition is started
183
            $started = true;
184
        }
185
186
        /** @var self $delegate Iterate delegated container to get their conditions */
187
        foreach ($this->delegates as $delegate) {
188
            // Set current generator
189
            if ($delegate instanceof AbstractContainer) {
190
                $delegate->generator = $this->generator;
191
            }
192
            $delegate->generateConditions($inputVariable, $started);
193
        }
194
    }
195
196
    /**
197
     * Generate dependency injection logic function.
198
     *
199
     * @param string $functionName
200
     *
201
     * @return string PHP logic function code
202
     */
203
    public function generateFunction($functionName = self::LOGIC_FUNCTION_NAME)
204
    {
205
        $inputVariable = '$aliasOrClassName';
206
        $this->generator
207
            ->defFunction($functionName, array($inputVariable))
208
            ->defVar('static $services')
209
            ->defVar($inputVariable)
210
            ->newLine();
211
212
        // Generate all container and delegate conditions
213
        $this->generateConditions($inputVariable, false);
214
215
        // Add method not found
216
        return $this->generator
217
            ->endIfCondition()
218
            ->endFunction()
219
            ->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