Completed
Push — master ( d10f54...00ced4 )
by
unknown
05:32
created

XmlResolver::resolve()   C

Complexity

Conditions 14
Paths 59

Size

Total Lines 62
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 14

Importance

Changes 0
Metric Value
dl 0
loc 62
rs 6.1699
c 0
b 0
f 0
ccs 21
cts 21
cp 1
cc 14
eloc 20
nc 59
nop 2
crap 14

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style introduced by
End of line character is invalid; expected "\n" but found "\r\n"
Loading history...
2
/**
3
 * Created by Ruslan Molodyko.
4
 * Date: 12.09.2016
5
 * Time: 6:33
6
 */
7
namespace samsonframework\container\definition\resolver\xml;
8
9
use samsonframework\container\definition\builder\DefinitionBuilder;
10
use samsonframework\container\definition\ClassDefinition;
11
use samsonframework\container\definition\exception\ClassDefinitionAlreadyExistsException;
12
use samsonframework\container\definition\exception\MethodDefinitionAlreadyExistsException;
13
use samsonframework\container\definition\exception\ParameterDefinitionAlreadyExistsException;
14
use samsonframework\container\definition\exception\PropertyDefinitionAlreadyExistsException;
15
use samsonframework\container\definition\MethodDefinition;
16
use samsonframework\container\definition\reference\BoolReference;
17
use samsonframework\container\definition\reference\ClassReference;
18
use samsonframework\container\definition\reference\CollectionItem;
19
use samsonframework\container\definition\reference\CollectionReference;
20
use samsonframework\container\definition\reference\ConstantReference;
21
use samsonframework\container\definition\reference\FloatReference;
22
use samsonframework\container\definition\reference\IntegerReference;
23
use samsonframework\container\definition\reference\NullReference;
24
use samsonframework\container\definition\reference\ParameterReference;
25
use samsonframework\container\definition\reference\ReferenceInterface;
26
use samsonframework\container\definition\reference\ServiceReference;
27
use samsonframework\container\definition\reference\StringReference;
28
use samsonframework\container\definition\resolver\exception\ReferenceNotImplementsException;
29
30
/**
31
 * Class XmlResolver
32
 *
33
 * @author Ruslan Molodyko <[email protected]>
34
 */
