Completed
Push — master ( b035c8...ddcf44 )
by Paweł
02:10
created

StringType::setValue()   F

Complexity

Conditions 16
Paths 432

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 16

Importance

Changes 0
Metric Value
cc 16
nc 432
nop 2
dl 0
loc 51
rs 2.1888
c 0
b 0
f 0
ccs 26
cts 26
cp 1
crap 16

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Wszetko\Sitemap\Items\DataTypes;
5
6
use InvalidArgumentException;
7
use Wszetko\Sitemap\Interfaces\DataType;
8
9
/**
10
 * Class StringType
11
 *
12
 * @package Wszetko\Sitemap\Items\DataTypes
13
 */
14
class StringType extends AbstractDataType
15
{
16
    /**
17
     * @var int
18
     */
19
    protected $minLength;
20
21
    /**
22
     * @var int
23
     */
24
    protected $maxLength;
25
26
    /**
27
     * @var array|null
28
     */
29
    protected $allowedValues = null;
30
31
    /**
32
     * @var string
33
     */
34
    protected $regex;
35
36
    /**
37
     * @var string
38
     */
39
    protected $regexGroup;
40
41
    /**
42
     * @return int|null
43
     */
44 57
    public function getMinLength(): ?int
45
    {
46 57
        return $this->minLength;
47
    }
48
49
    /**
50
     * @param int $minLength
51
     *
52
     * @return \Wszetko\Sitemap\Items\DataTypes\StringType
53
     */
54 24
    public function setMinLength(int $minLength): self
55
    {
56 24
        $this->minLength = $minLength;
57
58 24
        return $this;
59
    }
60
61
    /**
62
     * @return int|null
63
     */
64 57
    public function getMaxLength(): ?int
65
    {
66 57
        return $this->maxLength;
67
    }
68
69
    /**
70
     * @param int $maxLength
71
     *
72
     * @return self
73
     */
74 24
    public function setMaxLength(int $maxLength): self
75
    {
76 24
        $this->maxLength = $maxLength;
77
78 24
        return $this;
79
    }
80
81
    /**
82
     * @return array|null
83
     */
84 57
    public function getAllowedValues(): ?array
85
    {
86 57
        return $this->allowedValues;
87
    }
88
89
    /**
90
     * @param string|array|null $allowedValues
91
     *
92
     * @return \Wszetko\Sitemap\Items\DataTypes\StringType
93
     */
94 43
    public function setAllowedValues($allowedValues): self
95
    {
96 43
        if (is_string($allowedValues)) {
97 34
            $allowedValues = explode(',', $allowedValues);
98
99 34
            foreach ($allowedValues as $allowedValue) {
100 34
                $this->allowedValues[] = trim($allowedValue);
101
            }
102 9
        } elseif (is_array($allowedValues)) {
103 9
            $this->allowedValues = $allowedValues;
104
        }
105
106 43
        return $this;
107
    }
108
109
    /**
110
     * @param string $regex
111
     * @param string $regexGroup
112
     *
113
     * @return \Wszetko\Sitemap\Items\DataTypes\StringType
114
     */
115 40
    public function setValueRegex(string $regex, string $regexGroup): self
116
    {
117 40
        $this->regex = $regex;
118 40
        $this->regexGroup = $regexGroup;
119
120 40
        return $this;
121
    }
122
123
    /**
124
     * @return array|null
125
     */
126 57
    public function getValueRegex(): ?array
127
    {
128 57
        if (!empty($this->regex) && !empty($this->regexGroup))
129
        {
130 20
            return [$this->regexGroup => $this->regex];
131
        }
132
133 57
        return null;
134
    }
135
136
    /**
137
     * @param string|int|float|object|null $value
138
     * @param array                        $parameters
139
     *
140
     * @return self
141
     */
142 57
    public function setValue($value, ...$parameters): DataType
143
    {
144 57
        if (is_numeric($value)) {
145 2
            $value = strval($value);
146 57
        } elseif (is_object($value)) {
147 1
            if (method_exists($value, '__toString')) {
148 1
                $value = strval($value);
149
            } else {
150 1
                $value = null;
151
            }
152
        }
153
154 57
        if ($this->getMinLength() && mb_strlen($value) < $this->getMinLength()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getMinLength() of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
155 1
            $value = null;
156
        }
157
158 57
        if ($this->getMaxLength() && $value !== null && mb_strlen($value) > $this->getMaxLength()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getMaxLength() of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
159 2
            $value = mb_substr($value, 0, $this->getMaxLength());
160
        }
161
162 57
        if (!empty($this->getAllowedValues())) {
163 9
            $match = preg_grep("/$value/i", $this->getAllowedValues());
164
165 9
            if (empty($match)) {
166 3
                $value = null;
167
            } else {
168 9
                $value = array_values($match)[0];
169
            }
170
        }
171
172 57
        $regex = $this->getValueRegex();
173
174 57
        if (!empty($regex)) {
175 20
            preg_match_all(array_values($regex)[0], $value, $matches);
176
177 20
            if (empty($matches[array_key_first($regex)])) {
178 2
                $value = null;
179
            }
180
        }
181
182 57
        if (empty($value) && $this->isRequired())
183
        {
184 4
            throw new InvalidArgumentException($this->getName() . ' need to be set.');
185
        }
186
187 56
        $value = is_string($value) ? trim($value) : $value;
188
189 56
        parent::setValue($value, $parameters[0] ?? []);
190
191 56
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Wszetko\Sitemap\Items\DataTypes\StringType) is incompatible with the return type declared by the interface Wszetko\Sitemap\Interfaces\DataType::setValue of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
192
    }
193
}