Completed
Push — master ( 38c8ff...90c00b )
by Dmitry
01:29
created

Properties::getProperty()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 1
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace PHPKitchen\Platform\Mixin;
4
5
use PHPKitchen\Platform\Exception\Runtime\Property\InvalidAccessException;
6
use PHPKitchen\Platform\Exception\Runtime\Property\UndefinedPropertyException;
7
8
/**
9
 * Represents implementation of properties for PHP classes.
10
 *
11
 * @author Dmitry Kolodko <[email protected]>
12
 */
13
trait Properties {
14
    /**
15
     * Do not call this method directly as it is a PHP magic method that
16
     * will be implicitly called when executing `$value = $object->property;`.
17
     *
18
     * @param string $name the property name
19
     *
20
     * @return mixed the property value
21
     * @throws UndefinedPropertyException if the property is not defined
22
     * @throws InvalidAccessException if the property is write-only
23
     * @see getProperty()
24
     * @see __set()
25
     */
26
    public function __get($name) {
27
        return $this->getProperty($name);
28
    }
29
30
    /**
31
     * Returns the value of an object property.
32
     *
33
     * @param string $property the property name
34
     *
35
     * @return mixed the property value
36
     * @throws UndefinedPropertyException if the property is not defined
37
     * @throws InvalidAccessException if the property is write-only
38
     * @see setProperty()
39
     */
40
    protected function getProperty(string $property) {
41
42
        if ($this->hasGetterFor($property)) {
43
            return $this->{'get' . $property}();
44
        } elseif ($this->hasCondition($property)) {
45
            return $this->$property();
46
        } elseif ($this->hasSetterFor($property)) {
47
            throw new InvalidAccessException('Getting write-only property: ' . static::class . '::' . $property);
48
        }
49
50
        throw new UndefinedPropertyException('Getting unknown property: ' . static::class . '::' . $property);
51
    }
52
53
    /**
54
     * Do not call this method directly as it is a PHP magic method that
55
     * will be implicitly called when executing `$object->property = $value;`.
56
     *
57
     * @param string $name the property name
58
     * @param mixed $value the property value
59
     *
60
     * @throws UndefinedPropertyException if the property is not defined
61
     * @throws InvalidAccessException if the property is read-only
62
     * @see setProperty()
63
     * @see __get()
64
     */
65
    public function __set($name, $value) {
66
        $this->setProperty($name, $value);
67
    }
68
69
    /**
70
     * Sets value of an object property.
71
     *
72
     * @param string $name the property name
73
     * @param mixed $value the property value
74
     *
75
     * @throws UndefinedPropertyException if the property is not defined
76
     * @throws InvalidAccessException if the property is read-only
77
     * @see getProperty()
78
     */
79
    protected function setProperty(string $name, $value): void {
80
        $setter = 'set' . $name;
81
        if ($this->hasMethod($setter)) {
82
            $this->$setter($value);
83 View Code Duplication
        } elseif ($this->hasMethod('get' . $name)) {
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...
84
            throw new InvalidAccessException('Setting read-only property: ' . static::class . '::' . $name);
85
        } else {
86
            throw new UndefinedPropertyException('Setting unknown property: ' . static::class . '::' . $name);
87
        }
88
    }
89
90
    /**
91
     * Do not call this method directly as it is a PHP magic method that
92
     * will be implicitly called when executing `isset($object->property)`.
93
     *
94
     * @param string $name the property name
95
     *
96
     * @return bool whether the named property is set (not null).
97
     * @see isPropertySet
98
     * @see http://php.net/manual/en/function.isset.php
99
     */
100
    public function __isset($name) {
0 ignored issues
show
Coding Style introduced by
function __isset() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
101
        return $this->isPropertySet($name);
102
    }
103
104
    /**
105
     * Checks if a property is set, i.e. defined and not null.
106
     *
107
     * Note that if the property is not defined, false will be returned.
108
     *
109
     * @param string $name the property name
110
     *
111
     * @return bool whether the named property is set (not null).
112
     */
113
    public function isPropertySet(string $name): bool {
114
        $getter = 'get' . $name;
115
        if ($this->hasMethod($getter)) {
116
            return $this->$getter() !== null;
117
        }
118
119
        return false;
120
    }
121
122
    /**
123
     * Do not call this method directly as it is a PHP magic method that
124
     * will be implicitly called when executing `unset($object->property)`.
125
     *
126
     * @param string $name the property name
127
     *
128
     * @throws InvalidAccessException if the property is read only.
129
     * @see unSetProperty
130
     * @see http://php.net/manual/en/function.unset.php
131
     */
132
    public function __unset($name) {
133
        $this->unSetProperty($name);
134
    }
135
136
    /**
137
     * Sets an object property to null.
138
     *
139
     * Note that if the property is not defined, this method will do nothing.
140
     * If the property is read-only, it will throw an exception.
141
     *
142
     * @param string $name the property name
143
     *
144
     * @throws InvalidAccessException if the property is read only.
145
     */
146
    public function unSetProperty(string $name): void {
147
        $setter = 'set' . $name;
148
        if ($this->hasMethod($setter)) {
149
            $this->$setter(null);
150 View Code Duplication
        } elseif ($this->hasMethod('get' . $name)) {
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...
151
            throw new InvalidAccessException('Unsetting read-only property: ' . static::class . '::' . $name);
152
        }
153
    }
154
155
    /**
156
     * Returns a value indicating whether a property is defined.
157
     *
158
     * A property is defined if:
159
     *
160
     * - the class has a getter or setter method associated with the specified name
161
     *   (in this case, property name is case-insensitive);
162
     * - the class has a member variable with the specified name;
163
     *
164
     * @param string $name the property name
165
     *
166
     * @return bool whether the property is defined
167
     * @see canGetProperty()
168
     * @see canSetProperty()
169
     */
170
    public function hasProperty($name): bool {
171
        return $this->canGetProperty($name) || $this->canSetProperty($name);
172
    }
173
174
    /**
175
     * Returns a value indicating whether a condition property is defined.
176
     *
177
     * A condition property is defined if:
178
     * - the class has a "is" method associated with the specified name;
179
     * - the class has a "has" method associated with the specified name;
180
     *
181
     * Note: property name is case-insensitive
182
     *
183
     * @param string $name the property name
184
     *
185
     * @return bool whether the condition property is defined
186
     */
187
    public function hasCondition($name): bool {
188
        return $this->hasMethod($name) && (strpos($name, 'is') === 0 || strpos($name, 'has') === 0);
189
    }
190
191
    /**
192
     * Returns a value indicating whether a property can be read.
193
     *
194
     * A property is readable if:
195
     *
196
     * - the class has a getter method associated with the specified name
197
     *   (in this case, property name is case-insensitive);
198
     * - the class has a member variable with the specified name;
199
     *
200
     * @param string $property the property name
201
     *
202
     * @return bool whether the property can be read
203
     * @see canSetProperty()
204
     */
205
    public function canGetProperty(string $property): bool {
0 ignored issues
show
Coding Style introduced by
function canGetProperty() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
206
        return $this->hasGetterFor($property) || $this->hasField($property) || $this->hasCondition($property);
207
    }
208
209
    /**
210
     * Returns a value indicating whether a property can be set.
211
     *
212
     * A property is writable if:
213
     * - the class has a setter method associated with the specified name
214
     *   (in this case, property name is case-insensitive);
215
     * - the class has a member variable with the specified name;
216
     *
217
     * @param string $property the property name
218
     *
219
     * @return bool whether the property can be written
220
     * @see canGetProperty()
221
     */
222
    public function canSetProperty(string $property): bool {
0 ignored issues
show
Coding Style introduced by
function canSetProperty() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
223
        return $this->hasSetterFor($property) || $this->hasField($property);
224
    }
225
226
    /**
227
     * Returns a value indicating whether a class field is defined.
228
     *
229
     * @param string $name the field name
230
     *
231
     * @return bool whether the field is defined
232
     */
233
    public function hasField(string $name): bool {
234
        return property_exists($this, $name);
235
    }
236
237
    /**
238
     * Returns a value indicating whether a getter is defined for property.
239
     *
240
     * @param string $property the property name
241
     *
242
     * @return bool whether the getter is defined
243
     */
244
    public function hasGetterFor(string $property): bool {
245
        return $this->hasMethod('get' . $property);
246
    }
247
248
    /**
249
     * Returns a value indicating whether a setter is defined for property.
250
     *
251
     * @param string $property the property name
252
     *
253
     * @return bool whether the getter is defined
254
     */
255
    public function hasSetterFor(string $property): bool {
256
        return $this->hasMethod('set' . $property);
257
    }
258
259
    /**
260
     * Returns a value indicating whether a method is defined.
261
     *
262
     * The default implementation is a call to php function `method_exists()`.
263
     * You may override this method when you implemented the php magic method `__call()`.
264
     *
265
     * @param string $name the method name
266
     *
267
     * @return bool whether the method is defined
268
     */
269
    public function hasMethod(string $name): bool {
270
        return method_exists($this, $name);
271
    }
272
}