Path::arcTo()   A
last analyzed

Complexity

Conditions 4
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 1
nop 8
crap 4

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
namespace nstdio\svg\shape;
3
4
use nstdio\svg\container\ContainerInterface;
5
use nstdio\svg\ElementInterface;
6
use nstdio\svg\traits\ElementTrait;
7
8
/**
9
 * Class Path
10
 *
11
 * @property string $d This attribute defines a path to follow. {@link
12
 *           https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d}
13
 * @package shape
14
 * @author  Edgar Asatryan <[email protected]>
15
 */
16
class Path extends Shape implements ContainerInterface
17
{
18
    use ElementTrait;
19
20
    /**
21
     * @var  PathBounds
22
     */
23
    private $boundingBox;
24
25
    /**
26
     * Path constructor.
27
     *
28
     * @param ElementInterface $parent
29
     * @param float            $x
30
     * @param float            $y
31
     * @param bool             $absolute
32
     */
33 11
    public function __construct(ElementInterface $parent, $x, $y, $absolute = true)
34
    {
35 11
        parent::__construct($parent);
36
37 11
        $this->boundingBox = new PathBounds();
38 11
        $this->moveTo($x, $y, $absolute);
39 11
    }
40
41
    /**
42
     * Start a new sub-path at the given (x,y) coordinate. M (uppercase) indicates that absolute coordinates will
43
     * follow; m (lowercase) indicates that relative coordinates will follow. If a moveto is followed by multiple pairs
44
     * of coordinates, the subsequent pairs are treated as implicit lineto commands. Hence, implicit lineto commands
45
     * will be relative if the moveto is relative, and absolute if the moveto is absolute. If a relative moveto (m)
46
     * appears as the first element of the path, then it is treated as a pair of absolute coordinates. In this case,
47
     * subsequent pairs of coordinates are treated as relative even though the initial moveto is interpreted as an
48
     * absolute moveto.
49
     *
50
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
51
     *
52
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
53
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
54
     * @param bool  $absolute
55
     *
56
     * @return $this
57
     */
58 11
    public function moveTo($x, $y, $absolute = true)
59
    {
60 11
        $this->buildPath($absolute ? 'M' : 'm', $x, $y);
61
62 11
        return $this;
63
    }
64
65
    /**
66
     * @param ElementInterface $parent
67
     * @param                  $x0
68
     * @param                  $y0
69
     * @param                  $x1
70
     * @param                  $y1
71
     * @param                  $x
72
     * @param                  $y
73
     * @param bool             $absolute
74
     *
75
     * @return Path|ContainerInterface
76
     */
77
    public static function quadraticCurve(ElementInterface $parent, $x0, $y0, $x1, $y1, $x, $y, $absolute = true)
78
    {
79
        return self::create($parent, $x0, $y0, $absolute)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method quadraticCurveTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
80
            ->quadraticCurveTo($x1, $y1, $x, $y);
81
    }
82
83
    /**
84
     * Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1) as the control point. Q (uppercase)
85
     * indicates that absolute coordinates will follow; q (lowercase) indicates that relative coordinates will follow.
86
     * Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current
87
     * point becomes the final (x,y) coordinate pair used in the polybézier.
88
     *
89
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
90
     *
91
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
92
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
93
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
94
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
95
     *
96
     * @param bool  $absolute
97
     *
98
     * @return $this
99
     */
100 1
    public function quadraticCurveTo($x1, $y1, $x, $y, $absolute = true)
101
    {
102 1
        $this->buildPath($absolute ? 'Q' : 'q', $x1, $y1, $x, $y);
103
104 1
        return $this;
105
    }
106
107
    /**
108
     * @param string $type
109
     */
110 11
    private function buildPath($type)
111
    {
112 11
        $params = array_slice(func_get_args(), 1);
113 11
        $this->boundingBox->addData($type, $params);
114
115 11
        $this->d .= $this->d == null ? $type : " $type";
116 11
        foreach ($params as $key => $value) {
117 11
            if (is_array($value)) {
118 4
                $this->addArrayToPath($value);
119 4
            } else {
120 11
                if ($key % 2 !== 0 && !is_array($params[$key - 1])) {
121 11
                    $this->d .= ", $value";
122 11
                } else {
123 11
                    $this->d .= " $value";
124
                }
125
            }
126 11
        }
127 11
    }
128
129
    /**
130
     * @param $value
131
     */
132 4
    private function addArrayToPath(array $value)
133
    {
134 4
        foreach ($value as $item) {
135 4
            $this->d .= " $item,";
136 4
        }
137 4
        $this->d = rtrim($this->d, ',');
138 4
    }
139
140
    /**
141
     * @param ElementInterface $parent
142
     * @param                  $x
143
     * @param                  $y
144
     * @param bool             $absolute
145
     *
146
     * @return Path|ContainerInterface
147
     */
148 1
    public static function create(ElementInterface $parent, $x, $y, $absolute = true)
149
    {
150 1
        return new Path($parent, $x, $y, $absolute);
151
    }
152
153
    /**
154
     * @param ElementInterface $parent
155
     * @param                  $x
156
     * @param                  $y
157
     * @param                  $x1
158
     * @param                  $y1
159
     * @param bool             $absolute
160
     *
161
     * @return ContainerInterface|Path
162
     */
163 1
    public static function line(ElementInterface $parent, $x, $y, $x1, $y1, $absolute = true)
164
    {
165 1
        return self::create($parent, $x, $y, $absolute)->lineTo($x1, $y1, $absolute);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method lineTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
166
    }
167
168
    /**
169
     * Draw a line from the current point to the given (x,y) coordinate which becomes the new current point. L
170
     * (uppercase) indicates that absolute coordinates will follow; l (lowercase) indicates that relative coordinates
171
     * will follow. A number of coordinates pairs may be specified to draw a polyline. At the end of the command, the
172
     * new current point is set to the final set of coordinates provided.
173
     *
174
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
175
     *
176
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
177
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
178
     * @param bool  $absolute
179
     *
180
     * @return $this
181
     */
182 5
    public function lineTo($x, $y, $absolute = true)
183
    {
184 5
        $this->buildPath($absolute ? 'L' : 'l', $x, $y);
185
186 5
        return $this;
187
    }
188
189
    /**
190
     * @param ElementInterface $parent
191
     * @param                  $x
192
     * @param                  $y
193
     * @param                  $x1
194
     * @param bool             $absolute
195
     *
196
     * @return ContainerInterface|Path
197
     */
198
    public static function hLine(ElementInterface $parent, $x, $y, $x1, $absolute = true)
199
    {
200
        return self::create($parent, $x, $y, $absolute)->hLineTo($x1, $absolute);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method hLineTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
201
    }
202
203
    /**
204
     * Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H (uppercase) indicates that absolute
205
     * coordinates will follow; h (lowercase) indicates that relative coordinates will follow. Multiple x values can be
206
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
207
     * (x, cpy) for the final value of x.
208
     *
209
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
210
     *
211
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
212
     *
213
     * @param bool  $absolute
214
     *
215
     * @return $this
216
     */
217 4
    public function hLineTo($x, $absolute = true)
218
    {
219 4
        $this->buildPath($absolute ? 'H' : 'h', $x);
220
221 4
        return $this;
222
    }
223
224
    /**
225
     * @param ElementInterface $parent
226
     * @param                  $x
227
     * @param                  $y
228
     * @param                  $y1
229
     * @param bool             $absolute
230
     *
231
     * @return ContainerInterface|Path
232
     */
233
    public static function vLine(ElementInterface $parent, $x, $y, $y1, $absolute = true)
234
    {
235
        return self::create($parent, $x, $y, $absolute)->vLineTo($y1, $absolute);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\container\ContainerInterface as the method vLineTo() does only exist in the following implementations of said interface: nstdio\svg\shape\Path.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
236
    }
237
238
    /**
239
     * Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V (uppercase) indicates that absolute
240
     * coordinates will follow; v (lowercase) indicates that relative coordinates will follow. Multiple y values can be
241
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
242
     * (cpx, y) for the final value of y.
243
     *
244
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
245
     *
246
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
247
     *
248
     * @param bool  $absolute
249
     *
250
     * @return $this
251
     */
252 2
    public function vLineTo($y, $absolute = true)
253
    {
254 2
        $this->buildPath($absolute ? 'V' : 'v', $y);
255
256 2
        return $this;
257
    }
258
259
    /**
260
     * Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning
261
     * of the curve and (x2,y2) as the control point at the end of the curve.
262
     *
263
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
264
     *
265
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
266
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
267
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
268
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
269
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
270
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
271
     *
272
     * @param bool  $absolute
273
     *
274
     * @return $this
275
     */
276 1
    public function curveTo($x1, $y1, $x2, $y2, $x, $y, $absolute = true)
277
    {
278 1
        $this->buildPath($absolute ? 'C' : 'c', $x1, $y1, $x2, $y2, $x, $y);
279
280 1
        return $this;
281
    }
282
283
    /**
284
     * Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the
285
     * reflection of the second control point on the previous command relative to the current point. (If there is no
286
     * previous command or if the previous command was not an C, c, S or s, assume the first control point is
287
     * coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of
288
     * the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative
289
     * coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the
290
     * command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
291
     *
292
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
293
     *
294
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
295
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
296
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
297
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
298
     *
299
     * @param bool  $absolute
300
     *
301
     * @return $this
302
     */
303 1
    public function smoothCurveTo($x2, $y2, $x, $y, $absolute = true)
304
    {
305 1
        $this->buildPath($absolute ? 'S' : 's', $x2, $y2, $x, $y);
306
307 1
        return $this;
308
    }
309
310
    /**
311
     * Draws a quadratic Bézier curve from the current point to (x,y). The control point is assumed to be the
312
     * reflection of the control point on the previous command relative to the current point. (If there is no previous
313
     * command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the
314
     * current point.) T (uppercase) indicates that absolute coordinates will follow; t (lowercase) indicates that
315
     * relative coordinates will follow. At the end of the command, the new current point becomes the final (x,y)
316
     * coordinate pair used in the polybézier.
317
     *
318
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
319
     *
320
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
321
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
322
     *
323
     * @param bool  $absolute
324
     *
325
     * @return $this
326
     */
327 1
    public function smoothQuadraticCurveTo($x, $y, $absolute = true)
328
    {
329 1
        $this->buildPath($absolute ? 'T' : 't', $x, $y);
330
331 1
        return $this;
332
    }
333
334
    /**
335
     * Draws an elliptical arc from the current point to (x, y). The size and orientation of the ellipse are defined by
336
     * two radii (rx, ry) and an x-axis-rotation, which indicates how the ellipse as a whole is rotated relative to the
337
     * current coordinate system. The center (cx, cy) of the ellipse is calculated automatically to satisfy the
338
     * constraints imposed by the other parameters. large-arc-flag and sweep-flag contribute to the automatic
339
     * calculations and help determine how the arc is drawn.
340
     *
341
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands
342
     *
343
     * @param float   $rx           The x-axis radius for the ellipse (i.e., r1).
344
     * @param float   $ry           The y-axis radius for the ellipse
345
     * @param float   $xRotation    The rotation angle in degrees for the ellipse's x-axis relative to the x-axis of
346
     *                              the user coordinate system.
347
     * @param boolean $largeArcFlag The value of the large-arc-flag parameter.
348
     * @param boolean $sweepFlag    The value of the sweep-flag parameter.
349
     * @param float   $x            The absolute (relative) X coordinate for the end point of this path segment.
350
     * @param float   $y            The absolute (relative) Y coordinate for the end point of this path segment.
351
     * @param bool    $absolute
352
     *
353
     * @return $this
354
     */
355 4
    public function arcTo($rx, $ry, $xRotation, $largeArcFlag, $sweepFlag, $x, $y, $absolute = true)
356
    {
357 4
        $this->buildPath($absolute ? 'A' : 'a', [$rx, $ry], $xRotation, [$largeArcFlag ? 1 : 0, $sweepFlag ? 1 : 0], [$x, $y]);
358
359 4
        return $this;
360
    }
361
362
    /**
363
     * Close the current subpath by drawing a straight line from the current point to current subpath's initial point.
364
     * Since the Z and z commands take no parameters, they have an identical effect.
365
     *
366
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
367
     *
368
     * @param bool $absolute
369
     */
370 3
    public function closePath($absolute = true)
371
    {
372 3
        $this->buildPath($absolute ? 'Z' : 'z');
373 3
    }
374
375 11
    public function getName()
376
    {
377 11
        return 'path';
378
    }
379
380 1
    public function getBoundingBox()
381
    {
382 1
        return $this->boundingBox->getBox();
383
    }
384
385 1
    protected function getCenterX()
386
    {
387 1
        return $this->boundingBox->getBox()['width'] / 2;
388
    }
389
390 1
    protected function getCenterY()
391
    {
392 1
        return $this->boundingBox->getBox()['height'] / 2;
393
    }
394
}