Completed
Push — master ( 9016b2...9b9106 )
by Edgar
03:29
created

Transform   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 45
c 3
b 0
f 0
lcom 1
cbo 0
dl 0
loc 264
rs 8.3673

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A newInstance() 0 13 2
A result() 0 4 1
A setArgumentDelimiter() 0 11 4
B rotate() 0 11 5
A translate() 0 4 1
A scale() 0 4 1
A skewX() 0 4 1
A skewY() 0 4 1
A matrix() 0 8 2
A matchRotate() 0 4 1
A matchSkewX() 0 4 1
A matchSkewY() 0 4 1
A matchSkew() 0 4 1
A matchScale() 0 4 1
A matchMatrix() 0 11 2
A matchTranslate() 0 4 1
A getData() 0 4 1
A hasTransform() 0 4 1
A getTransform() 0 8 2
A makeSequence() 0 6 1
A addTransformSequence() 0 4 1
A buildTransformString() 0 10 2
A setTransformData() 0 12 4
A addTransformIfNeeded() 0 6 2
A matchPattern() 0 10 3
A shortcutBuild() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like Transform 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 Transform, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace nstdio\svg\util;
3
use Doctrine\Instantiator\Exception\InvalidArgumentException;
4
5
/**
6
 * Class Transform
7
 *
8
 * @package nstdio\svg\util
9
 * @author  Edgar Asatryan <[email protected]>
10
 */
