Test Failed
Push — master ( b95750...93ce75 )
by Edgar
03:22
created

Path::quadraticCurve()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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