Completed
Push — master ( fad66a...6b73b6 )
by Edgar
03:36
created

Pattern   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 15
c 6
b 0
f 0
lcom 1
cbo 6
dl 0
loc 118
ccs 61
cts 61
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
A getName() 0 4 1
A getTransformAttribute() 0 4 1
A withShape() 0 14 1
A verticalHatch() 0 4 1
A horizontalHatch() 0 4 1
A diagonalHatch() 0 4 1
B crossHatch() 0 27 3
A straightCrossHatch() 0 4 1
A hatch() 0 12 1
A getDefaultConfig() 0 4 1
A setTransformAttribute() 0 4 1
1
<?php
2
namespace nstdio\svg\container;
3
4
use nstdio\svg\attributes\Transformable;
5
use nstdio\svg\ElementInterface;
6
use nstdio\svg\shape\Line;
7
use nstdio\svg\shape\Shape;
8
use nstdio\svg\traits\TransformTrait;
9
use nstdio\svg\util\Transform;
10
use nstdio\svg\util\TransformInterface;
11
12
/**
13
 * Class Pattern
14
 *
15
 * A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled")
16
 * at fixed intervals in x and y to cover the areas to be painted. Patterns are defined using a 'pattern' element and
17
 * then referenced by properties 'fill' and 'stroke' on a given graphics element to indicate that the given element
18
 * shall be filled or stroked with the referenced pattern.
19
 *
20
 * @link    https://www.w3.org/TR/SVG/pservers.html#Patterns
21
 * @property string patternUnits        "userSpaceOnUse | objectBoundingBox" Defines the coordinate system for
22
 *           attributes 'x',
23
 *           'y', 'width' and 'height'. If patternUnits="userSpaceOnUse", 'x', 'y', 'width' and 'height' represent
24
 *           values in the coordinate system that results from taking the current user coordinate system in place at
25
 *           the time when the 'pattern' element is referenced (i.e., the user coordinate system for the element
26
 *           referencing the 'pattern' element via a 'fill' or 'stroke' property) and then applying the transform
27
 *           specified by attribute 'patternTransform'. If patternUnits="objectBoundingBox", the user coordinate system
28
 *           for attributes 'x', 'y', 'width' and 'height' is established using the bounding box of the element to
29
 *           which the pattern is applied (see Object bounding box units) and then applying the transform specified by
30
 *           attribute 'patternTransform'. If attribute 'patternUnits' is not specified, then the effect is as if a
31
 *           value of 'objectBoundingBox' were specified.
32
 * @property string patternContentUnits = "userSpaceOnUse | objectBoundingBox" Defines the coordinate system for the
33
 *           contents of the ‘pattern’. Note that this attribute has no effect if attribute ‘viewBox’ is specified. If
34
 *           patternContentUnits="userSpaceOnUse", the user coordinate system for the contents of the ‘pattern’ element
35
 *           is the coordinate system that results from taking the current user coordinate system in place at the time
36
 *           when the ‘pattern’ element is referenced (i.e., the user coordinate system for the element referencing the
37
 *           ‘pattern’ element via a ‘fill’ or ‘stroke’ property) and then applying the transform specified by
38
 *           attribute ‘patternTransform’. If patternContentUnits="objectBoundingBox", the user coordinate system for
39
 *           the contents of the ‘pattern’ element is established using the bounding box of the element to which the
40
 *           pattern is applied (see Object bounding box units) and then applying the transform specified by attribute
41
 *           ‘patternTransform’. If attribute ‘patternContentUnits’ is not specified, then the effect is as if a value
42
 *           of 'userSpaceOnUse' were specified.
43
 * @property string patternTransform    = "<transform-list>" Contains the definition of an optional additional
44
 *           transformation from the pattern coordinate system onto the target coordinate system (i.e.,
45
 *           'userSpaceOnUse' or 'objectBoundingBox'). This allows for things such as skewing the pattern tiles. This
46
 *           additional transformation matrix is post-multiplied to (i.e., inserted to the right of) any previously
47
 *           defined transformations, including the implicit transformation necessary to convert from object bounding
48
 *           box units to user space. If attribute ‘patternTransform’ is not specified, then the effect is as if an
49
 *           identity transform were specified.
50
 * @property float  x                   = "<coordinate>" ‘x’, ‘y’, ‘width’ and ‘height’ indicate how the pattern tiles
51
 *           are placed and spaced. These attributes represent coordinates and values in the coordinate space specified
52
 *           by the combination of attributes ‘patternUnits’ and ‘patternTransform’. If the attribute is not specified,
53
 *           the effect is as if a value of zero were specified.
54
 * @property float  y                   = "<coordinate>" See ‘x’. If the attribute is not specified, the effect is as
55
 *           if a value of zero were specified.
56
 * @property float  width               = "<length>" See ‘x’. A negative value is an error (see Error processing). A
57
 *           value of zero disables rendering of the element (i.e., no paint is applied). If the attribute is not
58
 *           specified, the effect is as if a value of zero were specified.
59
 * @property float  height              = "<length>" See ‘x’. A negative value is an error (see Error processing). A
60
 *           value of zero disables rendering of the element (i.e., no paint is applied). If the attribute is not
61
 *           specified, the effect is as if a value of zero were specified.
62
 * @property float  xlink               :href = "<iri>" An IRI reference to a different ‘pattern’ element within the
63
 *           current SVG document fragment. Any attributes which are defined on the referenced element which are not
64
 *           defined on this element are inherited by this element. If this element has no children, and the referenced
65
 *           element does (possibly due to its own ‘xlink:href’ attribute), then this element inherits the children
66
 *           from the referenced element. Inheritance can be indirect to an arbitrary level; thus, if the referenced
67
 *           element inherits attributes or children due to its own ‘xlink:href’ attribute, then the current element
68
 *           can inherit those attributes or children.
69
 * @package nstdio\svg\container
70
 * @author  Edgar Asatryan <[email protected]>
71
 */
