Completed
Pull Request — master (#119)
by Robbert
08:44
created

mysql_compiler::priv_sql_compile()   F

Complexity

Conditions 13
Paths 513

Size

Total Lines 73
Code Lines 50

Duplication

Lines 16
Ratio 21.92 %

Code Coverage

Tests 45
CRAP Score 16.484

Importance

Changes 0
Metric Value
cc 13
eloc 50
c 0
b 0
f 0
nc 513
nop 1
dl 16
loc 73
ccs 45
cts 62
cp 0.7258
crap 16.484
rs 3.3629

How to fix   Long Method    Complexity   

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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 4 and the first side effect is on line 2.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 108
	public function __construct (&$store, $tbl_prefix="") {
22 108
		debug("mysql_compiler($tbl_prefix)", "store");
23 108
		$this->tbl_prefix=$tbl_prefix;
24 108
		$this->store=$store;
25 108
	}
26
27 151
	protected function compile_tree(&$node, $arguments=null) {
28 151
		if ($arguments) {
29
			extract($arguments);
30
		}
31 151
		switch ((string)$node["id"]) {
32 151
			case 'property':
33 142
				$table=$this->tbl_prefix.$node["table"];
34 142
				$field=$node["field"];
35 142
				$record_id=$node["record_id"];
36 142
				if (!$record_id) {
37 142 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 142
						$this->select_tables[$table]=$table;
52 142
						$this->used_tables[$table]=$table;
53 142
						$result=" $table.$field ";
54
					}
55 94
				} 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 142
			break;
72 151 View Code Duplication
			case 'ident':
73 5
				$table=$this->tbl_prefix.$node["table"];
74 5
				$field=$node["field"];
75 5
				$this->used_tables[$table]=$table;
76 5
				$result=" $table.$field ";
77 5
			break;
78 151 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;
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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 151
			case 'string':
128 142
				$result = "'".addSlashes($node["value"])."'";
129 142
				if ($escape_chars) {
130
					$result = preg_replace('/([^\\\])_/', '\\1\_', $result);
131
				}
132 142
				return $result;
133
			break;
134 151
			case 'float':
135 151
			case 'int':
136 3
				$result=$node["value"];
137 3
			break;
138 151 View Code Duplication
			case 'and':
139 138
				$cr = $this->custom_ref;
140 138
				$left=$this->compile_tree($node["left"]);
141 138
				if ($this->custom_ref > $cr) {
142
					$this->custom_id++;
143
				}
144
145 138
				$right=$this->compile_tree($node["right"]);
146 138
				$cr = $this->custom_ref;
147 138
				if ($this->custom_ref > $cr) {
148
					$this->custom_id++;
149
				}
150 138
				$result=" $left and $right ";
151 138
			break;
152 151 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 151
			case 'cmp':
158 145
				$not="";
159 145
				switch ($node["operator"]) {
160 145
					case '=':
161 96
					case '==':
162 145
						$operator="=";
163 145
					break;
164
					case '!=':
165
					case '<=':
166
					case '>=':
167
					case '<':
168
					case '>':
169
						$operator=$node["operator"];
170
					break;
171
					case '!~':
172
					case '!~~':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
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 '!//':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
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 '!**':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
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 96
				}
225 145
				if ($node["left"]["id"]!=="implements") {
226 145
					$left=$this->compile_tree($node["left"]);
227 145
					if ($likeOp) {
228
						$right=$this->compile_tree($node["right"], array('escape_chars' => true));
229
					} else {
230 145
						$right=$this->compile_tree($node["right"]);
231
					}
232
					/* lastchanged == unixtimestamp -> lastchanged == 200201.. */
233 145
					if ($node["left"]["field"]=="lastchanged") {
234
						$right = date("YmdHis", $right);
235
					}
236 145
					$result=" $left $operator $right ";
237 96
				} else {
238 138
					$type = $this->compile_tree($node["right"]);
239
					switch ($operator) {
240 138 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 92 View Code Duplication
						default:
244 138
							$result=" ( SUBSTRING_INDEX(".$this->tbl_prefix."objects.vtype, '.', 1) in (select type from ".$this->tbl_prefix."types where implements $operator $type )) ";
245 138
						break;
246 92
					}
247
				}
248 145
			break;
249 151 View Code Duplication
			case 'group':
250
				$left=$this->compile_tree($node["left"]);
251
				if ($left) {
252
					$result=" ( $left ) ";
253
				}
254
			break;
255
256 151 View Code Duplication
			case 'orderby':
257 40
				$result=$this->compile_tree($node["left"]);
258 40
				$this->orderby_s=$this->compile_tree($node["right"]);
259 40
			break;
260
261 151
			case 'orderbyfield':
262 40
				$this->in_orderby = true;
263 40
				$left=$this->compile_tree($node["left"]);
264 40
				if ( $node["right"]["field"] != "none" ) {
265 2
					if ($node["right"]["field"] == 'AR_relevance' && $this->store->is_supported("fulltext")) {
266
						$right = $this->fulltext_expr[':'.$node["right"]["record_id"]];
267
					} else {
268 2
						$right=$this->compile_tree($node["right"]);
269
					}
270 2
					if ($left) {
271
						$result=" $left ,  $right ".$node["type"]." ";
272
					} else {
273 2
						$result=" $right ".$node["type"]." ";
274
					}
275 1
				} else {
276 40
					$result = "";
277 40
					if ($left) {
278
						$result = " $left ";
279
					}
280 40
					$this->skipDefaultOrderBy = true;
281
				}
282 40
			break;
283
284 151 View Code Duplication
			case 'limit':
285 151
				$this->where_s=$this->compile_tree($node["left"]);
286 151
				if ($node["limit"]) {
287 151
					$this->limit_s=" limit ".(int)$node["offset"].", ".$node["limit"]." ";
288 111
				} else if ($node["offset"]) {
289
					$this->limit_s=" limit ".(int)$node["offset"]." ";
290
				} else {
291 36
					if ($this->limit) {
292
						$offset = (int)$this->offset;
293
						$this->limit_s=" limit $offset, ".(int)$this->limit." ";
294
					}
295
				}
296 151
			break;
297
		}
298 151
		return $result;
299
	}
