Property::setValue()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Flying\Struct\Property;
4
5
use Flying\Config\AbstractConfig;
6
use Flying\Struct\Common\UpdateNotifyListenerInterface;
7
use Flying\Struct\Exception;
8
9
/**
10
 * Abstract implementation of structure property
11
 */
12
class Property extends AbstractConfig implements PropertyInterface
13
{
14
    /**
15
     * TRUE to skip property change notification, FALSE otherwise
16
     *
17
     * @var boolean
18
     */
19
    protected $skipNotify = false;
20
    /**
21
     * Property value
22
     *
23
     * @var mixed
24
     */
25
    private $value;
26
    /**
27
     * Cached value of "nullable" configuration option
28
     *
29
     * @var boolean
30
     */
31
    private $nullable = true;
32
33
    /**
34
     * Class constructor
35
     *
36
     * @param mixed $value  OPTIONAL Property value
37
     * @param array $config OPTIONAL Configuration options for this property
38
     * @throws \Flying\Struct\Exception
39
     * @throws \RuntimeException
40
     */
41 397 View Code Duplication
    public function __construct($value = null, array $config = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
42
    {
43
        // No change notification is required during object construction
44 397
        $flag = $this->skipNotify;
45 397
        $this->skipNotify = true;
46 397
        $this->bootstrapConfig();
47 397
        $this->setConfig($config);
0 ignored issues
show
Bug introduced by
It seems like $config defined by parameter $config on line 41 can also be of type null; however, Flying\Config\AbstractConfig::setConfig() does only seem to accept array|object|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
48
        // We must be sure that property value is always valid
49
        // even if no value for the property is given explicitly
50 386
        if ($value !== null) {
51 140
            if (!$this->setValue($value)) {
52 22
                $this->reset();
53 22
            }
54 139
        } else {
55 297
            $this->reset();
56
        }
57 384
        $this->skipNotify = $flag;
58 384
    }
59
60
    /**
61
     * Reset property to its default state
62
     *
63
     * @throws Exception
64
     * @return void
65
     * @throws \RuntimeException
66
     */
67 246
    public function reset()
68
    {
69
        // No change notification should be made for reset,
70
        // property value should be set to its default
71 246
        $flag = $this->skipNotify;
72 246
        $this->skipNotify = true;
73 246
        $default = $this->getConfig('default');
74 246
        if ($this->normalize($default)) {
75 245
            $this->value = $default;
76 245
        } else {
77 1
            throw new Exception('Default value for property class ' . get_class($this) . ' is not acceptable for property validation rules');
78
        }
79 245
        $this->skipNotify = $flag;
80 245
    }
81
82
    /**
83
     * Normalize given value to make it compatible with property requirements
84
     *
85
     * @param mixed $value  Given property value (passed by reference)
86
     * @return boolean      TRUE if value can be accepted, FALSE otherwise
87
     */
88 361
    protected function normalize(&$value)
89
    {
90 361
        if (($value === null) && (!$this->nullable)) {
91 11
            return false;
92
        }
93 360
        if ($value instanceof PropertyInterface) {
94 7
            $value = $value->getValue();
95 7
        }
96 360
        return true;
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     * @throws \InvalidArgumentException
102
     */
103 338
    public function validateConfig($name, &$value)
104
    {
105
        switch ($name) {
106 338
            case 'nullable':
107 257
                $value = (boolean)$value;
108 257
                break;
109 320
            case 'default':
110 265
                break;
111 289 View Code Duplication
            case 'update_notify_listener':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
112 289
                if (($value !== null) && (!$value instanceof UpdateNotifyListenerInterface)) {
113
                    throw new \InvalidArgumentException('Update notifications listener must implement UpdateNotifyListenerInterface');
114
                }
115 289
                break;
116
        }
117 338
        return true;
118
    }
119
120
    /**
121
     * Implementation of Serializable interface
122
     *
123
     * @return string
124
     * @throws \RuntimeException
125
     */
126 20
    public function serialize()
127
    {
128 20
        return serialize([
129 20
            'value'  => $this->getValue(),
130 20
            'config' => $this->getConfigForSerialization(),
131 20
        ]);
132
    }
133
134
    /**
135
     * Get property value
136
     *
137
     * @return mixed
138
     */
139 240
    public function getValue()
140
    {
141 240
        return $this->value;
142
    }
143
144
    /**
145
     * Set property value
146
     *
147
     * @param mixed $value
148
     * @return boolean
149
     * @throws \RuntimeException
150
     */
151 165
    public function setValue($value)
152
    {
153 165
        if ($this->normalize($value)) {
154 149
            $this->value = $value;
155 149
            $this->onChange();
156 149
            return true;
157
        }
158
159 24
        $this->onInvalidValue($value);
160 24
        return false;
161
    }
162
163
    /**
164
     * Get property's configuration options prepared for serialization
165
     *
166
     * @return array
167
     * @throws \RuntimeException
168
     */
169 20
    protected function getConfigForSerialization()
170
    {
171 20
        $config = $this->getConfig();
172 20
        unset($config['update_notify_listener']);
173 20
        return $config;
174
    }
175
176
    /**
177
     * Implementation of Serializable interface
178
     *
179
     * @param string $serialized Serialized object data
180
     * @return void
181
     * @throws \InvalidArgumentException
182
     * @throws \RuntimeException
183
     */
184 20
    public function unserialize($serialized)
185
    {
186 20
        $data = unserialize($serialized);
187 20
        if ((!is_array($data)) ||
188 20
            (!array_key_exists('value', $data)) ||
189 20
            (!array_key_exists('config', $data)) ||
190 20
            (!is_array($data['config']))
191 20
        ) {
192
            throw new \InvalidArgumentException('Serialized property information has invalid format');
193
        }
194 20
        $flag = $this->skipNotify;
195 20
        $this->skipNotify = true;
196 20
        $this->setConfig($data['config']);
197 20
        $this->setValue($data['value']);
198 20
        $this->skipNotify = $flag;
199 20
    }
200
201
    /**
202
     * Value change notification handler
203
     *
204
     * @return void
205
     * @throws \RuntimeException
206
     */
207 219 View Code Duplication
    protected function onChange()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208
    {
209 219
        if ($this->skipNotify) {
210 125
            return;
211
        }
212 159
        $owner = $this->getConfig('update_notify_listener');
213 159
        if ($owner instanceof UpdateNotifyListenerInterface) {
214 54
            $owner->updateNotify($this);
215 54
        }
216 159
    }
217
218
    /**
219
     * Invalid value setting handler
220
     *
221
     * @param mixed $value
222
     * @return void
223
     */
224 24
    protected function onInvalidValue($value)
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
225
    {
226
227 24
    }
228
229
    /**
230
     * {@inheritdoc}
231
     * @throws \InvalidArgumentException
232
     * @throws \RuntimeException
233
     */
234 3
    protected function initConfig()
235
    {
236 3
        parent::initConfig();
237 3
        $this->mergeConfig([
238 3
            'nullable'               => true, // TRUE if property value can be NULL, FALSE if not
239 3
            'default'                => null, // Default value for the property
240 3
            'update_notify_listener' => null, // Listener of property update notifications
241 3
        ]);
242 3
    }
243
244
    /**
245
     * {@inheritdoc}
246
     */
247 293
    protected function onConfigChange($name, $value)
248
    {
249
        switch ($name) {
250 293
            case 'nullable':
251 256
                $this->nullable = $value;
252 256
                break;
253
        }
254 293
    }
255
}
256