11
final class Transform implements TransformInterface
12
{
13
    private $trans;
14
15
    private $data;
16
17
    private $sequence;
18
19
    private $argDelimiter = TransformInterface::ARG_DELIM_SPACE;
20
21
    const ROTATE_PATTERN = "/rotate\s*\(\s*(?<a>[+-]?\d+(?:\.\d+)?)((?:\s{1,}\,?\s*|\,\s*)(?<x>[+-]?\d+(?:\.\d+)?)(?:\s{1,}\,?\s*|\,\s*)(?<y>[+-]?\d+(?:\.\d+)?))?\s*\)/";
22
23
    const TRANSLATE_PATTERN = "/translate\s*\(\s*(?<x>[+-]?\d+(?:\.\d+)?)((?:\s{1,}\,?\s*|\,\s*)(?<y>[+-]?\d+(?:\.\d+)?))?\)/";
24
25
    const SCALE_PATTERN = "/scale\s*\(\s*(?<x>[+-]?\d+(?:\.\d+)?)((?:\s{1,}\,?\s*|\,\s*)(?<y>[+-]?\d+(?:\.\d+)?))?\)/";
26
27
    const SKEW_PATTERN = "/skew([XY])\s*\(\s*(?<x>[+-]?\d+(?:\.\d+)?)?\)/";
28
29
    const MATRIX_PATTERN = "/matrix\s*\(\s*((([+-]?\d+(?:\.\d+)?)(?:\s+,?\s*|,\s*)){5}([+-]?\d+(?:\.\d+)?)\s*)\)/";
30
31
    private function __construct()
32
    {
33
    }
34
35
    /**
36
     * Use this method to instantiate Transform class.
37
     *
38
     * @param $transformString
39
     *
40
     * @uses matchRotate()
41
     * @uses matchSkewX()
42
     * @uses matchSkewY()
43
     * @uses matchTranslate()
44
     * @uses matchScale()
45
     * @uses matchMatrix()
46
     *
47
     * @return Transform
48
     */
49
    public static function newInstance($transformString = null)
50
    {
51
        $instance = new Transform();
52
        $instance->trans = $transformString;
53
        $instance->sequence = $instance->makeSequence();
54
55
        foreach ($instance->sequence as $value) {
56
            $method = 'match' . ucfirst($value);
57
            $instance->data[$value] = $instance->$method();
58
        }
59
60
        return $instance;
61
    }
62
63
    /**
64
     * @inheritdoc
65
     */
66
    public function result()
67
    {
68
        return $this->trans;
69
    }
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function setArgumentDelimiter($delim)
75
    {
76
        if ($delim !== TransformInterface::ARG_DELIM_COMMA &&
77
            $delim !== TransformInterface::ARG_DELIM_COMMA_SPACE &&
78
            $delim !== TransformInterface::ARG_DELIM_SPACE
79
        ) {
80
            throw new \InvalidArgumentException("Invalid delimiter. See TransformInterface::setArgumentDelimiter documentation for valid value.");
81
        }
82
83
        $this->argDelimiter = $delim;
84
    }
85
86
    /**
87
     * @inheritdoc
88
     */
89
    public function rotate($angle, $cx = null, $cy = null)
90
    {
91
        if ($cx !== null && $cy === null) {
92
            $cy = $cx;
93
        }
94
        if ($cy !== null && $cx === null) {
95
            $cx = $cy;
96
        }
97
98
        return $this->shortcutBuild('rotate', [$angle, $cx, $cy]);
99
    }
100
101
    /**
102
     * @inheritdoc
103
     */
104
    public function translate($x, $y = null)
105
    {
106
        return $this->shortcutBuild('translate', [$x, $y]);
107
    }
108
109
    /**
110
     * @inheritdoc
111
     */
112
    public function scale($x, $y = null)
113
    {
114
        return $this->shortcutBuild('scale', [$x, $y]);
115
    }
116
117
    /**
118
     * @inheritdoc
119
     */
120
    public function skewX($x)
121
    {
122
        return $this->shortcutBuild('skewX', [$x]);
123
    }
124
125
    /**
126
     * @inheritdoc
127
     */
128
    public function skewY($y)
129
    {
130
        return $this->shortcutBuild('skewY', [$y]);
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136
    public function matrix(array $matrix)
137
    {
138
        if (count($matrix) !== 6) {
139
            throw new InvalidArgumentException("Invalid matrix size. You must provide en array with 6 elements. " . count($matrix) . " elements given.");
140
        }
141
142
        return $this->shortcutBuild('matrix', $matrix);
143
    }
144
145
    /**
146
     * @return mixed
147
     */
148
    private function matchRotate()
149
    {
150
        return $this->matchPattern(self::ROTATE_PATTERN, ['a', 'x', 'y']);
151
    }
152
153
    private function matchSkewX()
154
    {
155
        return $this->matchSkew();
156
    }
157
158
    private function matchSkewY()
159
    {
160
        return $this->matchSkew();
161
    }
162
163
    private function matchSkew()
164
    {
165
        return $this->matchPattern(self::SKEW_PATTERN, ['x']);
166
    }
167
168
    private function matchScale()
169
    {
170
        return $this->matchPattern(self::SCALE_PATTERN, ['x', 'y']);
171
    }
172
173
    private function matchMatrix()
174
    {
175
        preg_match(self::MATRIX_PATTERN, $this->trans, $matches);
176
        if (isset($matches[1]) === false) {
177
            throw new \InvalidArgumentException("Cannot match matrix transformation.");
178
        }
179
180
        $matrix = explode(' ', preg_replace(['/\s+/', '/\,+/'], [' ', ''], $matches[1]), 6);
181
182
        return $matrix;
183
    }
184
185
    private function matchTranslate()
186
    {
187
        return $this->matchPattern(self::TRANSLATE_PATTERN, ['x', 'y']);
188
    }
189
190
    /**
191
     * @return array
192
     */
193
    public function getData()
194
    {
195
        return $this->data;
196
    }
197
198
    private function hasTransform($transform)
199
    {
200
        return in_array($transform, $this->sequence);
201
    }
202
203
    private function getTransform($transform)
204
    {
205
        if ($this->hasTransform($transform)) {
206
            return $this->data[$transform];
207
        }
208
209
        return null;
210
    }
211
212
    private function makeSequence()
213
    {
214
        preg_match_all("/\s*(matrix|translate|scale|rotate|skew[XY])/i", $this->trans, $matches);
215
216
        return $matches[1];
217
    }
218
219
    private function addTransformSequence($transform)
220
    {
221
        $this->sequence[] = $transform;
222
    }
223
224
    private function buildTransformString()
225
    {
226
        $ret = '';
227
        foreach ($this->sequence as $transform) {
228
            $ret .= $transform . "(" . rtrim(implode($this->argDelimiter, $this->data[$transform])) . ") ";
229
        }
230
        $this->trans = rtrim($ret);
231
232
        return $this->trans;
233
    }
234
235
    private function setTransformData($transform, $data)
236
    {
237
        if (isset($this->data[$transform]) === true) {
238
            $oldData = $this->data[$transform];
239
            foreach ($data as $key => $item) {
240
                if ($item === null) {
241
                    $data[$key] = $oldData[$key];
242
                }
243
            }
244
        }
245
        $this->data[$transform] = $data;
246
    }
247
248
    private function addTransformIfNeeded($transform)
249
    {
250
        if ($this->getTransform($transform) === null) {
251
            $this->addTransformSequence($transform);
252
        }
253
    }
254
255
    private function matchPattern($pattern, $named)
256
    {
257
        preg_match($pattern, $this->trans, $matches);
258
        $ret = [];
259
        foreach ($named as $value) {
260
            $ret[] = isset($matches[$value]) ? $matches[$value] : null;
261
        }
262
263
        return $ret;
264
    }
265
266
    private function shortcutBuild($transform, $data)
267
    {
268
        $this->addTransformIfNeeded($transform);
269
270
        $this->setTransformData($transform, $data);
271
272
        return $this->buildTransformString();
273
    }
274
}