WordPoints_Query_Builder_DB_MySQL   B
last analyzed

Complexity

Total Complexity 53

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 307
rs 7.4757
c 0
b 0
f 0
wmc 53
lcom 1
cbo 3

15 Methods

Rating   Name   Duplication   Size   Complexity  
A get_query() 0 13 2
A build_query() 0 12 1
A build_select() 0 8 2
C build_fields() 0 30 7
A build_field() 0 9 1
A build_from() 0 10 2
A build_joins() 0 10 3
C build_join() 0 42 7
A build_where() 0 13 3
C build_condition() 0 66 12
A build_condition_type() 0 20 4
A escape_identifier() 0 4 1
A get_field_name() 0 13 3
A get_field_type() 0 8 3
A serialize_value() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like WordPoints_Query_Builder_DB_MySQL often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WordPoints_Query_Builder_DB_MySQL, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * .
5
 *
6
 * @package wordpoints-hooks-api
7
 * @since 1.
8
 */
9
10
11
interface WordPoints_Query_BuilderI {
12
	public function set_fields( $fields );
13
	public function add_field( $field );
14
	public function set_table( $table_name );
15
	public function get_table();
16
	public function enter_join( $join );
17
	public function exit_join();
18
	public function where( $condition );
19
	public function get_query();
20
}
21
22
abstract class WordPoints_Query_Builder_DB implements WordPoints_Query_BuilderI {
23
24
	/**
25
	 *
26
	 *
27
	 * @since 1.
28
	 *
29
	 * @var WordPoints_Hierarchy
30
	 */
31
	protected $query;
32
33
	protected $aliases = array();
34
35
	public function __construct() {
36
		$this->query = new WordPoints_Hierarchy( 'joins' );
37
		$this->query->push( '', array() );
38
	}
39
40
	public function set_fields( $fields ) {
41
		$this->query->set_field( 'fields', $fields );
42
	}
43
44
	public function add_field( $field ) {
45
		$this->query->push_to( 'fields', $field );
46
	}
47
48
	public function set_table( $table_name ) {
49
		$this->query->set_field( 'table_name',  $table_name );
50
	}
51
52
	public function get_table() {
53
		return $this->query->get_field( 'table_name' );
54
	}
55
56
	protected function get_alias( $data ) {
57
58
		if ( ! isset( $this->aliases[ $data['table_name'] ] ) ) {
59
			$this->aliases[ $data['table_name'] ] = 0;
60
		}
61
62
		return $data['table_name'] . '_' . ++$this->aliases[ $data['table_name'] ];
63
	}
64
65
	public function enter_join( $join ) {
66
		$alias = $this->get_alias( $join );
67
		$this->query->push( $alias, $join );
68
	}
69
70
	public function exit_join() {
71
		$this->query->pop();
72
	}
73
74
	public function where( $condition ) {
75
76
		$where = $this->query->get_field( 'where' );
77
78
		if ( ! is_array( $where ) ) {
79
			$where = array();
80
		}
81
82
		$where[] = $condition;
83
84
		$this->query->set_field( 'where', $where );
85
	}
86
}
87
88
class WordPoints_Query_Builder_Exception extends Exception {}
89
90
class WordPoints_Query_Builder_DB_MySQL extends WordPoints_Query_Builder_DB {
91
92
	protected $sql;
93
94
	protected $fields;
95
96
	protected $alias;
97
98
	public function get_query() {
99
100
		$this->sql = '';
101
102
		var_dump($this->query);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($this->query); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
103
		try {
104
			$this->build_query( $this->query->get() );
105
		} catch ( WordPoints_Query_Builder_Exception $e ) {
106
			return new WP_Error( $e->getMessage(), $e->getTraceAsString() );
107
		}
108
109
		return $this->sql;
110
	}
111
112
	protected function build_query( $query ) {
113
114
		var_dump( __METHOD__, $query );
0 ignored issues
show
Security Debugging Code introduced by
var_dump(__METHOD__, $query); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
115
116
		$this->alias = $this->get_alias( $query );
117
118
		$this->build_fields( $query );
119
		$this->build_from( $query );
120
		$this->build_joins( $query );
121
		$this->build_where( $query );
122
		$this->build_select();
123
	}
124
125
	protected function build_select() {
126
127
		if ( empty( $this->fields ) ) {
128
			throw new WordPoints_Query_Builder_Exception( 'no fields' );
129
		}
130
131
		$this->sql = 'SELECT' . $this->fields . $this->sql;
132
	}
133
134
	protected function build_fields( $query ) {
135
136
		if ( empty( $query['fields'] ) ) {
137
			return;
138
		}
139
140
		foreach ( $query['fields'] as $field ) {
141
142
			if ( ! is_array( $field ) ) {
143
				$field = array( 'field' => $field );
144
			}
145
146
			if ( ! isset( $field['field'] ) ) {
147
				throw new WordPoints_Query_Builder_Exception( 'invalid field' );
148
			}
149
150
			if ( ! empty( $this->fields ) ) {
151
				$this->fields .= ',';
152
			}
153
154
			$this->fields .= ' ' . $this->build_field( $field['field'] );
155
156
			$as = $field['field'];
157
			if ( isset( $field['as'] ) ) {
158
				$as = $field['as'];
159
			}
160
161
			$this->fields .= ' AS `' . $this->escape_identifier( $as ) . '`';
162
		}
163
	}
164
165
	/**
166
	 *
167
	 *
168
	 * @since 1.
169
	 *
170
	 * @param $field
171
	 *
172
	 * @throws WordPoints_Query_Builder_Exception
173
	 */
174
	protected function build_field( $field ) {
175
176
		$field_name = $this->get_field_name( $field );
177
178
		$sql = '`' . $this->escape_identifier( $this->alias ) . '`';
179
		$sql .= '.`' . $this->escape_identifier( $field_name ) . '`';
180
181
		return $sql;
182
	}
183
184
	protected function build_from( $query ) {
185
186
		if ( ! isset( $query['table_name'] ) ) {
187
			throw new WordPoints_Query_Builder_Exception( 'table_name' );
188
		}
189
190
		$this->sql .= "\n";
191
		$this->sql .= 'FROM `' . $this->escape_identifier( $query['table_name'] ) . '`';
192
		$this->sql .= ' AS `' . $this->escape_identifier( $this->alias ) . '`';
193
	}
194
195
	protected function build_joins( $query ) {
196
197
		if ( ! isset( $query['joins'] ) ) {
198
			return;
199
		}
200
201
		foreach ( $query['joins'] as $join ) {
202
			$this->build_join( $join );
203
		}
204
	}
205
206
	protected function build_join( $join ) {
207
208
		if ( ! isset( $join['table_name'] ) ) {
209
			throw new WordPoints_Query_Builder_Exception( 'join table_name' );
210
		}
211
212
		if ( ! isset( $join['on'] ) ) {
213
			throw new WordPoints_Query_Builder_Exception( 'join on' );
214
		}
215
216
		if ( ! isset( $join['on']['join_field'] ) ) {
217
			throw new WordPoints_Query_Builder_Exception( 'join on join_field' );
218
		}
219
220
		if ( ! isset( $join['on']['primary_field'] ) ) {
221
			throw new WordPoints_Query_Builder_Exception( 'join on primary_field' );
222
		}
223
224
		$alias = $this->get_alias( $join );
225
226
		$this->sql .= "\n";
227
		$this->sql .= 'INNER JOIN `' . $this->escape_identifier( $join['table_name'] ) . '`';
228
		$this->sql .= ' AS `' . $this->escape_identifier( $alias ) . '`';
229
		$this->sql .= "\n\t" . 'ON ' . $this->build_field( $join['on']['primary_field'] );
230
231
		$old_alias = $this->alias;
232
		$this->alias = $alias;
233
234
		$this->sql .= ' = ' . $this->build_field( $join['on']['join_field'] );
235
236
		$this->build_fields( $join );
237
238
		if ( isset( $join['where'] ) ) {
239
			foreach ( $join['where'] as $condition ) {
240
				$this->build_condition( $condition );
241
			}
242
		}
243
244
		$this->build_joins( $join );
245
246
		$this->alias = $old_alias;
247
	}
248
249
	protected function build_where( $query ) {
250
251
		if ( empty( $query['where'] ) ) {
252
			return;
253
		}
254
255
		$this->sql .= "\n";
256
		$this->sql .= 'WHERE 1=1';
257
258
		foreach ( $query['where'] as $condition ) {
259
			$this->build_condition( $condition );
260
		}
261
	}
262
263
	protected function build_condition( $condition ) {
264
265
		global $wpdb;
266
267
		if ( ! isset( $condition['field'], $condition['value'] ) ) {
268
			var_dump($condition);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($condition); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
269
			throw new WordPoints_Query_Builder_Exception( 'bad condition' );
270
		}
271
272
		if ( ! isset( $condition['compare'] ) ) {
273
			$condition['compare'] = '=';
274
		}
275
276
		$this->sql .= "\n\t";
277
278
		$this->build_condition_type( $condition );
279
280
		$field_type = $this->get_field_type( $condition['field'] );
281
		if ( 'serialized_array' === $field_type ) {
282
			if ( is_array( $condition['value'] ) ) {
283
				if ( isset( $condition['compare'] ) && 'in' === $condition['compare'] ) {
284
					$this->sql .= '( 1=0 ';
285
					foreach ( $condition['value'] as $index => $value ) {
286
						$this->build_condition(
287
							array(
288
								'type' => 'or',
289
								'value' => "%{$value}%",
290
								'field' => $condition['field'],
291
								'compare' => 'like',
292
							)
293
						);
294
					}
295
					$this->sql .= ')';
296
					return;
297
				}
298
			} else {
299
				$condition['value'] = $this->serialize_value( $condition['value'] );
300
			}
301
		}
302
303
		$this->sql .= $this->build_field( $condition['field'] );
304
305
		switch ( $condition['compare'] ) {
306
307
			case '=':
308
				$this->sql .= ' = ';
309
				break;
310
311
			case 'like':
312
				$this->sql .= ' LIKE ';
313
				break;
314
315
			case 'in':
316
				$this->sql .= ' IN (' . wordpoints_prepare__in( $condition['value'] ) . ')';
317
				return;
318
319
			default:
320
				throw new WordPoints_Query_Builder_Exception( 'bad condition comparison' );
321
		}
322
323
		if ( ! is_scalar( $condition['value'] ) ) {
324
			throw new WordPoints_Query_Builder_Exception( 'bad condition value' );
325
		}
326
327
		$this->sql .= $wpdb->prepare( '%s', $condition['value'] );
328
	}
329
330
	/**
331
	 *
332
	 *
333
	 * @since 1.
334
	 *
335
	 * @param $condition
336
	 *
337
	 * @throws WordPoints_Query_Builder_Exception
338
	 */
339
	protected function build_condition_type( $condition ) {
340
341
		if ( ! isset( $condition['type'] ) ) {
342
			$condition['type'] = 'and';
343
		}
344
345
		switch ( $condition['type'] ) {
346
347
			case 'and':
348
				$this->sql .= 'AND ';
349
				break;
350
351
			case 'or':
352
				$this->sql .= 'OR ';
353
				break;
354
355
			default:
356
				throw new WordPoints_Query_Builder_Exception( 'bad condition type' );
357
		}
358
	}
359
360
	protected function escape_identifier( $identifier ) {
361
362
		return str_replace( '`', '``', $identifier );
363
	}
364
365
	protected function get_field_name( $field ) {
366
367
		if ( is_array( $field ) ) {
368
369
			if ( ! isset( $field['name'] ) ) {
370
				throw new WordPoints_Query_Builder_Exception( 'field name' );
371
			}
372
373
			$field = $field['name'];
374
		}
375
376
		return $field;
377
	}
378
379
	protected function get_field_type( $field ) {
380
381
		if ( is_array( $field ) && isset( $field['type'] ) ) {
382
			return $field['type'];
383
		}
384
385
		return 'string';
386
	}
387
388
	protected function serialize_value( $value ) {
389
390
		if ( is_string( $value ) ) {
391
			$value = '"' . $value . '"';
392
		}
393
394
		return '%:' . $value . ';%';
395
	}
396
}
397
398
// EOF
399