Passed
Push — master ( 2e2189...7af543 )
by Dmitry
03:06
created

ModelTransformer::addModelTransformer()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 2
Metric Value
c 5
b 0
f 2
dl 0
loc 36
rs 5.3846
cc 8
eloc 19
nc 7
nop 2
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 (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 ($modelTransformer instanceof ObjectTransformerInterface) {
163
            return $modelTransformer->transform($object);
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 150 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...
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
        $objectTransformers = $this->getObjectTransformers();
194
        if (isset($objectTransformers[get_class($object)]) && isset($objectTransformers[get_class($object)][$targetClass])) {
195
            return $objectTransformers[get_class($object)][$targetClass];
196
        }
197
198
        foreach ($this->getModelTransformers() as $modelTransformer) {
199
            if (($modelTransformer instanceof ContextualModelTransformerInterface) && $modelTransformer->supports($object, $targetClass, $context)) {
200
                return $modelTransformer;
201
            }
202
203
            if (($modelTransformer instanceof ModelTransformerInterface) && $modelTransformer->supports($object, $targetClass)) {
204
                return $modelTransformer;
205
            }
206
207
            if ($modelTransformer instanceof ObjectTransformerInterface) {
208
                continue;
209
            }
210
        }
211
212
        return null;
213
    }
214
}
215