Passed
Push — master ( 252c51...fbb2a0 )
by dima
06:40
created

CodeigniterQueryBuilder::escape()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 0
cts 9
cp 0
rs 8.8571
cc 5
eloc 8
nc 5
nop 1
crap 30
1
<?php
2
3
namespace SimpleORM\Adapter;
4
5
/**
6
 * Description of QueryBuilder
7
 *
8
 * @author Dmitriy
9
 */
10
class CodeigniterQueryBuilder implements \SimpleORM\QueryBuilderInterface
11
{
12
	
13
	protected $adapter;
14
	
15
	protected $database;
16
	
17
	protected $TableName;
18
	
19
	protected $bind_marker = '?';
20
			
21
	function __construct(\CI_DB_mysqli_driver $db)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
22
	{
23
		
24
		$this->adapter = $db;
25
		
26
	}
27
28
	/**
29
	 * "Smart" Escape String
30
	 *
31
	 * Escapes data based on type
32
	 * Sets boolean and null types
33
	 *
34
	 * @access	public
35
	 * @param	string
36
	 * @return	mixed
37
	 */
38
	protected function escape($str)
39
	{
40
		if (is_string($str)) {
41
			$str = "'" . $this->escape_str($str) . "'";
42
		} elseif (is_bool($str)) {
43
			$str = ($str === FALSE) ? 0 : 1;
44
		} elseif (is_null($str)) {
45
			$str = 'NULL';
46
		}
47
48
		return $str;
49
	}
50
	
51
	public function update($table,array $data,$where = []){
52
		return $this->adapter->update($table,$data,$where);
53
	}
54
	
55
	public function insert($table,array $data)
56
	{
57
		return $this->adapter->insert($table,$data);
58
	}
59
	
60
	public function insert_id()
61
	{
62
		return $this->adapter->insert_id();
63
	}
64
65
	public function delete($table,$where = []){
66
		return $this->adapter->delete($table,$where);
67
	}
68
69
	protected function escape_str($str, $like = FALSE)
70
	{
71
72
		if(!$like){
73
			return $this->adapter->escape_str($str);
74
		}
75
		else{
76
			return $this->adapter->escape_like_str($str);
77
		}	
78
		
79
		
80
//		if (is_array($str)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% 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...
81
//			foreach ($str as $key => $val) {
82
//				$str[$key] = $this->escape_str($val, $like);
83
//			}
84
//
85
//			return $str;
86
//		}
87
//
88
//		if (function_exists('mysql_real_escape_string') AND is_resource($this->adapter->dbserver->rsLink)) {
89
//			$str = mysql_real_escape_string($str, $this->adapter->dbserver->rsLink);
90
//		} elseif (function_exists('mysql_escape_string')) {
91
//			$str = mysql_escape_string($str);
92
//		} else {
93
//			$str = addslashes($str);
94
//		}
95
//
96
//		// escape LIKE condition wildcards
97
//		if ($like === TRUE) {
98
//			$str = str_replace(array('%', '_'), array('\\%', '\\_'), $str);
99
//		}
100
//
101
//		return $str;
102
	}
103
104
	
105
	/**
106
	 * Проверка корректности поля
107
	 */
108
	protected function fieldCheck($field)
109
	{
110
		if (empty($field)) {
111
			throw new HttpException('You cannot have an empty field name.');
112
		}
113
114
		if (strpos($field, '.') === false) {
115
			return $this->TableName . '.' . $field;
116
		}
117
118
		return $field;
119
	}	
120
	
121
	/**
122
	 * 
123
	 * @param type $param
0 ignored issues
show
Bug introduced by
There is no parameter named $param. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
124
	 */
125
	public function getResultQuery($table,\SimpleORM\ISpecificationCriteria $Criteria ) {
126
		
127
		$this->setTable($table);
128
		
129
		$res = $this->buildQuery(
130
				$Criteria->getWhere(), 
131
				$Criteria->getLimit(), 
132
				$Criteria->getOfset(), 
133
				$Criteria->getJoins(), 
134
				$Criteria->getOrder(), 
135
				$Criteria->getManualJoins(), 
136
				$Criteria->getGroup(), 
137
				$Criteria->getManualWheres(), 
138
				$Criteria->getWhereType()
139
				);
140
141
		return $res;		
142
	}
143
144
	protected function setTable($table){
145
		if(preg_match('~(.*?)\.(.*?)$~is',$table,$m)){
146
			$this->database = $m[1];
147
			$this->TableName = $m[2];
148
		}
149
		else{
150
			$this->TableName = $table;
151
		}		
152
	}	
153
	
154
	
155
	/**
156
	 * Создает селект не только для основной таблицы но и для приджойненых таблиц
157
	 * @param type $joins
158
	 * @return type
159
	 */
160
	protected function createSelect($joins)
161
	{
162
		$s = "`" . $this->TableName . '`.*';
163
		foreach ($joins as $table => $join) {
164
			$table = isset($join['alias']) ? "`{$join['alias']}`": $table;
165
			$s .= ", $table.*";
166
		}
167
		return $s;
168
	}	
169
170
	
171
	/**
172
	 * Получение записей по условию
173
	 * @param type $where
174
	 * @param type $limit
175
	 * @param type $offset
176
	 * @param type $joins
177
	 * @param type $order
178
	 * @param type $manualJoins
179
	 * @param type $group
180
	 * @param type $manualWheres
181
	 * @param type $whereType
182
	 * @return boolean
183
	 * @throws \PDOException
184
	 */
185
	protected function buildQuery($where = array(), $limit = 25, $offset = 0, $joins = array(), $order = array(), $manualJoins = array(), $group = null, $manualWheres = array(), $whereType = 'AND')
186
	{
187
		$table = !empty($this->database)? "`{$this->database}`.".$this->TableName : $this->TableName;
188
		$query = 'SELECT ' . $this->createSelect($joins) . " FROM `".$table."`";
0 ignored issues
show
Bug introduced by
It seems like $joins defined by parameter $joins on line 185 can also be of type array; however, SimpleORM\Adapter\Codeig...Builder::createSelect() does only seem to accept object<SimpleORM\Adapter\type>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
189
		//$countQuery = "SELECT COUNT(*) AS cnt FROM `{$this->database}`.".$this->getTableName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
190
191
		$wheres = array();
192
		$params = array();
193
		foreach ($where as $key => $value) {
194
			$key = $this->fieldCheck($key);
195
196
			if (!is_array($value)) {
197
				$params[] = $value;
198
				$wheres[] = $key . ' = ?';
199
			} else {
200
				if (isset($value['operator'])) {
201
					if (is_array($value['value'])) {
202
						if ($value['operator'] == 'between') {
203
							$params[] = $value['value'][0];
204
							$params[] = $value['value'][1];
205
							$wheres[] = $key . ' BETWEEN ? AND ?';
206
						} elseif ($value['operator'] == 'IN') {
207
							$in = array();
208
209
							foreach ($value['value'] as $item) {
210
								$params[] = $item;
211
								$in[] = '?';
212
							}
213
214
							$wheres[] = $key . ' IN (' . implode(', ', $in) . ') ';
215
						} else {
216
							$ors = array();
217
							foreach ($value['value'] as $item) {
218
								if ($item == 'null') {
219 View Code Duplication
									switch ($value['operator']) {
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...
220
										case '!=':
221
											$ors[] = $key . ' IS NOT NULL';
222
											break;
223
224
										case '==':
225
										default:
226
											$ors[] = $key . ' IS NULL';
227
											break;
228
									}
229
								} else {
230
									$params[] = $item;
231
									$ors[] = $this->fieldCheck($key) . ' ' . $value['operator'] . ' ?';
232
								}
233
							}
234
							$wheres[] = '(' . implode(' OR ', $ors) . ')';
235
						}
236
					} else {
237
						if ($value['operator'] == 'like') {
238
							$params[] = '%' . $value['value'] . '%';
239
							$wheres[] = $key . ' ' . $value['operator'] . ' ?';
240
						} else {
241
							if ($value['value'] === 'null') {
242 View Code Duplication
								switch ($value['operator']) {
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...
243
									case '!=':
244
										$wheres[] = $key . ' IS NOT NULL';
245
										break;
246
247
									case '==':
248
									default:
249
										$wheres[] = $key . ' IS NULL';
250
										break;
251
								}
252
							} else {
253
								$params[] = $value['value'];
254
								$wheres[] = $key . ' ' . $value['operator'] . ' ?';
255
							}
256
						}
257
					}
258
				} else {
259
					$wheres[] = $key . ' IN (' . implode(', ', array_map(array($this, 'escape'), $value)) . ')';
260
				}
261
			}
262
		}
263
264
		if (count($joins)) {
265
			foreach ($joins as $table => $join) {
266
				$type = isset($join['type'])?$join['type']:'INNER';
267
				$query .= ' '. $type.' JOIN `' . $table . '` as `' . $join['alias'] . '` ON ' . $join['on'] . ' ';
268
				//$countQuery .= ' '.$type.' JOIN ' . $table . ' ' . $join['alias'] . ' ON ' . $join['on'] . ' ';
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
269
			}
270
		}
271
272
		if (count($manualJoins)) {
273
			foreach ($manualJoins as $join) {
274
				$query .= ' ' . $join . ' ';
275
				//$countQuery .= ' ' . $join . ' ';
276
			}
277
		}
278
279
		$hasWhere = false;
280
		if (count($wheres)) {
281
			$hasWhere = true;
282
			$query .= ' WHERE (' . implode(' ' . $whereType . ' ', $wheres) . ')';
283
			//$countQuery .= ' WHERE (' . implode(' ' . $whereType . ' ', $wheres) . ')';
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
284
		}
285
286
		if (count($manualWheres)) {
287
			foreach ($manualWheres as $where) {
288
				if (!$hasWhere) {
289
					$hasWhere = true;
290
					$query .= ' WHERE ';
291
					//$countQuery .= ' WHERE ';
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
292
				} else {
293
					$query .= ' ' . $where['type'] . ' ';
294
					//$countQuery .= ' ' . $where['type'] . ' ';
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% 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...
295
				}
296
297
				$query .= ' ' . $where['query'];
298
				//$countQuery .= ' ' . $where['query'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
299
300
				if (isset($where['params'])) {
301
					foreach ($where['params'] as $param) {
302
						$params[] = $param;
303
					}
304
				}
305
			}
306
		}
307
308
		if (!is_null($group)) {
309
			$query .= ' GROUP BY ' . $group . ' ';
310
		}
311
312
		if (count($order)) {
313
			$orders = array();
314
			if (is_string($order) && $order == 'rand') {
315
				$query .= ' ORDER BY RAND() ';
316
			} else {
317
				foreach ($order as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $order of type object<SimpleORM\Adapter\type>|array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
318
					$orders[] = $this->fieldCheck($key) . ' ' . $value;
319
				}
320
321
				$query .= ' ORDER BY ' . implode(', ', $orders);
322
			}
323
		}
324
325
		if ($limit) {
326
			$query .= ' LIMIT ' . $limit;
327
		}
328
329
		if ($offset) {
330
			$query .= ' OFFSET ' . $offset;
331
		}
332
333
334
335
336
//		try {
0 ignored issues
show
Unused Code Comprehensibility introduced by
51% 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...
337
//			$countQuery = $this->compile_binds($countQuery, $params);
338
//			$res = $this->adapter->getRow($countQuery);
339
//			$count = (int) $res['cnt'];
340
//		} catch(\PDOException $ex) {
341
//			$count = 0;
342
//		}
343
344
345
		try {
346
			//$query = $this->compile_binds($query, $params);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% 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...
347
			
348
			return $this->adapter->query($query,$params);
349
			//ed( $this->adapter->query($query) ,1);
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
350
//			if ($res = $this->adapter->getRows($query)) {
351
//				$rtn = array();
352
//
353
//				foreach ($res as $data) {
354
//					$rtn[] = $this->mapper->buildEntity($data);
355
//					unset($data);
356
//				}
357
//
358
//				return array('items' => $rtn, 'count' => $count);
359
//			} else {
360
//				return false;
361
//			}
362
		} catch(\PDOException $ex) {
363
			throw $ex;
364
		}
365
	}	
366
	
367
	
368
	/**
369
	 * применение значений в стиле PDO
370
	 * 
371
	 * @param type $sql
372
	 * @param type $binds
373
	 * @return type
374
	 */
375
	protected function compile_binds($sql, $binds)
376
	{
377
		if (strpos($sql, $this->bind_marker) === FALSE) {
378
			return $sql;
379
		}
380
381
		if (!is_array($binds)) {
382
			$binds = array($binds);
383
		}
384
385
		// Get the sql segments around the bind markers
386
		$segments = explode($this->bind_marker, $sql);
387
388
		// The count of bind should be 1 less then the count of segments
389
		// If there are more bind arguments trim it down
390
		if (count($binds) >= count($segments)) {
391
			$binds = array_slice($binds, 0, count($segments) - 1);
392
		}
393
394
		// Construct the binded query
395
		$result = $segments[0];
396
		$i = 0;
397
		foreach ($binds as $bind) {
398
			$result .= $this->escape($bind);
399
			$result .= $segments[++$i];
400
		}
401
402
		return $result;
403
	}	
404
		
405
}
406