Test Failed
Push — master ( 40f013...45c23a )
by Edgar
10:04
created

Path::quadraticCurve()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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 10
    public function __construct(ElementInterface $parent, $x, $y, $absolute = true)
34
    {
35 10
        parent::__construct($parent);
36
37 10
        $this->boundingBox = new PathBounds();
38 10
        $this->moveTo($x, $y, $absolute);
39 10
    }
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 10
    public function moveTo($x, $y, $absolute = true)
59
    {
60 10
        $modifier = $absolute ? 'M' : 'm';
61 10
        $this->d = "$modifier $x, $y";
62
63 10
        $this->boundingBox->addData($modifier, [$x, $y]);
64
65 10
        return $this;
66
    }
67
68
    /**
69
     * @param ElementInterface $parent
70
     * @param                  $x0
71
     * @param                  $y0
72
     * @param                  $x1
73
     * @param                  $y1
74
     * @param                  $x
75
     * @param                  $y
76
     * @param bool             $absolute
77
     *
78
     * @return Path|ContainerInterface
79
     */
80
    public static function quadraticCurve(ElementInterface $parent, $x0, $y0, $x1, $y1, $x, $y, $absolute = true)
81
    {
82
        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...
83
            ->quadraticCurveTo($x1, $y1, $x, $y);
84
    }
85
86
    /**
87
     * Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1) as the control point. Q (uppercase)
88
     * indicates that absolute coordinates will follow; q (lowercase) indicates that relative coordinates will follow.
89
     * Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current
90
     * point becomes the final (x,y) coordinate pair used in the polybézier.
91
     *
92
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
93
     *
94
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
95
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
96
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
97
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
98
     *
99
     * @param bool  $absolute
100
     *
101
     * @return $this
102
     */
103 1
    public function quadraticCurveTo($x1, $y1, $x, $y, $absolute = true)
104
    {
105 1
        $this->buildPath($absolute ? 'Q' : 'q', $x1, $y1, $x, $y);
106
107 1
        return $this;
108
    }
109
110
    /**
111
     * @param string $type
112
     */
113 7
    private function buildPath($type)
114
    {
115 7
        $params = array_slice(func_get_args(), 1);
116 7
        $this->boundingBox->addData($type, $params);
117
118 7
        $this->d .= " $type";
119 7
        foreach ($params as $key => $value) {
120 7
            if (is_array($value)) {
121 4
                $this->addArrayToPath($value);
122 4
            } else {
123 7
                if ($key % 2 !== 0 && !is_array($params[$key - 1])) {
124 5
                    $this->d .= ", $value";
125 5
                } else {
126 7
                    $this->d .= " $value";
127
                }
128
            }
129 7
        }
130 7
    }
131
132
    /**
133
     * @param $value
134
     */
135 4
    private function addArrayToPath(array $value)
136
    {
137 4
        foreach ($value as $item) {
138 4
            $this->d .= " $item,";
139 4
        }
140 4
        $this->d = rtrim($this->d, ',');
141 4
    }
142
143
    /**
144
     * @param ElementInterface $parent
145
     * @param                  $x
146
     * @param                  $y
147
     * @param bool             $absolute
148
     *
149
     * @return Path|ContainerInterface
150
     */
151 1
    public static function create(ElementInterface $parent, $x, $y, $absolute = true)
152
    {
153 1
        return new Path($parent, $x, $y, $absolute);
154
    }
155
156
    /**
157
     * @param ElementInterface $parent
158
     * @param                  $x
159
     * @param                  $y
160
     * @param                  $x1
161
     * @param                  $y1
162
     * @param bool             $absolute
163
     *
164
     * @return ContainerInterface|Path
165
     */
166 1
    public static function line(ElementInterface $parent, $x, $y, $x1, $y1, $absolute = true)
167
    {
168 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...
169
    }
