ModelTransformer   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 51
c 3
b 0
f 0
lcom 1
cbo 3
dl 0
loc 237
rs 8.3206

7 Methods

Rating   Name   Duplication   Size   Complexity  
C findSupportedObjectTransformer() 0 24 7
C addModelTransformer() 0 36 8
A getObjectTransformers() 0 17 4
A getModelTransformers() 0 14 3
C supports() 0 27 11
D transform() 0 31 9
C findSupportedModelTransformer() 0 22 9

How to fix   Complexity   

Complex Class

Complex classes like ModelTransformer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ModelTransformer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Tonic\Component\ApiLayer\ModelTransformer;
4
5
use Tonic\Component\ApiLayer\ModelTransformer\Exception\UnsupportedTransformationException;
6
7
/**
8
 * Manages transformers.
9
 */
10
class ModelTransformer implements ModelTransformerInterface
11
{
12
    /**
13
     * @var ModelTransformerInterface[]|ContextualModelTransformerInterface[]
14
     */
15
    private $modelTransformers = [];
16
17
    /**
18
     * @var ObjectTransformerInterface[]
19
     */
20
    private $objectTransformers = [];
21
22
    /**
23
     * @var array
24
     */
25
    private $sortedModelTransformers = [];
26
27
    /**
28
     * @var array
29
     */
30
    private $sortedObjectTransformers = [];
31
32
    /**
33
     * @param ModelTransformerInterface $modelTransformer
34
     * @param int $priority
35
     *
36
     * @return $this|ModelTransformerInterface
37
     *
38
     * @throws \RuntimeException
39
     */
40
    public function addModelTransformer($modelTransformer, $priority = 0)
41
    {
42
        if (!(($modelTransformer instanceof ModelTransformerInterface)
43
            || ($modelTransformer instanceof ContextualModelTransformerInterface)
44
            || ($modelTransformer instanceof ObjectTransformerInterface)
45
        )
46
        ) {
47
            throw new \RuntimeException(
48
                sprintf('Model transformer should be an instance of "%s", "%s" or "%s"', ModelTransformerInterface::class, ContextualModelTransformerInterface::class, ObjectTransformerInterface::class)
49
            );
50
        }
51
52
        if ($modelTransformer instanceof ObjectTransformerInterface) {
53
            if (!isset($this->objectTransformers[$modelTransformer->getSupportedClass()])) {
54
                $this->objectTransformers[$modelTransformer->getSupportedClass()] = [];
55
            }
56
57
            if (!isset($this->objectTransformers[$modelTransformer->getSupportedClass()][$modelTransformer->getTargetClass()])) {
58
                $this->objectTransformers[$modelTransformer->getSupportedClass()][$modelTransformer->getTargetClass()] = [];
59
            }
60
61
            $this->objectTransformers[$modelTransformer->getSupportedClass()][$modelTransformer->getTargetClass()][$priority] = $modelTransformer;
62
            unset($this->sortedObjectTransformers);
63
64
            return $this;
65
        }
66
67
        if (!isset($this->modelTransformers[$priority])) {
68
            $this->modelTransformers[$priority] = [];
69
        }
70
71
        $this->modelTransformers[$priority][] = $modelTransformer;
72
        unset($this->sortedModelTransformers);
73
74
        return $this;
75
    }
76
77
    /**
78
     * @return ObjectTransformerInterface[]
79
     */
80
    public function getObjectTransformers()
81
    {
82
        if (isset($this->sortedObjectTransformers)) {
83
            return $this->sortedObjectTransformers;
84
        }
85
86
        $this->sortedObjectTransformers = [];
87
        foreach ($this->objectTransformers as $supportedClass => $objectTransformersBySupportedClass) {
88
            $this->sortedObjectTransformers[$supportedClass] = [];
89
            foreach ($objectTransformersBySupportedClass as $targetClass => $objectTransformersByPriorities) {
0 ignored issues
show
Bug introduced by
The expression $objectTransformersBySupportedClass of type object<Tonic\Component\A...ctTransformerInterface> is not traversable.
Loading history...
90
                ksort($objectTransformersByPriorities);
91
                $this->sortedObjectTransformers[$supportedClass][$targetClass] = array_values($objectTransformersByPriorities)[0];
92
            }
93
        }
94
95
        return $this->sortedObjectTransformers;
96
    }
97
98
    /**
99
     * @return ModelTransformerInterface[]
100
     */
101
    public function getModelTransformers()
102
    {
103
        if (isset($this->sortedModelTransformers)) {
104
            return $this->sortedModelTransformers;
105
        }
106
107
        krsort($this->modelTransformers);
108
        $this->sortedModelTransformers = [];
109
        foreach ($this->modelTransformers as $modelTransformers) {
110
            $this->sortedModelTransformers = array_merge($this->sortedModelTransformers, $modelTransformers);
111
        }
112
113
        return $this->sortedModelTransformers;
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function supports($object, $targetClass)
120
    {
121
        if (null === $object) {
122
            // transformation of nothing ot anything is nothing
123
            return true;
124
        }
125
126
        /** @var ContextInterface $context */
127
        $context = (func_num_args() == 3) ? func_get_arg(2) : null;
128
129
        $objectTransformers = $this->getObjectTransformers();
130
        if (is_object($object) && isset($objectTransformers[get_class($object)]) && isset($objectTransformers[get_class($object)][$targetClass])) {
131
            return true;
132
        }
133
134
        foreach ($this->getModelTransformers() as $modelTransformer) {
135
            if (($modelTransformer instanceof ContextualModelTransformerInterface) && $modelTransformer->supports($object, $targetClass, $context)) {
136
                return true;
137
            }
138
139
            if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
140
                return true;
141
            }
142
        }
143
144
        return false;
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function transform($object, $targetClass)
151
    {
152
        if (null === $object) {
153
            // transformation of nothing ot anything is nothing
154
            return null;
155
        }
156
157
        /** @var ContextInterface $context */
158
        $context = (func_num_args() == 3) ? func_get_arg(2) : null;
159
160
        $modelTransformer = $this->findSupportedModelTransformer($object, $targetClass, $context);
161
162
        if (is_object($object) && ($modelTransformer instanceof ObjectTransformerInterface)) {
163
            return $modelTransformer->transform($object);
164
        }
165
166
        if ($modelTransformer instanceof ContextualModelTransformerInterface) {
167
            return $modelTransformer->transform($object, $targetClass, $context);
168
        }
169
170
        if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
171
            return $modelTransformer->transform($object, $targetClass);
172
        }
173
174
        $objectType = is_object($object) ? get_class($object) : gettype($object);
175
        throw new UnsupportedTransformationException(sprintf(
176
            'Can not transform object of type "%s" to object of type "%s"',
177
            $objectType,
178
            $targetClass
179
        ));
180
    }
181
182
    /**
183
     * Finds and returns model transformer which supports specified object and target class.
184
     *
185
     * @param object|array $object
186
     * @param string $targetClass
187
     * @param ContextInterface|null $context
188
     *
189
     * @return null|ModelTransformerInterface|ContextualModelTransformerInterface|ObjectTransformerInterface
190
     */
191
    public function findSupportedModelTransformer($object, $targetClass, ContextInterface $context = null)
192
    {
193
        if (is_object($object) && ($objectTransformer = $this->findSupportedObjectTransformer(get_class($object), $targetClass))) {
194
            return $objectTransformer;
195
        }
196
197
        foreach ($this->getModelTransformers() as $modelTransformer) {
198
            if (($modelTransformer instanceof ContextualModelTransformerInterface) && $modelTransformer->supports($object, $targetClass, $context)) {
199
                return $modelTransformer;
200
            }
201
202
            if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
203
                return $modelTransformer;
204
            }
205
206
            if ($modelTransformer instanceof ObjectTransformerInterface) {
207
                continue;
208
            }
209
        }
210
211
        return null;
212
    }