35
class XmlResolver
36
{
37
    /** How parameter presents in xml code */
38
    const PARAMETERS_KEY = 'parameters';
39
    /** How dependencies presents in xml code */
40
    const DEPENDENCIES_KEY = 'dependencies';
41
    /** How instance presents in xml code */
42
    const INSTANCE_KEY = 'definition';
43
44
    /**
45
     * Resolve xml code
46
     *
47
     * @param DefinitionBuilder $definitionBuilder
48
     * @param $xml
49
     *
50
     * @throws ClassDefinitionAlreadyExistsException
51
     * @throws \InvalidArgumentException
52
     * @throws PropertyDefinitionAlreadyExistsException
53
     * @throws ReferenceNotImplementsException
54
     * @throws MethodDefinitionAlreadyExistsException
55
     * @throws ParameterDefinitionAlreadyExistsException
56
     * @throws \samsonframework\container\definition\parameter\exception\ParameterAlreadyExistsException
57
     */
58 1
    public function resolve(DefinitionBuilder $definitionBuilder, $xml)
59
    {
60
        /**
61
         * Iterate config and resolve single instance
62
         *
63
         * @var string $key
64
         * @var array $arrayData
65
         */
66 1
        foreach ($this->xml2array(new \SimpleXMLElement($xml)) as $key => $arrayData) {
67
            // Resolve parameters
68 1
            if ($key === self::PARAMETERS_KEY) {
69
                // Define parameters
70 1
                foreach ($arrayData as $parameterKey => $parameterArray) {
71 1
                    $definitionBuilder->defineParameter($parameterKey, $this->resolveDependency($parameterArray));
72
                }
73
            }
74
            // Resolve dependencies
75 1
            if ($key === self::DEPENDENCIES_KEY) {
76
                // Iterate instances
77 1
                foreach ($arrayData as $dependencyKey => $definitionsArrayData) {
78
                    // Get only definition instances
79 1
                    if ($dependencyKey === self::INSTANCE_KEY) {
80
                        /**
81
                         * If we have only one instance we need to add array
82
                         * @var array $collection
83
                         */
84 1
                        $collection = !array_key_exists(0,
85 1
                            $definitionsArrayData) ? [$definitionsArrayData] : $definitionsArrayData;
86
                        /**
87
                         * Iterate collection of instances
88
                         * @var array $definitionsArrayData
89
                         */
90 1
                        foreach ($collection as $definitionArrayData) {
91
                            /**
92
                             * Create class definition
93
                             * @var ClassDefinition $classDefinition
94
                             */
95 1
                            $classDefinition = $definitionBuilder->addDefinition($definitionArrayData['@attributes']['class']);
96
                            // Resolve constructor
97 1
                            if (array_key_exists('constructor', $definitionArrayData)) {
98 1
                                $this->resolveConstructor($classDefinition, $definitionArrayData['constructor']);
99
                            }
100
                            // Resolve methods
101 1
                            if (array_key_exists('methods', $definitionArrayData)) {
102
                                // Iteare methods
103 1
                                foreach ($definitionArrayData['methods'] as $methodName => $methodArray) {
104 1
                                    $this->resolveMethod($classDefinition, $methodArray, $methodName);
105
                                }
106
                            }
107
                            // Resolve properties
108 1
                            if (array_key_exists('properties', $definitionArrayData)) {
109
                                // Iterate properties
110 1
                                foreach ($definitionArrayData['properties'] as $propertyName => $propertyArray) {
111 1
                                    $this->resolveProperty($classDefinition, $propertyArray, $propertyName);
112
                                }
113
                            }
114
                        }
115
                    }
116
                }
117
            }
118
        }
119 1
    }
120
121
    /**
122
     * Resolve constructor
123
     *
124
     * @param ClassDefinition $classDefinition
125
     * @param array $constructorArray
126
     * @throws MethodDefinitionAlreadyExistsException
127
     * @throws \InvalidArgumentException
128
     * @throws ParameterDefinitionAlreadyExistsException
129
     * @throws ReferenceNotImplementsException
130
     */
131 1 View Code Duplication
    public function resolveConstructor(ClassDefinition $classDefinition, array $constructorArray)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
132
    {
133 1
        $methodDefinition = $classDefinition->defineConstructor();
134 1
        if (array_key_exists('arguments', $constructorArray)) {
135 1
            $this->resolveArguments($methodDefinition, $constructorArray['arguments']);
0 ignored issues
show
Compatibility introduced by
$methodDefinition of type object<samsonframework\c...MethodBuilderInterface> is not a sub-type of object<samsonframework\c...ition\MethodDefinition>. It seems like you assume a concrete implementation of the interface samsonframework\containe...\MethodBuilderInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
136
        }
137 1
    }
138
139
    /**
140
     * Resolve property
141
     *
142
     * @param ClassDefinition $classDefinition
143
     * @param array $propertyArray
144
     * @param string $propertyName
145
     * @throws \InvalidArgumentException
146
     * @throws ReferenceNotImplementsException
147
     * @throws PropertyDefinitionAlreadyExistsException
148
     */
149 1
    public function resolveProperty(ClassDefinition $classDefinition, array $propertyArray, string $propertyName)
150
    {
151 1
        $propertyDefinition = $classDefinition->defineProperty($propertyName);
152 1
        $propertyDefinition->defineDependency($this->resolveDependency($propertyArray));
153 1
    }
154
155
    /**
156
     * Resolve method
157
     *
158
     * @param ClassDefinition $classDefinition
159
     * @param array $methodArray
160
     * @param string $methodName
161
     * @throws MethodDefinitionAlreadyExistsException
162
     * @throws \InvalidArgumentException
163
     * @throws ParameterDefinitionAlreadyExistsException
164
     * @throws ReferenceNotImplementsException
165
     */
166 1 View Code Duplication
    public function resolveMethod(ClassDefinition $classDefinition, array $methodArray, string $methodName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168 1
        $methodDefinition = $classDefinition->defineMethod($methodName);
169 1
        if (array_key_exists('arguments', $methodArray)) {
170 1
            $this->resolveArguments($methodDefinition, $methodArray['arguments']);
0 ignored issues
show
Compatibility introduced by
$methodDefinition of type object<samsonframework\c...MethodBuilderInterface> is not a sub-type of object<samsonframework\c...ition\MethodDefinition>. It seems like you assume a concrete implementation of the interface samsonframework\containe...\MethodBuilderInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
171
        }
172 1
    }
173
174
    /**
175
     * Resolve method/constructor arguments
176
     *
177
     * @param MethodDefinition $methodDefinition
178
     * @param array $arguments
179
     * @throws ParameterDefinitionAlreadyExistsException
180
     * @throws \InvalidArgumentException
181
     * @throws ReferenceNotImplementsException
182
     */
183 1
    public function resolveArguments(MethodDefinition $methodDefinition, array $arguments)
184
    {
185 1
        foreach ($arguments as $argumentName => $argumentValue) {
186
            $methodDefinition
187 1
                ->defineParameter($argumentName)
188 1
                    ->defineDependency($this->resolveDependency($argumentValue));
189
        }
190 1
    }
191
192
    /**
193
     * Resolve dependency
194
     *
195
     * @param $data
196
     * @return ReferenceInterface
197
     * @throws \InvalidArgumentException
198
     * @throws ReferenceNotImplementsException
199
     */
200 1
    public function resolveDependency($data): ReferenceInterface
201
    {
202
        // Get value type
203 1
        $type = $data['@attributes']['type'] ?? 'string';
204
        // Get value
205 1
        $value = $data['@attributes']['value'] ?? null;
206
        // When that is not a collection then value can not be null
207 1
        if ($type !== 'collection' && $value === null) {
208
            throw new \InvalidArgumentException(sprintf('Value for type "%s" have to be specified', $type));
209
        }
210
        // Resolve type
211
        switch ($type) {
212 1
            case 'text':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
213 1
            case 'string':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
214 1
                return new StringReference($value);
215 1
            case 'int':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
216 1
            case 'integer':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
217
                return new IntegerReference($value);
218 1
            case 'float':
219
                return new FloatReference($value);
220 1
            case 'boolean':
221 1
            case 'bool':
222
                return new BoolReference($value);
223 1
            case 'class':
224 1
                return new ClassReference($value);
225 1
            case 'constant':
226 1
                return new ConstantReference($value);
227 1
            case 'service':
228
                return new ServiceReference($value);
229 1
            case 'null':
230
                return new NullReference();
231 1
            case 'parameter':
232 1
                return new ParameterReference($value);
233 1
            case 'collection':
234 1
                return $this->resolveCollection($data);
235
            default:
236
                throw new ReferenceNotImplementsException();
237
        }
238
    }
239
240
    /**
241
     * Resolve collection type
242
     *
243
     * @param array $data
244
     * @return CollectionReference
245
     * @throws ReferenceNotImplementsException
246
     * @throws \InvalidArgumentException
247
     */
248 1
    public function resolveCollection(array $data): CollectionReference
249
    {
250 1
        $collection = new CollectionReference();
251 1
        if (array_key_exists('item', $data)) {
252
            /** @var array $itemCollection */
253 1
            $itemCollection = array_key_exists('key', $data['item']) && array_key_exists('value', $data['item'])
254 1
                ? [$data['item']]
255 1
                : $data['item'];
256
            /** @var array $item */
257 1
            foreach ($itemCollection as $item) {
258 1
                $collection->addItem(new CollectionItem(
259 1
                    $this->resolveDependency($item['key']),
260 1
                    $this->resolveDependency($item['value'])
261
                ));
262
            }
263
        }
264 1
        return $collection;
265
    }
266
267
    /**
268
     * Convert xml to array
269
     *
270
     * @param $xmlObject
271
     * @param array $out
272
     * @return array
273
     */
274 1
    protected function xml2array($xmlObject, array $out = []): array
275
    {
276 1
        foreach ((array)$xmlObject as $index => $node) {
277 1
            $out[$index] = (is_object($node) || is_array($node)) ? $this->xml2array($node) : $node;
278
        }
279 1
        return $out;
280
    }
281
}
282