Completed
Push — master ( d5cccf...59cfd0 )
by Edgar
09:03
created

Pattern::horizontalHatch()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 4
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
    public function __construct(ElementInterface $parent, $id = null)
77
    {
78
        if ($parent instanceof SVG) {
79
            $defs = $parent->getFirstChild();
80
            $parent = $defs;
81
        }
82
        parent::__construct($parent);
83
84
        $this->transformImpl = Transform::newInstance($this->getTransformAttribute());
85
        $this->id = $id;
86
    }
87
88
    public function getName()
89
    {
90
        return 'pattern';
91
    }
92
93
    public static function withShape(ContainerInterface $container, Shape $shape, array $patternConfig = [], $id = null)
94
    {
95
        $patternConfig = array_merge(self::getDefaultConfig(), $patternConfig);
96
97
        $shapeBox = $shape->getBoundingBox();
98
        $patternConfig['width'] = $shapeBox['width'];
99
        $patternConfig['height'] = $shapeBox['height'];
100
101
        $pattern = (new self($container, $id))->apply($patternConfig);
102
        $shape->selfRemove();
0 ignored issues
show
Bug introduced by
The method selfRemove() cannot be called from this context as it is declared protected in class nstdio\svg\SVGElement.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
103
        $pattern->append($shape);
104
105
        return $pattern;
106
    }
107
108
    public static function verticalHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
109
    {
110
        return self::hatch($container, $patternConfig, $lineConfig, $id);
111
    }
112
113
    public static function horizontalHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
114
    {
115
        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
    public static function diagonalHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
119
    {
120
        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
    public static function crossHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
124
    {
125
        if (isset($patternConfig['width'])) {
126
            $patternConfig['height'] = $patternConfig['width'];
127
        }
128
        if (isset($patternConfig['height'])) {
129
            $patternConfig['width'] = $patternConfig['height'];
130
        }
131
132
        $pattern = self::hatch($container, $patternConfig, $lineConfig, $id);
133
        $pattern->rotate(45);
134
135
        /** @var Line $firstLine */
136
        $firstLine = $pattern->getFirstChild()->apply([
137
            'x1' => 0,
138
            'y1' => $pattern->height / 2,
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...
139
            'x2' => $pattern->width,
0 ignored issues
show
Bug introduced by
Accessing width 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...
140
            'y2' => $pattern->height / 2,
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...
141
        ]);
142
143
        $attrs = $firstLine->allAttributes(['x1', 'y1', 'x2', 'y2', 'id']);
144
        $line = new Line($pattern, $pattern->width / 2, 0, $pattern->width / 2, $pattern->height);
0 ignored issues
show
Bug introduced by
Accessing width 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...
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...
145
        $line->id = null;
146
        $line->apply($attrs);
147
148
        return $pattern;
149
    }
150
151
    public static function straightCrossHatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
152
    {
153
        return self::crossHatch($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...
154
    }
155
156
    protected static function hatch(ContainerInterface $container, array $patternConfig = [], array $lineConfig = [], $id = null)
157
    {
158
        $patternConfig = array_merge(self::getDefaultConfig(), $patternConfig);
159
        $lineDefaultConfig = ['stroke' => 'black', 'stroke-width' => 1, 'fill' => 'none'];
160
        $lineConfig = array_merge($lineDefaultConfig, $lineConfig);
161
162
        $pattern = (new self($container, $id))->apply($patternConfig);
163
164
        (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
        return $pattern;
167
    }
168
169
    protected static function getDefaultConfig()
170
    {
171
        return ['x' => 0, 'y' => 0, 'height' => 4, 'width' => 4, 'patternUnits' => 'userSpaceOnUse'];
172
    }
173
174
    /**
175
     * @inheritdoc
176
     */
177
    public function getTransformAttribute()
178
    {
179
        return $this->patternTransform;
180
    }
181
182
    /**
183
     * @inheritdoc
184
     */
185
    public function setTransformAttribute($transformList)
186
    {
187
        $this->patternTransform = $transformList;
188
    }
189
}