213
214
    /**
215
     * Tries to find supported object transformer.
216
     *
217
     * @param string $supportedClass
218
     * @param string $targetClass
219
     *
220
     * @return null|ObjectTransformerInterface
221
     */
222
    private function findSupportedObjectTransformer($supportedClass, $targetClass)
223
    {
224
        $objectTransformers = $this->getObjectTransformers();
225
        if (isset($objectTransformers[$supportedClass]) && isset($objectTransformers[$supportedClass][$targetClass])) {
226
            return $objectTransformers[$supportedClass][$targetClass];
227
        }
228
229
        $supportedReflectionClass = new \ReflectionClass($supportedClass);
230
231
        if ($supportedReflectionClass->getParentClass()
232
            && ($objectTransformer = $this->findSupportedObjectTransformer($supportedReflectionClass->getParentClass()->getName(), $targetClass))
233
        ) {
234
            return $objectTransformer;
235
        }
236
237
        $targetReflectionClass = new \ReflectionClass($targetClass);
238
        if ($targetReflectionClass->getParentClass()
239
            && ($objectTransformer = $this->findSupportedObjectTransformer($supportedClass, $targetReflectionClass->getParentClass()->getName()))
240
        ) {
241
            return $objectTransformer;
242
        }
243
244
        return null;
245
    }
246
}
247