1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Base daft objects. |
4
|
|
|
* |
5
|
|
|
* @author SignpostMarv |
6
|
|
|
*/ |
7
|
|
|
declare(strict_types=1); |
8
|
|
|
|
9
|
|
|
namespace SignpostMarv\DaftObject; |
10
|
|
|
|
11
|
|
|
use BadMethodCallException; |
12
|
|
|
use ReflectionMethod; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Base daft object. |
16
|
|
|
*/ |
17
|
|
|
abstract class AbstractDaftObject implements DaftObject |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* List of properties that can be defined on an implementation. |
21
|
|
|
* |
22
|
|
|
* @var string[] |
23
|
|
|
*/ |
24
|
|
|
const PROPERTIES = []; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* List of nullable properties that can be defined on an implementation. |
28
|
|
|
* |
29
|
|
|
* @var string[] |
30
|
|
|
*/ |
31
|
|
|
const NULLABLE_PROPERTIES = []; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* List of exportable properties that can be defined on an implementation. |
35
|
|
|
* |
36
|
|
|
* @var string[] |
37
|
|
|
*/ |
38
|
|
|
const EXPORTABLE_PROPERTIES = []; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* import/export definition for DaftJson. |
42
|
|
|
*/ |
43
|
|
|
const JSON_PROPERTIES = []; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Does some sanity checking. |
47
|
|
|
* |
48
|
|
|
* @see DefinesOwnIdPropertiesInterface |
49
|
|
|
* @see self::CheckTypeDefinesOwnIdProperties() |
50
|
|
|
*/ |
51
|
189 |
|
public function __construct() |
52
|
|
|
{ |
53
|
|
|
if ( |
54
|
189 |
|
($this instanceof DefinesOwnIdPropertiesInterface) |
55
|
|
|
) { |
56
|
117 |
|
self::CheckTypeDefinesOwnIdProperties($this); |
57
|
|
|
} |
58
|
187 |
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* {@inheritdoc} |
62
|
|
|
*/ |
63
|
41 |
|
public function __get(string $property) |
64
|
|
|
{ |
65
|
41 |
|
return $this->DoGetSet( |
66
|
41 |
|
$property, |
67
|
41 |
|
'Get' . ucfirst($property), |
68
|
41 |
|
PropertyNotReadableException::class, |
69
|
41 |
|
NotPublicGetterPropertyException::class |
70
|
|
|
); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* {@inheritdoc} |
75
|
|
|
*/ |
76
|
80 |
|
public function __set(string $property, $v) |
77
|
|
|
{ |
78
|
80 |
|
return $this->DoGetSet( |
79
|
80 |
|
$property, |
80
|
80 |
|
'Set' . ucfirst($property), |
81
|
80 |
|
PropertyNotWriteableException::class, |
82
|
80 |
|
NotPublicSetterPropertyException::class, |
83
|
80 |
|
$v |
84
|
|
|
); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* {@inheritdoc} |
89
|
|
|
* |
90
|
|
|
* @see static::NudgePropertyValue() |
91
|
|
|
*/ |
92
|
20 |
|
public function __unset(string $property) : void |
93
|
|
|
{ |
94
|
20 |
|
$this->NudgePropertyValue($property, null); |
95
|
18 |
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* {@inheritdoc} |
99
|
|
|
*/ |
100
|
31 |
|
public function __debugInfo() : array |
101
|
|
|
{ |
102
|
31 |
|
$out = []; |
103
|
31 |
|
foreach (static::DaftObjectExportableProperties() as $prop) { |
104
|
30 |
|
$expectedMethod = 'Get' . ucfirst($prop); |
105
|
|
|
if ( |
106
|
30 |
|
$this->__isset($prop) && |
107
|
30 |
|
method_exists($this, $expectedMethod) && |
108
|
|
|
( |
109
|
24 |
|
new ReflectionMethod(static::class, $expectedMethod) |
110
|
24 |
|
)->isPublic() |
111
|
|
|
) { |
112
|
24 |
|
$out[$prop] = $this->$expectedMethod(); |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
31 |
|
return $out; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* List of properties that can be defined on an implementation. |
121
|
|
|
* |
122
|
|
|
* @return string[] |
123
|
|
|
*/ |
124
|
217 |
|
final public static function DaftObjectProperties() : array |
125
|
|
|
{ |
126
|
217 |
|
return static::PROPERTIES; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* {@inheritdoc} |
131
|
|
|
*/ |
132
|
57 |
|
final public static function DaftObjectNullableProperties() : array |
133
|
|
|
{ |
134
|
57 |
|
return static::NULLABLE_PROPERTIES; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* {@inheritdoc} |
139
|
|
|
*/ |
140
|
63 |
|
final public static function DaftObjectExportableProperties() : array |
141
|
|
|
{ |
142
|
63 |
|
return static::EXPORTABLE_PROPERTIES; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* {@inheritdoc} |
147
|
|
|
*/ |
148
|
28 |
|
final public static function DaftObjectJsonProperties() : array |
149
|
|
|
{ |
150
|
28 |
View Code Duplication |
if (false === is_a(static::class, DaftJson::class, true)) { |
|
|
|
|
151
|
16 |
|
throw new BadMethodCallException( |
152
|
|
|
static::class . |
153
|
|
|
' does not implement ' . |
154
|
16 |
|
DaftJson::class |
155
|
|
|
); |
156
|
|
|
} |
157
|
|
|
|
158
|
12 |
|
return static::JSON_PROPERTIES; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Nudge the state of a given property, marking it as dirty. |
163
|
|
|
* |
164
|
|
|
* @param string $property property being nudged |
165
|
|
|
* @param mixed $value value to nudge property with |
166
|
|
|
* |
167
|
|
|
* @throws UndefinedPropertyException if $property is not in static::DaftObjectProperties() |
168
|
|
|
* @throws PropertyNotNullableException if $property is not in static::DaftObjectNullableProperties() |
169
|
|
|
* @throws PropertyNotRewriteableException if class is write-once read-many and $property was already changed |
170
|
|
|
*/ |
171
|
|
|
abstract protected function NudgePropertyValue( |
172
|
|
|
string $property, |
173
|
|
|
$value |
174
|
|
|
) : void; |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Checks if a type correctly defines it's own id. |
178
|
|
|
* |
179
|
|
|
* @param DaftObject $object |
180
|
|
|
* |
181
|
|
|
* @throws ClassDoesNotImplementClassException if $object is not an implementation of DefinesOwnIdPropertiesInterface |
182
|
|
|
* @throws ClassMethodReturnHasZeroArrayCountException if $object::DaftObjectIdProperties() does not contain at least one property |
183
|
|
|
* @throws ClassMethodReturnIsNotArrayOfStringsException if $object::DaftObjectIdProperties() is not string[] |
184
|
|
|
* @throws UndefinedPropertyException if an id property is not in $object::DaftObjectIdProperties() |
185
|
|
|
*/ |
186
|
118 |
|
final protected static function CheckTypeDefinesOwnIdProperties( |
187
|
|
|
DaftObject $object |
188
|
|
|
) : void { |
189
|
118 |
|
$class = get_class($object); |
190
|
118 |
|
if (false === ($object instanceof DefinesOwnIdPropertiesInterface)) { |
191
|
1 |
|
throw new ClassDoesNotImplementClassException( |
192
|
1 |
|
$class, |
193
|
1 |
|
DefinesOwnIdPropertiesInterface::class |
194
|
|
|
); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @var DefinesOwnIdPropertiesInterface $object |
199
|
|
|
*/ |
200
|
117 |
|
$object = $object; |
201
|
|
|
|
202
|
117 |
|
$properties = $object::DaftObjectIdProperties(); |
203
|
|
|
|
204
|
117 |
|
if (count($properties) < 1) { |
205
|
1 |
|
throw new ClassMethodReturnHasZeroArrayCountException( |
206
|
1 |
|
$class, |
207
|
1 |
|
'DaftObjectIdProperties' |
208
|
|
|
); |
209
|
|
|
} |
210
|
|
|
|
211
|
116 |
|
foreach ($properties as $property) { |
212
|
116 |
|
if (false === is_string($property)) { |
213
|
1 |
|
throw new ClassMethodReturnIsNotArrayOfStringsException( |
214
|
1 |
|
$class, |
215
|
1 |
|
'DaftObjectIdProperties' |
216
|
|
|
); |
217
|
|
|
} |
218
|
|
|
} |
219
|
115 |
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* @param mixed $v |
223
|
|
|
* |
224
|
|
|
* @return mixed |
225
|
|
|
*/ |
226
|
109 |
|
protected function DoGetSet( |
227
|
|
|
string $property, |
228
|
|
|
string $expectedMethod, |
229
|
|
|
string $notExists, |
230
|
|
|
string $notPublic, |
231
|
|
|
$v = null |
232
|
|
|
) { |
233
|
|
|
if ( |
234
|
|
|
( |
235
|
109 |
|
'id' !== $property || |
236
|
9 |
|
false === ($this instanceof DefinesOwnIdPropertiesInterface) |
237
|
|
|
) && |
238
|
109 |
|
false === in_array($property, static::DaftObjectProperties(), true) |
239
|
|
|
) { |
240
|
3 |
|
throw new UndefinedPropertyException(static::class, $property); |
241
|
106 |
|
} elseif (false === method_exists($this, $expectedMethod)) { |
242
|
2 |
|
throw new $notExists( |
243
|
2 |
|
static::class, |
244
|
2 |
|
$property |
245
|
|
|
); |
246
|
|
|
} elseif ( |
247
|
|
|
false === ( |
248
|
104 |
|
new ReflectionMethod(static::class, $expectedMethod) |
249
|
104 |
|
)->isPublic() |
250
|
|
|
) { |
251
|
2 |
|
throw new $notPublic( |
252
|
2 |
|
static::class, |
253
|
2 |
|
$property |
254
|
|
|
); |
255
|
|
|
} |
256
|
|
|
|
257
|
102 |
|
return $this->$expectedMethod($v); |
258
|
|
|
} |
259
|
|
|
} |
260
|
|
|
|
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.