Completed
Push — master ( afbab4...927ca3 )
by Vitaly
10:31
created

AbstractContainer::build()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

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