170
171
    /**
172
     * Draw a line from the current point to the given (x,y) coordinate which becomes the new current point. L
173
     * (uppercase) indicates that absolute coordinates will follow; l (lowercase) indicates that relative coordinates
174
     * will follow. A number of coordinates pairs may be specified to draw a polyline. At the end of the command, the
175
     * new current point is set to the final set of coordinates provided.
176
     *
177
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
178
     *
179
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
180
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
181
     * @param bool  $absolute
182
     *
183
     * @return $this
184
     */
185 5
    public function lineTo($x, $y, $absolute = true)
186
    {
187 5
        $this->buildPath($absolute ? 'L' : 'l', $x, $y);
188
189 5
        return $this;
190
    }
191
192
    /**
193
     * @param ElementInterface $parent
194
     * @param                  $x
195
     * @param                  $y
196
     * @param                  $x1
197
     * @param bool             $absolute
198
     *
199
     * @return ContainerInterface|Path
200
     */
201
    public static function hLine(ElementInterface $parent, $x, $y, $x1, $absolute = true)
202
    {
203
        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...
204
    }
205
206
    /**
207
     * Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H (uppercase) indicates that absolute
208
     * coordinates will follow; h (lowercase) indicates that relative coordinates will follow. Multiple x values can be
209
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
210
     * (x, cpy) for the final value of x.
211
     *
212
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
213
     *
214
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
215
     *
216
     * @param bool  $absolute
217
     *
218
     * @return $this
219
     */
220 4
    public function hLineTo($x, $absolute = true)
221
    {
222 4
        $this->buildPath($absolute ? 'H' : 'h', $x);
223
224 4
        return $this;
225
    }
226
227
    /**
228
     * @param ElementInterface $parent
229
     * @param                  $x
230
     * @param                  $y
231
     * @param                  $y1
232
     * @param bool             $absolute
233
     *
234
     * @return ContainerInterface|Path
235
     */
236
    public static function vLine(ElementInterface $parent, $x, $y, $y1, $absolute = true)
237
    {
238
        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...
239
    }
240
241
    /**
242
     * Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V (uppercase) indicates that absolute
243
     * coordinates will follow; v (lowercase) indicates that relative coordinates will follow. Multiple y values can be
244
     * provided (although usually this doesn't make sense). At the end of the command, the new current point becomes
245
     * (cpx, y) for the final value of y.
246
     *
247
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
248
     *
249
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
250
     *
251
     * @param bool  $absolute
252
     *
253
     * @return $this
254
     */
255 2
    public function vLineTo($y, $absolute = true)
256
    {
257 2
        $this->buildPath($absolute ? 'V' : 'v', $y);
258
259 2
        return $this;
260
    }
261
262
    /**
263
     * Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning
264
     * of the curve and (x2,y2) as the control point at the end of the curve.
265
     *
266
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
267
     *
268
     * @param float $x1 The absolute (relative) X coordinate for the first control point.
269
     * @param float $y1 The absolute (relative) Y coordinate for the first control point.
270
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
271
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
272
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
273
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
274
     *
275
     * @param bool  $absolute
276
     *
277
     * @return $this
278
     */
279 1
    public function curveTo($x1, $y1, $x2, $y2, $x, $y, $absolute = true)
280
    {
281 1
        $this->buildPath($absolute ? 'C' : 'c', $x1, $y1, $x2, $y2, $x, $y);
282
283 1
        return $this;
284
    }
285
286
    /**
287
     * Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the
288
     * reflection of the second control point on the previous command relative to the current point. (If there is no
289
     * previous command or if the previous command was not an C, c, S or s, assume the first control point is
290
     * coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of
291
     * the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative
292
     * coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the
293
     * command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
294
     *
295
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataCurveCommands
296
     *
297
     * @param float $x2 The absolute (relative) X coordinate for the second control point.
298
     * @param float $y2 The absolute (relative) Y coordinate for the second control point.
299
     * @param float $x  The absolute (relative) X coordinate for the end point of this path segment.
300
     * @param float $y  The absolute (relative) Y coordinate for the end point of this path segment.
301
     *
302
     * @param bool  $absolute
303
     *
304
     * @return $this
305
     */
