Completed
Push — master ( 226003...873ec4 )
by J.D.
03:07
created

WordPoints_Hook_Reaction_Validator::validate()   C

Complexity

Conditions 7
Paths 125

Size

Total Lines 59
Code Lines 26

Duplication

Lines 10
Ratio 16.95 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 10
loc 59
rs 6.4682
cc 7
eloc 26
nc 125
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->reaction->get_all_meta() can also be of type false. However, the property $settings is declared as type array. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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 ) ) {
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...
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 ) ) {
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...
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;
0 ignored issues
show
Bug introduced by
Accessing ID on the interface WordPoints_Hook_ReactionI suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
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