Passed
Push — master ( fe5751...2e2189 )
by Dmitry
04:33
created

ModelTransformer::findSupportedModelTransformer()   C

Complexity

Conditions 9
Paths 6

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 23
rs 5.8541
cc 9
eloc 12
nc 6
nop 3
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
        /** @var ContextInterface $context */
122
        $context = (func_num_args() == 3) ? func_get_arg(2) : null;
123
124
        $objectTransformers = $this->getObjectTransformers();
125
        if (isset($objectTransformers[get_class($object)]) && isset($objectTransformers[get_class($object)][$targetClass])) {
126
            return true;
127
        }
128
129
        foreach ($this->getModelTransformers() as $modelTransformer) {
130
            if (($modelTransformer instanceof ContextualModelTransformerInterface) && $modelTransformer->supports($object, $targetClass, $context)) {
131
                return true;
132
            }
133
134
            if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
135
                return true;
136
            }
137
        }
138
139
        return false;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function transform($object, $targetClass)
146
    {
147
        /** @var ContextInterface $context */
148
        $context = (func_num_args() == 3) ? func_get_arg(2) : null;
149
150
        $modelTransformer = $this->findSupportedModelTransformer($object, $targetClass, $context);
151
152
        if ($modelTransformer instanceof ObjectTransformerInterface) {
153
            return $modelTransformer->transform($object);
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 145 can also be of type array; however, Tonic\Component\ApiLayer...rInterface::transform() does only seem to accept object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
154
        }
155
156
        if ($modelTransformer instanceof ContextualModelTransformerInterface) {
157
            return $modelTransformer->transform($object, $targetClass, $context);
158
        }
159
160
        if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
161
            return $modelTransformer->transform($object, $targetClass);
162
        }
163
164
        $objectType = is_object($object) ? get_class($object) : gettype($object);
165
        throw new UnsupportedTransformationException(sprintf(
166
            'Can not transform object of type "%s" to object of type "%s"',
167
            $objectType,
168
            $targetClass
169
        ));
170
    }
171
172
    /**
173
     * Finds and returns model transformer which supports specified object and target class.
174
     *
175
     * @param object|array $object
176
     * @param string $targetClass
177
     * @param ContextInterface|null $context
178
     *
179
     * @return null|ModelTransformerInterface|ContextualModelTransformerInterface|ObjectTransformerInterface
180
     */
181
    public function findSupportedModelTransformer($object, $targetClass, ContextInterface $context = null)
182
    {
183
        $objectTransformers = $this->getObjectTransformers();
184
        if (isset($objectTransformers[get_class($object)]) && isset($objectTransformers[get_class($object)][$targetClass])) {
185
            return $objectTransformers[get_class($object)][$targetClass];
186
        }
187
188
        foreach ($this->getModelTransformers() as $modelTransformer) {
189
            if (($modelTransformer instanceof ContextualModelTransformerInterface) && $modelTransformer->supports($object, $targetClass, $context)) {
190
                return $modelTransformer;
191
            }
192
193
            if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
194
                return $modelTransformer;
195
            }
196
197
            if ($modelTransformer instanceof ObjectTransformerInterface) {
198
                continue;
199
            }
200
        }
201
202
        return null;
203
    }
204
}
205