306 1
    public function smoothCurveTo($x2, $y2, $x, $y, $absolute = true)
307
    {
308 1
        $this->buildPath($absolute ? 'S' : 's', $x2, $y2, $x, $y);
309
310 1
        return $this;
311
    }
312
313
    /**
314
     * Draws a quadratic Bézier curve from the current point to (x,y). The control point is assumed to be the
315
     * reflection of the control point on the previous command relative to the current point. (If there is no previous
316
     * command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the
317
     * current point.) T (uppercase) indicates that absolute coordinates will follow; t (lowercase) indicates that
318
     * relative coordinates will follow. At the end of the command, the new current point becomes the final (x,y)
319
     * coordinate pair used in the polybézier.
320
     *
321
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
322
     *
323
     * @param float $x The absolute (relative) X coordinate for the end point of this path segment.
324
     * @param float $y The absolute (relative) Y coordinate for the end point of this path segment.
325
     *
326
     * @param bool  $absolute
327
     *
328
     * @return $this
329
     */
330 1
    public function smoothQuadraticCurveTo($x, $y, $absolute = true)
331
    {
332 1
        $this->buildPath($absolute ? 'T' : 't', $x, $y);
333
334 1
        return $this;
335
    }
336
337
    /**
338
     * Draws an elliptical arc from the current point to (x, y). The size and orientation of the ellipse are defined by
339
     * two radii (rx, ry) and an x-axis-rotation, which indicates how the ellipse as a whole is rotated relative to the
340
     * current coordinate system. The center (cx, cy) of the ellipse is calculated automatically to satisfy the
341
     * constraints imposed by the other parameters. large-arc-flag and sweep-flag contribute to the automatic
342
     * calculations and help determine how the arc is drawn.
343
     *
344
     * @link https://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands
345
     *
346
     * @param float   $rx           The x-axis radius for the ellipse (i.e., r1).
347
     * @param float   $ry           The y-axis radius for the ellipse
348
     * @param float   $xRotation    The rotation angle in degrees for the ellipse's x-axis relative to the x-axis of
349
     *                              the user coordinate system.
350
     * @param boolean $largeArcFlag The value of the large-arc-flag parameter.
351
     * @param boolean $sweepFlag    The value of the sweep-flag parameter.
352
     * @param float   $x            The absolute (relative) X coordinate for the end point of this path segment.
353
     * @param float   $y            The absolute (relative) Y coordinate for the end point of this path segment.
354
     * @param bool    $absolute
355
     *
356
     * @return $this
357
     */
358 4
    public function arcTo($rx, $ry, $xRotation, $largeArcFlag, $sweepFlag, $x, $y, $absolute = true)
359
    {
360 4
        $this->buildPath($absolute ? 'A' : 'a', [$rx, $ry], $xRotation, [$largeArcFlag ? 1 : 0, $sweepFlag ? 1 : 0], [$x, $y]);
361
362 4
        return $this;
363
    }
364
365
    /**
366
     * Close the current subpath by drawing a straight line from the current point to current subpath's initial point.
367
     * Since the Z and z commands take no parameters, they have an identical effect.
368
     *
369
     * @link https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
370
     *
371
     * @param bool $absolute
372
     */
373 3
    public function closePath($absolute = true)
374
    {
375 3
        $this->buildPath($absolute ? 'Z' : 'z');
376 3
    }
377
378 10
    public function getName()
379
    {
380 10
        return 'path';
381
    }
382
383 1
    public function getBoundingBox()
384
    {
385 1
        return $this->boundingBox->getBox();
386
    }
387
388 1
    protected function getCenterX()
389
    {
390 1
        return $this->boundingBox->getBox()['width'] / 2;
391
    }
392
393 1
    protected function getCenterY()
394
    {
395 1
        return $this->boundingBox->getBox()['height'] / 2;
396
    }
397
}