Completed
Push — master ( 918a00...a5f333 )
by Arnold
13s queued 11s
created

With   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Test Coverage

Coverage 96.55%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 54
c 2
b 0
f 0
dl 0
loc 169
ccs 56
cts 58
cp 0.9655
rs 10
wmc 29

6 Methods

Rating   Name   Duplication   Size   Complexity  
A withPropertyItem() 0 17 5
A withoutPropertyKey() 0 17 5
A withProperty() 0 17 6
A withoutPropertyItem() 0 19 4
A withoutProperty() 0 14 3
A withPropertyKey() 0 17 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Jasny\Immutable;
6
7
use ArrayAccess;
8
9
/**
10
 * Trait with the `withProperty` methods that can be used by classes of immutable objects.
11
 *
12
 * {@internal Some lines are expected to be covered, which should be ignored. Added codeCoverageIgnore. }}
13
 */
14
trait With
15
{
16
    /**
17
     * Return a copy with a changed property.
18
     * Returns this object if the resulting object would be the same as the current one.
19
     *
20
     * @param string  $property
21
     * @param mixed   $value
22
     * @return static
23
     * @throws \BadMethodCallException if property doesn't exist
24
     */
25 2
    protected function withProperty(string $property, $value)
26
    {
27 2
        if (!property_exists($this, $property)) {
28 1
            throw new \BadMethodCallException(sprintf('%s has no property "%s"', get_class($this), $property));
29
        }
30
31
        if (
32 1
            (isset($this->{$property}) && $this->{$property} === $value) || // Typed property may be not initialized
33 1
            (!isset($this->{$property}) && $value === null)        
34
        ) {
35 1
            return $this;
36
        }
37
38 1
        $clone = clone $this;
39 1
        $clone->{$property} = $value;
40
41 1
        return $clone;
42
    }
43
44
    /**
45
     * Return a copy with a property unset.
46
     * Returns this object if the resulting object would be the same as the current one.
47
     *
48
     * @param string  $property
49
     * @return static
50
     * @throws \BadMethodCallException if property doesn't exist
51
     */
52 2
    protected function withoutProperty(string $property)
53
    {
54 2
        if (!property_exists($this, $property)) {
55 1
            throw new \BadMethodCallException(sprintf('%s has no property "%s"', get_class($this), $property));
56
        }
57
58 1
        if (!isset($this->{$property})) {
59 1
            return $this;
60
        }
61
62 1
        $clone = clone $this;
63 1
        unset($clone->{$property});
64
65 1
        return $clone;
66
    }
67
68
69
    /**
70
     * Return a copy with an added item for a property.
71
     * Returns this object if the resulting object would be the same as the current one.
72
     *
73
     * @param string  $property
74
     * @param string  $key
75
     * @param mixed   $value
76
     * @return static
77
     * @throws \BadMethodCallException if property doesn't exist
78
     */
79 2
    protected function withPropertyKey(string $property, string $key, $value)
80
    {
81 2
        if (!property_exists($this, $property)) {
82
            throw new \BadMethodCallException(sprintf('%s has no property "%s"', get_class($this), $property));
83
        } // @codeCoverageIgnore
84 2
        if (!is_array($this->{$property}) && !$this->{$property} instanceof ArrayAccess) {
85 1
            throw new \BadMethodCallException(sprintf('%s::$%s is not an array', get_class($this), $property));
86
        }
87
88 1
        if (isset($this->{$property}[$key]) && $this->{$property}[$key] === $value) {
89 1
            return $this;
90
        }
91
92 1
        $clone = clone $this;
93 1
        $clone->{$property}[$key] = $value;
94
95 1
        return $clone;
96
    }
97
98
    /**
99
     * Return a copy with a removed item from a property.
100
     * Returns this object if the resulting object would be the same as the current one.
101
     *
102
     * @param string  $property
103
     * @param string  $key
104
     * @return static
105
     * @throws \BadMethodCallException if property doesn't exist or isn't an array
106
     */
107 3
    protected function withoutPropertyKey(string $property, string $key)
108
    {
109 3
        if (!property_exists($this, $property)) {
110 1
            throw new \BadMethodCallException(sprintf('%s has no property "%s"', get_class($this), $property));
111
        } // @codeCoverageIgnore
112 2
        if (!is_array($this->{$property}) && !$this->{$property} instanceof ArrayAccess) {
113 1
            throw new \BadMethodCallException(sprintf('%s::$%s is not an array', get_class($this), $property));
114
        }
115
116 1
        if (!isset($this->{$property}[$key])) {
117 1
            return $this;
118
        }
119
120 1
        $clone = clone $this;
121 1
        unset($clone->{$property}[$key]);
122
123 1
        return $clone;
124
    }
125
126
127
    /**
128
     * Return a copy with a value added to a sequential array.
129
     *
130
     * @param string  $property
131
     * @param mixed   $value
132
     * @param mixed   $unique    Don't add if the array already has a copy of the value.
133
     * @return static
134
     * @throws \BadMethodCallException if property doesn't exist or isn't an array
135
     */
136 2
    protected function withPropertyItem(string $property, $value, bool $unique = false)
137
    {
138 2
        if (!property_exists($this, $property)) {
139
            throw new \BadMethodCallException(sprintf('%s has no property "%s"', get_class($this), $property));
140
        } // @codeCoverageIgnore
141 2
        if (!is_array($this->{$property})) {
142 1
            throw new \BadMethodCallException(sprintf('%s::$%s is not an array', get_class($this), $property));
143
        }
144
145 1
        if ($unique && in_array($value, $this->{$property}, true)) {
146 1
            return $this;
147
        }
148
149 1
        $clone = clone $this;
150 1
        $clone->{$property}[] = $value;
151
152 1
        return $clone;
153
    }
154
155
    /**
156
     * Return a copy with a value removed from a sequential array.
157
     * Returns this object if the resulting object would be the same as the current one.
158
     *
159
     * @param string  $property
160
     * @param mixed   $value
161
     * @return static
162
     * @throws \BadMethodCallException if property doesn't exist or isn't an array
163
     */
164 3
    protected function withoutPropertyItem(string $property, $value)
165
    {
166 3
        if (!property_exists($this, $property)) {
167 1
            throw new \BadMethodCallException(sprintf('%s has no property "%s"', get_class($this), $property));
168
        }
169 2
        if (!is_array($this->{$property})) {
170 1
            throw new \BadMethodCallException(sprintf('%s::$%s is not an array', get_class($this), $property));
171
        }
172
173 1
        $keys = array_keys($this->{$property}, $value, true);
174
175 1
        if ($keys === []) {
176 1
            return $this;
177
        }
178
179 1
        $clone = clone $this;
180 1
        $clone->{$property} = array_values(array_diff_key($clone->{$property}, array_fill_keys($keys, null)));
181
182 1
        return $clone;
183
    }
184
}
185