Completed
Push — master ( cf3632...f41966 )
by Vitaly
09:23
created

AbstractContainer::generateConditions()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 24
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
ccs 12
cts 12
cp 1
rs 8.6845
cc 4
eloc 9
nc 6
nop 2
crap 4
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 2
    public function __construct(Generator $generator)
47
    {
48 2
        $this->generator = $generator;
49 2
    }
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
     */
76 1
    protected function logic($alias)
77
    {
78 1
        return call_user_func(self::LOGIC_FUNCTION_NAME, $alias);
79
    }
80
81
    /**
82
     * Finds an entry of the container by its identifier and returns it.
83
     *
84
     * @param string $alias Identifier of the entry to look for.
85
     *
86
     * @throws NotFoundException  No entry was found for this identifier.
87
     * @throws ContainerException Error while retrieving the entry.
88
     *
89
     * @return mixed Entry.
90
     */
91 1
    public function get($alias)
92
    {
93
        // Get pointer from logic
94 1
        $module = $this->logic($alias);
95
96
        // Try delegate lookup
97 1
        if (null === $module) {
98
            foreach ($this->delegates as $delegate) {
99
                try {
100
                    $module = $delegate->get($alias);
101
                } catch (ContainerException $e) {
102
                    // Catch all delegated exceptions
103
                } catch (NotFoundException $e) {
104
                    // Catch all delegated exceptions
105
                }
106
            }
107
        }
108
109 1
        if (null === $module) {
110
            throw new NotFoundException($alias);
111
        } else {
112 1
            if (!is_object($module)) {
113
                throw new ContainerException($alias);
114
            } else {
115 1
                return $module;
116
            }
117
        }
118
    }
119
120
    /**
121
     * Implementing delegate lookup feature.
122
     * If current container cannot resolve entity dependency
123
     * resolving process is passed to delegated container.
124
     *
125
     * @param ContainerInterface $container Container for delegate lookup
126
     */
127 2
    public function delegate(ContainerInterface $container)
128
    {
129 2
        $this->delegates[] = $container;
130 2
    }
131
132
    /**
133
     * Returns true if the container can return an entry for the given identifier.
134
     * Returns false otherwise.
135
     *
136
     * @param string $alias Identifier of the entry to look for.
137
     *
138
     * @return boolean
139
     */
140 1
    public function has($alias)
141
    {
142 1
        $found = array_key_exists($alias, $this->dependencies)
143 1
            || array_key_exists($alias, $this->aliases);
144
145 1
        if (!$found) {
146
            foreach ($this->delegates as $delegate) {
147
                if ($delegate->has($alias)) {
148
                    return true;
149
                }
150
            }
151
        }
152
153 1
        return $found;
154
    }
155
156
    /**
157
     * Generate logic conditions and their implementation for container and its delegates.
158
     *
159
     * @param string     $inputVariable Input condition parameter variable name
160
     * @param bool|false $started Flag if condition branching has been started
161
     */
162 2
    public function generateConditions($inputVariable = '$alias', $started = false)
163
    {
164
        // Iterate all container dependencies
165 2
        foreach ($this->dependencies as $alias => $entity) {
166
            // Generate condition statement to define if this class is needed
167 2
            $conditionFunc = !$started ? 'defIfCondition' : 'defElseIfCondition';
168
169
            // Output condition branch
170 2
            $this->generator->$conditionFunc($inputVariable . ' === \'' . $alias . '\'');
171
172
            // Generate condition for each dependency
173 2
            $this->generateCondition($alias, $entity);
174
175
            // Set flag that condition is started
176 2
            $started = true;
177 2
        }
178
179
        /** @var self $delegate Iterate delegated container to get their conditions */
180 2
        foreach ($this->delegates as $delegate) {
181
            // Set current generator
182 2
            $delegate->generator = $this->generator;
0 ignored issues
show
Bug introduced by
Accessing generator on the interface Interop\Container\ContainerInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
183 2
            $delegate->generateConditions($inputVariable, $started);
184 2
        }
185 2
    }
186
187
    /**
188
     * Generate dependency injection logic function.
189
     *
190
     * @param string $functionName
191
     *
192
     * @return string PHP logic function code
193
     */
194 2
    public function generateFunction($functionName = self::LOGIC_FUNCTION_NAME)
195
    {
196 2
        $inputVariable = '$aliasOrClassName';
197 2
        $this->generator
198 2
            ->defFunction($functionName, array($inputVariable))
199 2
            ->defVar('static $services')
200 2
            ->newLine();
201
202
        // Generate all container and delegate conditions
203 2
        $this->generateConditions($inputVariable, false);
204
205
        // Add method not found
206 2
        return $this->generator
207 2
            ->endIfCondition()
208 2
            ->endFunction()
209 2
            ->flush();
210
    }
211
212
    /**
213
     * Set container dependency.
214
     *
215
     * @param mixed         $entity Entity
216
     * @param string|null   $alias  Entity alias for simplier finding
217
     * @param array         $parameters Collection of additional parameters
218
     *
219
     * @return self Chaining
220
     */
221
    abstract public function set($entity, $alias = null, array $parameters = array());
222
223
    /**
224
     * Generate container dependency condition code.
225
     * @param string    $alias Entity alias
226
     * @param mixed     $entity Entity
227
     */
228
    abstract protected function generateCondition($alias, $entity);
229
}
230