1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Hook reaction validator class. |
5
|
|
|
* |
6
|
|
|
* @package wordpoints-hooks-api |
7
|
|
|
* @since 1.0.0 |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Validator for hook reaction settings. |
12
|
|
|
* |
13
|
|
|
* @since 1.0.0 |
14
|
|
|
*/ |
15
|
|
|
final class WordPoints_Hook_Reaction_Validator { |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* The object for the hook reaction being validated, or false if none is set. |
19
|
|
|
* |
20
|
|
|
* @since 1.0.0 |
21
|
|
|
* |
22
|
|
|
* @var WordPoints_Hook_ReactionI|false |
23
|
|
|
*/ |
24
|
|
|
protected $reaction = false; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* The settings of the hook reaction being validated. |
28
|
|
|
* |
29
|
|
|
* @since 1.0.0 |
30
|
|
|
* |
31
|
|
|
* @var array |
32
|
|
|
*/ |
33
|
|
|
protected $settings; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* The slug of the event the reaction is to. |
37
|
|
|
* |
38
|
|
|
* @since 1.0.0 |
39
|
|
|
* |
40
|
|
|
* @var string |
41
|
|
|
*/ |
42
|
|
|
protected $event_slug; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Whether to stop validating after encountering the first error. |
46
|
|
|
* |
47
|
|
|
* @since 1.0.0 |
48
|
|
|
* |
49
|
|
|
* @var bool |
50
|
|
|
*/ |
51
|
|
|
protected $fail_fast = false; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* A stack of fields for tracking the hierarchy of the field being validated. |
55
|
|
|
* |
56
|
|
|
* @since 1.0.0 |
57
|
|
|
* |
58
|
|
|
* @var array |
59
|
|
|
*/ |
60
|
|
|
protected $field_stack = array(); |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* A list of error messages and the fields they are associated with. |
64
|
|
|
* |
65
|
|
|
* @since 1.0.0 |
66
|
|
|
* |
67
|
|
|
* @var array[] |
68
|
|
|
*/ |
69
|
|
|
protected $errors = array(); |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* The event args object for the event this reaction is for. |
73
|
|
|
* |
74
|
|
|
* @since 1.0.0 |
75
|
|
|
* |
76
|
|
|
* @var WordPoints_Hook_Event_Args |
77
|
|
|
*/ |
78
|
|
|
protected $event_args; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Shortcut to the hooks app. |
82
|
|
|
* |
83
|
|
|
* @since 1.0.0 |
84
|
|
|
* |
85
|
|
|
* @var WordPoints_Hooks |
86
|
|
|
*/ |
87
|
|
|
protected $hooks; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* The slug of the reactor the reaction is for. |
91
|
|
|
* |
92
|
|
|
* @since 1.0.0 |
93
|
|
|
* |
94
|
|
|
* @var string |
95
|
|
|
*/ |
96
|
|
|
protected $reactor_slug; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @since 1.0.0 |
100
|
|
|
* |
101
|
|
|
* @param WordPoints_Hook_ReactionI|array $settings The settings or reaction to |
102
|
|
|
* validate. |
103
|
|
|
* @param bool $fail_fast Whether to fail as soon as |
104
|
|
|
* the first error is found. |
105
|
|
|
*/ |
106
|
|
|
public function __construct( $settings, $fail_fast = false ) { |
107
|
|
|
|
108
|
|
|
$this->fail_fast = $fail_fast; |
109
|
|
|
$this->hooks = wordpoints_hooks(); |
110
|
|
|
|
111
|
|
|
if ( $settings instanceof WordPoints_Hook_ReactionI ) { |
112
|
|
|
|
113
|
|
|
$this->reaction = $settings; |
114
|
|
|
$this->settings = $this->reaction->get_all_meta(); |
|
|
|
|
115
|
|
|
$this->event_slug = $this->reaction->get_event_slug(); |
116
|
|
|
$this->reactor_slug = $this->reaction->get_reactor_slug(); |
117
|
|
|
|
118
|
|
|
} else { |
119
|
|
|
|
120
|
|
|
$this->settings = $settings; |
121
|
|
|
|
122
|
|
|
if ( isset( $this->settings['event'] ) ) { |
123
|
|
|
$this->event_slug = $this->settings['event']; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
if ( isset( $this->settings['reactor'] ) ) { |
127
|
|
|
$this->reactor_slug = $this->settings['reactor']; |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Validates the settings for the reaction. |
134
|
|
|
* |
135
|
|
|
* @since 1.0.0 |
136
|
|
|
* |
137
|
|
|
* @return array The validated settings. |
138
|
|
|
*/ |
139
|
|
|
public function validate() { |
140
|
|
|
|
141
|
|
|
$this->errors = $this->field_stack = array(); |
142
|
|
|
|
143
|
|
|
try { |
144
|
|
|
|
145
|
|
|
// We have to bail early if we don't have a valid event. |
146
|
|
|
$fail_fast = $this->fail_fast; |
147
|
|
|
$this->fail_fast = true; |
148
|
|
|
|
149
|
|
View Code Duplication |
if ( ! isset( $this->event_slug ) ) { |
|
|
|
|
150
|
|
|
$this->add_error( __( 'Event is missing.', 'wordpoints' ), 'event' ); |
151
|
|
|
} elseif ( ! $this->hooks->events->is_registered( $this->event_slug ) ) { |
152
|
|
|
$this->add_error( __( 'Event is invalid.', 'wordpoints' ), 'event' ); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
View Code Duplication |
if ( ! isset( $this->reactor_slug ) ) { |
|
|
|
|
156
|
|
|
$this->add_error( __( 'Reactor is missing.', 'wordpoints' ), 'reactor' ); |
157
|
|
|
} elseif ( ! $this->hooks->reactors->is_registered( $this->reactor_slug ) ) { |
158
|
|
|
$this->add_error( __( 'Reactor is invalid.', 'wordpoints' ), 'reactor' ); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
// From here on out we can collect errors as they come (unless we are |
162
|
|
|
// supposed to fail fast). |
163
|
|
|
$this->fail_fast = $fail_fast; |
164
|
|
|
|
165
|
|
|
$event_args = $this->hooks->events->args->get_children( |
166
|
|
|
$this->event_slug |
167
|
|
|
); |
168
|
|
|
|
169
|
|
|
$this->event_args = new WordPoints_Hook_Event_Args( $event_args ); |
170
|
|
|
$this->event_args->set_validator( $this ); |
171
|
|
|
|
172
|
|
|
$reactor = $this->hooks->reactors->get( $this->reactor_slug ); |
173
|
|
|
|
174
|
|
|
$this->settings = $reactor->validate_settings( $this->settings, $this, $this->event_args ); |
175
|
|
|
|
176
|
|
|
/** @var WordPoints_Hook_Extension $extension */ |
177
|
|
|
foreach ( $this->hooks->extensions->get_all() as $extension ) { |
178
|
|
|
$this->settings = $extension->validate_settings( $this->settings, $this, $this->event_args ); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* A hook reaction's settings are being validated. |
183
|
|
|
* |
184
|
|
|
* @param array $settings The settings. |
185
|
|
|
* @param WordPoints_Hook_Reaction_Validator $validator The validator object. |
186
|
|
|
* @param WordPoints_Hook_Event_Args $args The event args object. |
187
|
|
|
*/ |
188
|
|
|
$this->settings = apply_filters( 'wordpoints_hook_reaction_validate', $this->settings, $this, $this->event_args ); |
189
|
|
|
|
190
|
|
|
} catch ( WordPoints_Hook_Validator_Exception $e ) { |
191
|
|
|
|
192
|
|
|
// Do nothing. |
193
|
|
|
unset( $e ); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
return $this->settings; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Get the args for the event this reaction is for. |
201
|
|
|
* |
202
|
|
|
* @since 1.0.0 |
203
|
|
|
* |
204
|
|
|
* @return WordPoints_Hook_Event_Args The event args object. |
205
|
|
|
*/ |
206
|
|
|
public function get_event_args() { |
207
|
|
|
return $this->event_args; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Adds an error to the stack. |
212
|
|
|
* |
213
|
|
|
* @since 1.0.0 |
214
|
|
|
* |
215
|
|
|
* @param string $message The message to add. |
216
|
|
|
* @param string $field The field the message is for. |
217
|
|
|
* |
218
|
|
|
* @throws WordPoints_Hook_Validator_Exception If the validator is configured to |
219
|
|
|
* fail as soon as an error is found. |
220
|
|
|
*/ |
221
|
|
|
public function add_error( $message, $field = null ) { |
222
|
|
|
|
223
|
|
|
$field_stack = $this->field_stack; |
224
|
|
|
|
225
|
|
|
if ( null !== $field ) { |
226
|
|
|
$field_stack[] = $field; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
$this->errors[] = array( 'message' => $message, 'field' => $field_stack ); |
230
|
|
|
|
231
|
|
|
if ( $this->fail_fast ) { |
232
|
|
|
throw new WordPoints_Hook_Validator_Exception; |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Checks if any settings were invalid, giving errors. |
238
|
|
|
* |
239
|
|
|
* @since 1.0.0 |
240
|
|
|
* |
241
|
|
|
* @return bool Whether the validator found any errors. |
242
|
|
|
*/ |
243
|
|
|
public function had_errors() { |
244
|
|
|
return ! empty( $this->errors ); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Get the errors. |
249
|
|
|
* |
250
|
|
|
* @since 1.0.0 |
251
|
|
|
* |
252
|
|
|
* @return array[] The messages and the fields they relate to. |
253
|
|
|
*/ |
254
|
|
|
public function get_errors() { |
255
|
|
|
return $this->errors; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Push a field onto the field stack. |
260
|
|
|
* |
261
|
|
|
* @since 1.0.0 |
262
|
|
|
* |
263
|
|
|
* @param string $field The field. |
264
|
|
|
*/ |
265
|
|
|
public function push_field( $field ) { |
266
|
|
|
$this->field_stack[] = $field; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Pop a field off of the field stack. |
271
|
|
|
* |
272
|
|
|
* @since 1.0.0 |
273
|
|
|
*/ |
274
|
|
|
public function pop_field() { |
275
|
|
|
array_pop( $this->field_stack ); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Get the current field hierarchy. |
280
|
|
|
* |
281
|
|
|
* @since 1.0.0 |
282
|
|
|
* |
283
|
|
|
* @return string[] The field stack. |
284
|
|
|
*/ |
285
|
|
|
public function get_field_stack() { |
286
|
|
|
return $this->field_stack; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* Get the reaction settings. |
291
|
|
|
* |
292
|
|
|
* @since 1.0.0 |
293
|
|
|
* |
294
|
|
|
* @return array |
295
|
|
|
*/ |
296
|
|
|
public function get_settings() { |
297
|
|
|
return $this->settings; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Get the reaction object. |
302
|
|
|
* |
303
|
|
|
* @since 1.0.0 |
304
|
|
|
* |
305
|
|
|
* @return WordPoints_Hook_ReactionI|false The reaction, or false if not set. |
306
|
|
|
*/ |
307
|
|
|
public function get_reaction() { |
308
|
|
|
return $this->reaction; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Get the reaction ID. |
313
|
|
|
* |
314
|
|
|
* @since 1.0.0 |
315
|
|
|
* |
316
|
|
|
* @return int|false The reaction ID, or false if no reaction is set. |
317
|
|
|
*/ |
318
|
|
|
public function get_id() { |
319
|
|
|
|
320
|
|
|
if ( ! $this->reaction ) { |
321
|
|
|
return false; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
return $this->reaction->ID; |
|
|
|
|
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Get the slug of the event this reaction is for. |
329
|
|
|
* |
330
|
|
|
* @since 1.0.0 |
331
|
|
|
* |
332
|
|
|
* @return string The event slug. |
333
|
|
|
*/ |
334
|
|
|
public function get_event_slug() { |
335
|
|
|
return $this->event_slug; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Get the slug of the reactor this reaction is for. |
340
|
|
|
* |
341
|
|
|
* @since 1.0.0 |
342
|
|
|
* |
343
|
|
|
* @return string The reactor slug. |
344
|
|
|
*/ |
345
|
|
|
public function get_reactor_slug() { |
346
|
|
|
return $this->reactor_slug; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Get a piece of metadata for this reaction. |
351
|
|
|
* |
352
|
|
|
* @since 1.0.0 |
353
|
|
|
* |
354
|
|
|
* @param string $key The meta key. |
355
|
|
|
* |
356
|
|
|
* @return mixed The meta value. |
357
|
|
|
*/ |
358
|
|
|
public function get_meta( $key ) { |
359
|
|
|
|
360
|
|
|
if ( ! isset( $this->settings[ $key ] ) ) { |
361
|
|
|
return null; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
return $this->settings[ $key ]; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* Get all metadata for this reaction. |
369
|
|
|
* |
370
|
|
|
* @since 1.0.0 |
371
|
|
|
* |
372
|
|
|
* @return array The reaction metadata. |
373
|
|
|
*/ |
374
|
|
|
public function get_all_meta() { |
375
|
|
|
return $this->settings; |
376
|
|
|
} |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
// EOF |
380
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.