Completed
Push — tar-fix ( d474e8...0c2e2e )
by
unknown
09:21 queued 06:42
created

mysql_compiler   F

Complexity

Total Complexity 86

Size/Duplication

Total Lines 373
Duplicated Lines 36.73 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 137
loc 373
ccs 0
cts 314
cp 0
rs 2
c 0
b 0
f 0
wmc 86
lcom 1
cbo 1

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
F compile_tree() 121 273 72
F priv_sql_compile() 16 73 13

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like mysql_compiler 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 mysql_compiler, and based on these observations, apply Extract Interface, too.

1
<?php
2
  include_once($this->code."stores/modules/sql_compiler.php");
3
4
class mysql_compiler extends sql_compiler {
5
	protected $tbl_prefix;
6
	protected $store;
7
	protected $in_orderby;
8
	protected $nls_join;
9
	protected $select_tables;
10
	protected $used_tables;
11
	protected $custom_id;
12
	protected $custom_ref;
13
	protected $used_custom_fields;
14
	protected $where_s_ext;
15
	protected $fulltext_expr;
16
	protected $where_s;
17
	protected $limit_s;
18
	protected $orderby_s;
19
20
21
	public function __construct (&$store, $tbl_prefix="") {
22
		debug("mysql_compiler($tbl_prefix)", "store");
23
		$this->tbl_prefix=$tbl_prefix;
24
		$this->store=$store;
25
	}
26
27
	protected function compile_tree(&$node, $arguments=null) {
28
		if ($arguments) {
29
			extract($arguments);
30
		}
31
		switch ((string)$node["id"]) {
32
			case 'property':
33
				$table=$this->tbl_prefix.$node["table"];
34
				$field=$node["field"];
35
				$record_id=$node["record_id"];
36
				if (!$record_id) {
37 View Code Duplication
					if ($this->in_orderby && $node["nls"]) {
38
						/*
39
							we do a left join so that we will also find non
40
							matching objects
41
						*/
42
						$objects_table = $this->tbl_prefix."objects";
43
						$aliastable = $table.$node['nls'];
44
						$this->nls_join[$table.$node['nls']] = "left join $table as order_$aliastable on $objects_table.id=order_$aliastable.object and order_$aliastable.AR_nls='".$node["nls"]."' ";
45
						$result = " order_$aliastable.$field ";
46
					} else {
47
						/*
48
							if we are parsing 'orderby' properties we have
49
							to join our tables for the whole query
50
						*/
51
						$this->select_tables[$table]=$table;
52
						$this->used_tables[$table]=$table;
53
						$result=" $table.$field ";
54
					}
55
				} else {
56
					$this->used_tables["$table as $table$record_id"] = $table.$record_id;
57
					if (!$this->in_orderby && !$no_context_join) {
58
						if ($this->join_target_properties[$node["table"]][":$record_id"]) {
59
							$result=" $table$record_id.object = target.object and $table$record_id.$field ";
60
						} else {
61
							$result=" $table$record_id.object = ".$this->tbl_prefix."objects.id and $table$record_id.$field ";
62
						}
63
					} else {
64
						if ($this->join_target_properties[$node["table"]]) {
65
							$this->join_target_properties["$table as $table$record_id"] = $table.$record_id;
66
						}
67
						$this->select_tables["$table as $table$record_id"] = $table.$record_id;
68
						$result=" $table$record_id.$field ";
69
					}
70
				}
71
			break;
72 View Code Duplication
			case 'ident':
73
				$table=$this->tbl_prefix.$node["table"];
74
				$field=$node["field"];
75
				$this->used_tables[$table]=$table;
76
				$result=" $table.$field ";
77
			break;
78 View Code Duplication
			case 'custom':
79
				$table = $this->tbl_prefix."prop_custom";
80
				$field = $node["field"];
81
				$nls = $node["nls"];
82
				$record_id = $node["record_id"];
83
				/*
84
					when we are compiling orderby properties we always want
85
					to assign it to a new table alias
86
				*/
87
				if ($this->in_orderby) {
88
					$this->custom_id++;
89
				}
90
				$this->custom_ref++;
91
				if (!$record_id) {
92
					$this->used_tables[$table." as $table".$this->custom_id] = $table.$this->custom_id;
93
					$this->select_tables[$table." as $table".$this->custom_id] = 1;
94
95
					$this->used_custom_fields[$field] = true;
96
					$result = " $table".$this->custom_id.".AR_name = '$field' ";
97
					if ($nls) {
98
						$result = " $result and $table".$this->custom_id.".AR_nls = '$nls' ";
99
					}
100
101
					if (!$this->in_orderby) {
102
						$result = " $result and $table".$this->custom_id.".AR_value ";
103
					} else {
104
						$this->where_s_ext = $result;
105
						$result = " $table".$this->custom_id.".AR_value ";
106
					}
107
				} else {
108
					$this->used_tables["$table as $table$record_id"] = $table.$record_id;
109
					//$this->select_tables[$table." as $table$record_id"] = 1;
110
111
					$result = " $table$record_id.AR_name = '$field' ";
112
					if (!$this->in_orderby && !$no_context_join) {
113
						if ($this->join_target_properties["prop_my"][":$record_id"]) {
114
							$result=" $result and $table$record_id.object = target.object and $table$record_id.AR_value ";
115
						} else {
116
							$result=" $table$record_id.object = ".$this->tbl_prefix."objects.id and $table$record_id.AR_value ";
117
						}
118
					} else {
119
						if ($this->join_target_properties[$node["table"]]) {
120
							$this->join_target_properties["$table as $table$record_id"] = $table.$record_id;
121
						}
122
						$this->select_tables["$table as $table$record_id"] = $table.$record_id;
123
						$result=" $table$record_id.AR_value ";
124
					}
125
				}
126
			break;
127
			case 'string':
128
				$result = "'".addSlashes($node["value"])."'";
129
				if ($escape_chars) {
130
					$result = preg_replace('/([^\\\])_/', '\\1\_', $result);
131
				}
132
				return $result;
133
			break;
134
			case 'float':
135
			case 'int':
136
				$result=$node["value"];
137
			break;
138 View Code Duplication
			case 'and':
139
				$cr = $this->custom_ref;
140
				$left=$this->compile_tree($node["left"]);
141
				if ($this->custom_ref > $cr) {
142
					$this->custom_id++;
143
				}
144
145
				$right=$this->compile_tree($node["right"]);
146
				$cr = $this->custom_ref;
147
				if ($this->custom_ref > $cr) {
148
					$this->custom_id++;
149
				}
150
				$result=" $left and $right ";
151
			break;
152 View Code Duplication
			case 'or':
153
				$left=$this->compile_tree($node["left"]);
154
				$right=$this->compile_tree($node["right"]);
155
				$result=" $left or $right ";
156
			break;
157
			case 'cmp':
158
				$not="";
159
				switch ($node["operator"]) {
160
					case '=':
161
					case '==':
162
						$operator="=";
163
					break;
164
					case '!=':
165
					case '<=':
166
					case '>=':
167
					case '<':
168
					case '>':
169
						$operator=$node["operator"];
170
					break;
171
					case '!~':
172
					case '!~~':
173
						$not="NOT ";
174
					case '~=':
175
					case '=~':
176
					case '=~~':
177
						$likeOp = true;
178
						$operator=$not."LIKE";
179
						/* double tildes indicate case-sensitive */
180
						if (strlen($operator)==3) {
181
							$operator.=" BINARY";
182
						}
183
					break;
184
					case '!/':
185
					case '!//':
186
						$not="NOT ";
187
					case '=/':
188
					case '=//':
189
						$operator=$not."REGEXP";
190
						/* double slashes indicate case-sensitive */
191
						if (strlen($operator)==3) {
192
							$operator.=" BINARY";
193
						}
194
					break;
195
					case '!*':
196
					case '!**':
197
						$not = " not";
198
					case '=*':
199
					case '=**':
200
						if ($node["left"]["id"]!=="implements" && $this->store->is_supported("fulltext")) {
201
							$left=$this->compile_tree($node["left"], array("no_context_join" => true));
202
							$right=$this->compile_tree($node["right"]);
203
							/* fulltext search operators: =*, !*, =**, !** */
204
							$operator = $node["operator"];
205
							$query = stripslashes(substr($right,1,-1));
206
							if (strlen($operator)==3 && $this->store->is_supported("fulltext_boolean")) {
207
								/* double asterisks indicate boolean mode */
208
								/* make sure the operators are not formatted_for_fti */
209
								$storeclass=get_class($this->store);
210
								$query = preg_replace(
211
										'%(^|\s)([-+~<>(]*)("([^"]*)"|([^ "*]*))([)*]?)%e',
212
										"'\\1\\2'.('\\4'?'\"'.$storeclass::format_for_fti('\\4').'\"':$storeclass::format_for_fti('\\5')).'\\6'",
213
										$query);
214
								$boolmode = " in boolean mode";
215
							} else {
216
								$boolmode = "";
217
								$query = $this->store->format_for_fti($query);
218
							}
219
							$result = "$not match ($left) against ('$query'$boolmode) ";
220
							$this->fulltext_expr[':'.$node["right"]["record_id"]] = $result;
221
							return $result;
222
						}
223
					break;
224
				}
225
				if ($node["left"]["id"]!=="implements") {
226
					$left=$this->compile_tree($node["left"]);
227
					if ($likeOp) {
228
						$right=$this->compile_tree($node["right"], array('escape_chars' => true));
229
					} else {
230
						$right=$this->compile_tree($node["right"]);
231
					}
232
					/* lastchanged == unixtimestamp -> lastchanged == 200201.. */
233
					if ($node["left"]["field"]=="lastchanged") {
234
						$right = date("YmdHis", $right);
235
					}
236
					$result=" $left $operator $right ";
237
				} else {
238
					$type = $this->compile_tree($node["right"]);
239
					switch ($operator) {
240 View Code Duplication
						case '!=':
241
							$result=" (".$this->tbl_prefix."objects.type not in (select type from ".$this->tbl_prefix."types where implements = $type )) ";
242
						break;
243 View Code Duplication
						default:
244
							$result=" ( SUBSTRING_INDEX(".$this->tbl_prefix."objects.vtype, '.', 1) in (select type from ".$this->tbl_prefix."types where implements $operator $type )) ";
245
						break;
246
					}
247
				}
248
			break;
249 View Code Duplication
			case 'group':
250
				$left=$this->compile_tree($node["left"]);
251
				if ($left) {
252
					$result=" ( $left ) ";
253
				}
254
			break;
255
256 View Code Duplication
			case 'orderby':
257
				$result=$this->compile_tree($node["left"]);
258
				$this->orderby_s=$this->compile_tree($node["right"]);
259
			break;
260
261
			case 'orderbyfield':
262
				$this->in_orderby = true;
263
				$left=$this->compile_tree($node["left"]);
264
				if ( $node["right"]["field"] != "none" ) {
265
					if ($node["right"]["field"] == 'AR_relevance' && $this->store->is_supported("fulltext")) {
266
						$right = $this->fulltext_expr[':'.$node["right"]["record_id"]];
267
					} else {
268
						$right=$this->compile_tree($node["right"]);
269
					}
270
					if ($left) {
271
						$result=" $left ,  $right ".$node["type"]." ";
272
					} else {
273
						$result=" $right ".$node["type"]." ";
274
					}
275
				} else {
276
					$result = "";
277
					if ($left) {
278
						$result = " $left ";
279
					}
280
					$this->skipDefaultOrderBy = true;
281
				}
282
			break;
283
284 View Code Duplication
			case 'limit':
285
				$this->where_s=$this->compile_tree($node["left"]);
286
				if ($node["limit"]) {
287
					$this->limit_s=" limit ".(int)$node["offset"].", ".$node["limit"]." ";
288
				} else if ($node["offset"]) {
289
					$this->limit_s=" limit ".(int)$node["offset"]." ";
290
				} else {
291
					if ($this->limit) {
292
						$offset = (int)$this->offset;
293
						$this->limit_s=" limit $offset, ".(int)$this->limit." ";
294
					}
295
				}
296
			break;
297
		}
298
		return $result;
299
	}
300
301
	// mysql specific compiler function
302
	protected function priv_sql_compile($tree) {
303
		$this->custom_ref = 0;
304
		$this->custom_id = 0;
305
		$this->used_tables = array();
306
		$this->compile_tree($tree);
307
308
		$prop_dep = '';
309
		$query = '';
310
		$orderby = '';
311
312
		if ( $this->error ) {
313
			return null;
314
		}
315
316
		$nodes=$this->tbl_prefix."nodes";
317
		$objects=$this->tbl_prefix."objects";
318
		$properties=$this->tbl_prefix."prop_";
319
		$this->used_tables[$nodes]=$nodes;
320
		$this->used_tables[$objects]=$objects;
321 View Code Duplication
		if ($this->join_target_properties) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->join_target_properties of type array<*,string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
322
			$this->used_tables[$properties."references as target_reference"] = $properties."references as target_reference";
323
			$this->used_tables["$nodes as target"] = "$nodes as target";
324
		}
325
326
		$tables = implode(', ',array_keys($this->used_tables));
327
		foreach ( $this->used_tables as $key => $val){
328 View Code Duplication
			if ($this->select_tables[$key]) {
329
				if ($this->join_target_properties[$key]) {
330
					$prop_dep.=" and $val.object=target.object ";
331
				} else {
332
					$prop_dep.=" and $val.object=$objects.id ";
333
				}
334
			}
335
		}
336
337
		$join = "";
338
		if (is_array($this->nls_join)) {
339
			$join = implode("", $this->nls_join);
340
		}
341
342
		$query.=" where $nodes.object=$objects.id $prop_dep";
343
		$query.=" and $nodes.path like '".str_replace('_','\\_',AddSlashes($this->path))."%' ";
344
		if ($this->where_s) {
345
			$query.=" and ( $this->where_s ) ";
346
		}
347
		if ($this->where_s_ext) {
348
			$query .= " and ($this->where_s_ext) ";
349
		}
350
		/* do target join */
351
		if ($this->join_target_properties) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->join_target_properties of type array<*,string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
352
			$query .= " and $objects.id = target_reference.object ";
353
			$query .= " and target.path = target_reference.AR_path ";
354
		}
355
356
		if ($this->orderby_s) {
357 View Code Duplication
			if ($this->skipDefaultOrderBy) {
358
				$orderby = " order by $this->orderby_s ";
359
			} else {
360
				$orderby = " order by $this->orderby_s, $nodes.parent ASC, $nodes.priority DESC, $nodes.path ASC ";
361
			}
362
		} else if (!$this->skipDefaultOrderBy) {
363
			$orderby = " order by $nodes.parent ASC, $nodes.priority DESC, $nodes.path ASC ";
364
		}
365
366
		$select_query = "select distinct($nodes.path), $objects.id, $nodes.parent, $nodes.priority, $objects.type, ".
367
		                " UNIX_TIMESTAMP($objects.lastchanged) as lastchanged, $objects.vtype ";
368
		$select_query .= "from ($tables) $join $query ";
369
370
		$select_query .= $orderby . " $this->limit_s ";
371
		$count_query = "select count(distinct($nodes.path)) as count from $tables ".$query;
372
373
		return array("select_query" => $select_query, "count_query" => $count_query);
374
	}
375
376
  }
377