1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Dazzle\MySQL\Protocol; |
4
|
|
|
|
5
|
|
|
use Dazzle\Throwable\Exception\LogicException; |
6
|
|
|
|
7
|
|
|
class Query implements QueryInterface |
8
|
|
|
{ |
9
|
|
|
/** |
10
|
|
|
* @var string |
11
|
|
|
*/ |
12
|
|
|
protected $sql; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* @var string |
16
|
|
|
*/ |
17
|
|
|
protected $sqlPrepared; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var mixed[] |
21
|
|
|
*/ |
22
|
|
|
protected $params = []; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var string[] |
26
|
|
|
*/ |
27
|
|
|
private $escapeChars = [ |
28
|
|
|
"\x00" => "\\0", |
29
|
|
|
"\r" => "\\r", |
30
|
|
|
"\n" => "\\n", |
31
|
|
|
"\t" => "\\t", |
32
|
|
|
"'" => "\'", |
33
|
|
|
'"' => '\"', |
34
|
|
|
"\\" => "\\\\", |
35
|
|
|
]; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @param string $sql |
39
|
|
|
* @param mixed[] $sqlParams |
40
|
|
|
*/ |
41
|
|
|
public function __construct($sql, $sqlParams = []) |
42
|
|
|
{ |
43
|
|
|
$this->sql = $sql; |
44
|
|
|
$this->sqlPrepared = null; |
45
|
|
|
$this->params = $sqlParams; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @override |
50
|
|
|
* @inheritDoc |
51
|
|
|
*/ |
52
|
|
|
public function bindParams(...$args) |
53
|
|
|
{ |
54
|
|
|
$this->sqlPrepared = null; |
55
|
|
|
$this->params = $args; |
56
|
|
|
|
57
|
|
|
return $this; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @override |
62
|
|
|
* @inheritDoc |
63
|
|
|
*/ |
64
|
|
|
public function bindParamsFromArray(array $params = []) |
65
|
|
|
{ |
66
|
|
|
$this->sqlPrepared = null; |
67
|
|
|
$this->params = $params; |
68
|
|
|
|
69
|
|
|
return $this; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @override |
74
|
|
|
* @inheritDoc |
75
|
|
|
*/ |
76
|
|
|
public function getSQL() |
77
|
|
|
{ |
78
|
|
|
return $this->sqlPrepared === null ? $this->buildSQL() : $this->sqlPrepared; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Escape string. |
83
|
|
|
* |
84
|
|
|
* @param string $str |
85
|
|
|
* @return string |
86
|
|
|
*/ |
87
|
|
|
protected function escape($str) |
88
|
|
|
{ |
89
|
|
|
return strtr($str, $this->escapeChars); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Resolve value for SQL. |
94
|
|
|
* |
95
|
|
|
* @param mixed $value |
96
|
|
|
* @return string |
97
|
|
|
*/ |
98
|
|
|
protected function resolveValueForSql($value) |
99
|
|
|
{ |
100
|
|
|
$type = gettype($value); |
101
|
|
|
|
102
|
|
|
switch ($type) |
103
|
|
|
{ |
104
|
|
|
case 'boolean': |
105
|
|
|
$value = (int) $value; |
106
|
|
|
break; |
107
|
|
|
case 'double': |
108
|
|
|
case 'integer': |
109
|
|
|
break; |
110
|
|
|
case 'string': |
111
|
|
|
$value = "'" . $this->escape($value) . "'"; |
112
|
|
|
break; |
113
|
|
|
case 'array': |
114
|
|
|
$nvalue = []; |
115
|
|
|
foreach ($value as $v) { |
116
|
|
|
$nvalue[] = $this->resolveValueForSql($v); |
117
|
|
|
} |
118
|
|
|
$value = implode(',', $nvalue); |
119
|
|
|
break; |
120
|
|
|
case 'null': |
121
|
|
|
$value = 'null'; |
122
|
|
|
break; |
123
|
|
|
default: |
124
|
|
|
throw new \InvalidArgumentException(sprintf('Not supportted value type of %s.', $type)); |
125
|
|
|
break; |
|
|
|
|
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
return $value; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Build sql query replacing and escaping characters. |
133
|
|
|
* |
134
|
|
|
* @return string |
135
|
|
|
* @throws LogicException |
136
|
|
|
*/ |
137
|
|
|
protected function buildSQL() |
138
|
|
|
{ |
139
|
|
|
$sql = $this->sql; |
140
|
|
|
$offset = strpos($sql, '?'); |
141
|
|
|
|
142
|
|
|
foreach ($this->params as $param) |
143
|
|
|
{ |
144
|
|
|
$replacement = $this->resolveValueForSql($param); |
145
|
|
|
$sql = substr_replace($sql, $replacement, $offset, 1); |
146
|
|
|
$offset = strpos($sql, '?', $offset + strlen($replacement)); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
if ($offset !== false) |
150
|
|
|
{ |
151
|
|
|
throw new LogicException('Params not enough to build valid SQL!'); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
return $sql; |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.