72
class Pattern extends Container implements TransformInterface, Transformable
73
{
74
    use TransformTrait;
75
76 9
    public function __construct(ElementInterface $parent, $id = null)
77
    {
78 9
        if ($parent instanceof SVG) {
79 9
            $defs = $parent->getFirstChild();
80 9
            $parent = $defs;
81 9
        }
82 9
        parent::__construct($parent);
83
84 9
        $this->transformImpl = Transform::newInstance($this->getTransformAttribute());
85 9
        $this->id = $id;
86 9
    }
87
88 9
    public function getName()
89
    {
90 9
        return 'pattern';
91
    }
92
93 1
    public static function withShape(ContainerInterface $container, Shape $shape, array $patternConfig = [], $id = null)
94
    {
95 1
        $patternConfig = array_merge(self::getDefaultConfig(), $patternConfig);
96
97 1
        $shapeBox = $shape->getBoundingBox();
98 1
        $patternConfig['width'] = $shapeBox['width'];
99 1
        $patternConfig['height'] = $shapeBox['height'];
100
101 1
        $pattern = (new self($container, $id))->apply($patternConfig);
102 1
        $shape->getRoot()->removeChild($shape);
0 ignored issues
show
Bug introduced by
The method removeChild() does not seem to exist on object<nstdio\svg\XMLDocumentInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
103 1
        $pattern->append($shape);
104
105 1
        return $pattern;
106
    }
107
108 1
    public static function verticalHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
109
    {
110 1
        return self::hatch($container, $patternConfig, $lineConfig, $id);
111
    }
112
113 1
    public static function horizontalHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
114
    {
115 1
        return self::hatch($container, $patternConfig, $lineConfig, $id)->rotate(90);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\ElementInterface as the method rotate() does only exist in the following implementations of said interface: nstdio\svg\container\G, nstdio\svg\container\Group, nstdio\svg\container\Pattern, nstdio\svg\shape\Circle, nstdio\svg\shape\Ellipse, nstdio\svg\shape\Line, nstdio\svg\shape\Path, nstdio\svg\shape\Polygon, nstdio\svg\shape\Polyline, nstdio\svg\shape\Rect, nstdio\svg\shape\RoundedShape, nstdio\svg\shape\Shape.

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...
116
    }
117
118 3
    public static function diagonalHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
119
    {
120 3
        return self::hatch($container, $patternConfig, $lineConfig, $id)->rotate(45);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface nstdio\svg\ElementInterface as the method rotate() does only exist in the following implementations of said interface: nstdio\svg\container\G, nstdio\svg\container\Group, nstdio\svg\container\Pattern, nstdio\svg\shape\Circle, nstdio\svg\shape\Ellipse, nstdio\svg\shape\Line, nstdio\svg\shape\Path, nstdio\svg\shape\Polygon, nstdio\svg\shape\Polyline, nstdio\svg\shape\Rect, nstdio\svg\shape\RoundedShape, nstdio\svg\shape\Shape.

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...
121
    }
122
123 2
    public static function crossHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
124
    {
125 2
        if (isset($patternConfig['width'])) {
126 2
            $patternConfig['height'] = $patternConfig['width'];
127 2
        }
128 2
        if (isset($patternConfig['height'])) {
129 2
            $patternConfig['width'] = $patternConfig['height'];
130 2
        }
131
132
        /** @var Pattern $pattern */
133 2
        $pattern = self::diagonalHatch($container, $patternConfig, $lineConfig, $id);
134
135
        /** @var Line $firstLine */
136 2
        $firstLine = $pattern->getFirstChild()->apply([
137 2
            'x1' => 0,
138 2
            'y1' => $pattern->height / 2,
139 2
            'x2' => $pattern->width,
140 2
            'y2' => $pattern->height / 2,
141 2
        ]);
142
143 2
        $attrs = $firstLine->allAttributes(['x1', 'y1', 'x2', 'y2', 'id']);
144 2
        $line = new Line($pattern, $pattern->width / 2, 0, $pattern->width / 2, $pattern->height);
145 2
        $line->id = null;
146 2
        $line->apply($attrs);
147
148 2
        return $pattern;
149
    }
150
151 1
    public static function straightCrossHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
152
    {
153 1
        return self::crossHatch($container, $patternConfig, $lineConfig, $id)->rotate(90);
154
    }
155
156 5
    protected static function hatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
157
    {
158 5
        $patternConfig = array_merge(self::getDefaultConfig(), $patternConfig);
159 5
        $lineDefaultConfig = ['stroke' => 'black', 'stroke-width' => 1, 'fill' => 'none'];
160 5
        $lineConfig = array_merge($lineDefaultConfig, $lineConfig);
161
162 5
        $pattern = (new self($container, $id))->apply($patternConfig);
163
164 5
        (new Line($pattern, 0, 0, 0, $pattern->height))->apply($lineConfig);
0 ignored issues
show
Bug introduced by
Accessing height on the interface nstdio\svg\ElementInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
165
166 5
        return $pattern;
167
    }
168
169 6
    protected static function getDefaultConfig()
170
    {
171 6
        return ['x' => 0, 'y' => 0, 'height' => 4, 'width' => 4, 'patternUnits' => 'userSpaceOnUse'];
172
    }
173
174
    /**
175
     * @inheritdoc
176
     */
177 9
    public function getTransformAttribute()
178
    {
179 9
        return $this->patternTransform;
180
    }
181
182
    /**
183
     * @inheritdoc
184
     */
185 5
    public function setTransformAttribute($transformList)
186
    {
187 5
        $this->patternTransform = $transformList;
188
    }
189
}