300
301
	// mysql specific compiler function
302 104
	protected function priv_sql_compile($tree) {
303 104
		$this->custom_ref = 0;
304 104
		$this->custom_id = 0;
305 104
		$this->used_tables = array();
306 104
		$this->compile_tree($tree);
307
308 104
		$prop_dep = '';
309 104
		$query = '';
310 104
		$orderby = '';
311
312 104
		if ( $this->error ) {
313
			return null;
314
		}
315
316 104
		$nodes=$this->tbl_prefix."nodes";
317 104
		$objects=$this->tbl_prefix."objects";
318 104
		$properties=$this->tbl_prefix."prop_";
319 104
		$this->used_tables[$nodes]=$nodes;
320 104
		$this->used_tables[$objects]=$objects;
321 104 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 104
		$tables = implode(', ',array_keys($this->used_tables));
327 104
		foreach ( $this->used_tables as $key => $val){
328 104 View Code Duplication
			if ($this->select_tables[$key]) {
329 96
				if ($this->join_target_properties[$key]) {
330
					$prop_dep.=" and $val.object=target.object ";
331
				} else {
332 100
					$prop_dep.=" and $val.object=$objects.id ";
333
				}
334 48
			}
335 52
		}
336
337 104
		$join = "";
338 104
		if (is_array($this->nls_join)) {
339
			$join = implode("", $this->nls_join);
340
		}
341
342 104
		$query.=" where $nodes.object=$objects.id $prop_dep";
343 104
		$query.=" and $nodes.path like '".str_replace('_','\\_',AddSlashes($this->path))."%' ";
344 104
		if ($this->where_s) {
345 98
			$query.=" and ( $this->where_s ) ";
346 49
		}
347 104
		if ($this->where_s_ext) {
348
			$query .= " and ($this->where_s_ext) ";
349
		}
350
		/* do target join */
351 104
		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 104
		if ($this->orderby_s) {
357 2 View Code Duplication
			if ($this->skipDefaultOrderBy) {
358 2
				$orderby = " order by $this->orderby_s ";
359 1
			} else {
360 1
				$orderby = " order by $this->orderby_s, $nodes.parent ASC, $nodes.priority DESC, $nodes.path ASC ";
361
			}
362 103
		} else if (!$this->skipDefaultOrderBy) {
363 100
			$orderby = " order by $nodes.parent ASC, $nodes.priority DESC, $nodes.path ASC ";
364 50
		}
365
366 104
		$select_query = "select distinct($nodes.path), $objects.id, $nodes.parent, $nodes.priority, $objects.type, ".
367 104
		                " UNIX_TIMESTAMP($objects.lastchanged) as lastchanged, $objects.vtype ";
368 104
		$select_query .= "from ($tables) $join $query ";
369
370 104
		$select_query .= $orderby . " $this->limit_s ";
371 104
		$count_query = "select count(distinct($nodes.path)) as count from $tables ".$query;
372
373 104
		return array("select_query" => $select_query, "count_query" => $count_query);
374
	}
375
376
  }
377