Completed
Push — master ( ea7c62...226003 )
by J.D.
03:16
created

WordPoints_Hook_Extension_Conditions   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 280
Duplicated Lines 2.86 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 32
c 3
b 0
f 0
lcom 1
cbo 8
dl 8
loc 280
rs 9.6

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B get_ui_script_data() 0 22 4
A validate_action_type_settings() 0 3 1
B validate_conditions() 0 55 8
B validate_condition() 8 58 5
A should_hit() 0 10 3
B conditions_are_met() 0 40 6
A get_data_type() 0 14 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * Conditions hook extension class.
5
 *
6
 * @package wordpoints-hooks-api
7
 * @since 1.0.0
8
 */
9
10
/**
11
 * Requires the event args to meet certain conditions for the target to be hit.
12
 *
13
 * @since 1.0.0
14
 */
15
class WordPoints_Hook_Extension_Conditions extends WordPoints_Hook_Extension {
16
17
	/**
18
	 * @since 1.0.0
19
	 */
20
	protected $slug = 'conditions';
21
22
	/**
23
	 * The conditions registry.
24
	 *
25
	 * @since 1.0.0
26
	 *
27
	 * @var WordPoints_Class_Registry_Children
28
	 */
29
	protected $conditions;
30
31
	/**
32
	 * @since 1.0.0
33
	 */
34
	public function __construct() {
35
		$this->conditions = wordpoints_hooks()->conditions;
36
	}
37
38
	/**
39
	 * @since 1.0.0
40
	 */
41
	public function get_ui_script_data() {
42
43
		$conditions_data = array();
44
45
		foreach ( $this->conditions->get_all() as $data_type => $conditions ) {
46
			foreach ( $conditions as $slug => $condition ) {
47
48
				if ( ! ( $condition instanceof WordPoints_Hook_Condition ) ) {
49
					continue;
50
				}
51
52
				$conditions_data[ $data_type ][ $slug ] = array(
53
					'slug'      => $slug,
54
					'data_type' => $data_type,
55
					'title'     => $condition->get_title(),
56
					'fields'    => $condition->get_settings_fields(),
57
				);
58
			}
59
		}
60
61
		return array( 'conditions' => $conditions_data );
62
	}
63
64
	/**
65
	 * @since 1.0.0
66
	 */
67
	protected function validate_action_type_settings( $settings ) {
68
		return $this->validate_conditions( $settings );
69
	}
70
71
	/**
72
	 * Validate the conditions.
73
	 *
74
	 * @since 1.0.0
75
	 *
76
	 * @param array                      $conditions The args and their conditions.
77
	 * @param WordPoints_Hook_Event_Args $event_args The event args object.
78
	 *
79
	 * @return array The validated settings.
80
	 */
81
	public function validate_conditions( $conditions, WordPoints_Hook_Event_Args $event_args = null ) {
82
83
		if ( $event_args ) {
84
			$this->event_args = $event_args;
85
			$this->validator = $event_args->get_validator();
86
		}
87
88
		if ( ! is_array( $conditions ) ) {
89
90
			$this->validator->add_error(
91
				__( 'Conditions do not match expected format.', 'wordpoints' )
92
			);
93
94
			return array();
95
		}
96
97
		foreach ( $conditions as $arg_slug => $sub_args ) {
98
99
			if ( '_conditions' === $arg_slug ) {
100
101
				$this->validator->push_field( $arg_slug );
102
103
				foreach ( $sub_args as $index => $settings ) {
104
105
					$this->validator->push_field( $index );
106
107
					$condition = $this->validate_condition( $settings );
108
109
					if ( $condition ) {
110
						$sub_args[ $index ] = $condition;
111
					}
112
113
					$this->validator->pop_field();
114
				}
115
116
				$this->validator->pop_field();
117
118
			} else {
119
120
				if ( ! $this->event_args->descend( $arg_slug ) ) {
121
					continue;
122
				}
123
124
				$sub_args = $this->validate_action_type_settings( $sub_args );
125
126
				$conditions[ $arg_slug ] = $sub_args;
127
128
				$this->event_args->ascend();
129
			}
130
131
			$conditions[ $arg_slug ] = $sub_args;
132
		}
133
134
		return $conditions;
135
	}
136
137
	/**
138
	 * Validate a condition's settings.
139
	 *
140
	 * @since 1.0.0
141
	 *
142
	 * @param array $settings The condition settings.
143
	 *
144
	 * @return array|false The validated conditions settings, or false if unable to
145
	 *                     validate.
146
	 */
147
	protected function validate_condition( $settings ) {
148
149 View Code Duplication
		if ( ! isset( $settings['type'] ) ) {
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->validator->add_error( __( 'Condition type is missing.', 'wordpoints' ) );
151
			return false;
152
		}
153
154
		$arg = $this->event_args->get_current();
155
156
		$data_type = $this->get_data_type( $arg );
157
158
		if ( ! $data_type ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data_type of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
159
			$this->validator->add_error(
160
				__( 'This type of condition does not work for the selected attribute.', 'wordpoints' )
161
			);
162
163
			return false;
164
		}
165
166
		$condition = wordpoints_hooks()->conditions->get( $data_type, $settings['type'] );
167
168
		if ( ! $condition ) {
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...
169
170
			$this->validator->add_error(
171
				sprintf(
172
					__( 'Unknown condition type &#8220;%s&#8221;.', 'wordpoints' )
173
					, $settings['type']
174
				)
175
				, 'type'
176
			);
177
178
			return false;
179
		}
180
181 View Code Duplication
		if ( ! isset( $settings['settings'] ) ) {
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...
182
			$this->validator->add_error( __( 'Condition settings are missing.', 'wordpoints' ) );
183
			return false;
184
		}
185
186
		$this->validator->push_field( 'settings' );
187
188
		// The condition may call this object's validate_settings() method to
189
		// validate some sub-conditions. When that happens, these properties will be
190
		// reset, so we need to back up their values and then restore them below.
191
		$backup = array( $this->validator, $this->event_args );
192
193
		$settings['settings'] = $condition->validate_settings(
194
			$arg
195
			, $settings['settings']
196
			, $this->validator
197
		);
198
199
		list( $this->validator, $this->event_args ) = $backup;
200
201
		$this->validator->pop_field();
202
203
		return $settings;
204
	}
205
206
	/**
207
	 * @since 1.0.0
208
	 */
209
	public function should_hit( WordPoints_Hook_Fire $fire ) {
210
211
		$conditions = $this->get_settings_from_fire( $fire );
212
213
		if ( $conditions && ! $this->conditions_are_met( $conditions, $fire->event_args ) ) {
214
			return false;
215
		}
216
217
		return true;
218
	}
219
220
	/**
221
	 * Check if the event args meet the conditions.
222
	 *
223
	 * @since 1.0.0
224
	 *
225
	 * @param array                      $conditions The conditions.
226
	 * @param WordPoints_Hook_Event_Args $event_args The event args.
227
	 *
228
	 * @return bool Whether the conditions are met.
229
	 */
230
	public function conditions_are_met(
231
		$conditions,
232
		WordPoints_Hook_Event_Args $event_args
233
	) {
234
235
		foreach ( $conditions as $arg_slug => $sub_args ) {
236
237
			$event_args->descend( $arg_slug );
238
239
			if ( isset( $sub_args['_conditions'] ) ) {
240
241
				foreach ( $sub_args['_conditions'] as $settings ) {
242
243
					$condition = $this->conditions->get(
244
						$this->get_data_type( $event_args->get_current() )
0 ignored issues
show
Security Bug introduced by
It seems like $this->get_data_type($event_args->get_current()) targeting WordPoints_Hook_Extensio...itions::get_data_type() can also be of type false; however, WordPoints_Class_Registry_Children::get() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
245
						, $settings['type']
246
					);
247
248
					$is_met = $condition->is_met( $settings['settings'], $event_args );
249
250
					if ( ! $is_met ) {
251
						$event_args->ascend();
252
						return false;
253
					}
254
				}
255
256
				unset( $sub_args['_conditions'] );
257
			}
258
259
			$are_met = $this->conditions_are_met( $sub_args, $event_args );
260
261
			$event_args->ascend();
262
263
			if ( ! $are_met ) {
264
				return false;
265
			}
266
		}
267
268
		return true;
269
	}
270
271
	/**
272
	 * Get the data type of an entity.
273
	 *
274
	 * @since 1.0.0
275
	 *
276
	 * @param WordPoints_EntityishI $arg An entity object.
277
	 *
278
	 * @return string|false The data type, or false.
279
	 */
280
	protected function get_data_type( $arg ) {
281
282
		if ( $arg instanceof WordPoints_Entity_Attr ) {
283
			$data_type = $arg->get_data_type();
284
		} elseif ( $arg instanceof WordPoints_Entity_Array ) {
285
			$data_type = 'entity_array';
286
		} elseif ( $arg instanceof WordPoints_Entity ) {
287
			$data_type = 'entity';
288
		} else {
289
			$data_type = false;
290
		}
291
292
		return $data_type;
293
	}
294
}
295
296
// EOF
297