|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Class DbSqlPrepare |
|
5
|
|
|
*/ |
|
6
|
|
|
class DbSqlPrepare { |
|
7
|
|
|
const COMMENT_PLACEHOLDER = ':__COMMENT__'; |
|
8
|
|
|
const PARAM_PREG = '#(\:.+?\b)#im'; |
|
9
|
|
|
|
|
10
|
|
|
public $query = ''; |
|
11
|
|
|
public $values = array(); |
|
12
|
|
|
|
|
13
|
|
|
public $comment = ''; |
|
14
|
|
|
|
|
15
|
|
|
public $queryPrepared = ''; |
|
16
|
|
|
public $paramsPrepared = array(); |
|
17
|
|
|
public $valuesPrepared = array(); |
|
18
|
|
|
public $valueTypesPrepared = ''; |
|
19
|
|
|
/** |
|
20
|
|
|
* @var mysqli_stmt $statement |
|
21
|
|
|
*/ |
|
22
|
|
|
public $statement; |
|
23
|
|
|
|
|
24
|
|
|
// /** |
|
25
|
|
|
// * @var ReflectionMethod |
|
26
|
|
|
// */ |
|
27
|
|
|
// public static $bindParamMethod; |
|
28
|
|
|
|
|
29
|
|
|
/** |
|
30
|
|
|
* @var self[] |
|
31
|
|
|
*/ |
|
32
|
|
|
protected static $statements = array(); |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* DbSqlPrepare constructor. |
|
36
|
|
|
* |
|
37
|
|
|
* @param string $statement |
|
38
|
|
|
* @param array $values |
|
39
|
|
|
*/ |
|
40
|
1 |
|
public function __construct($statement, $values = array()) { |
|
41
|
|
|
// if(empty(static::$bindParamMethod)) { |
|
|
|
|
|
|
42
|
|
|
// $ref = new ReflectionClass('mysqli_stmt'); |
|
43
|
|
|
// static::$bindParamMethod = $ref->getMethod("bind_param"); |
|
44
|
|
|
// } |
|
45
|
|
|
|
|
46
|
1 |
|
$this->query = trim($statement); |
|
47
|
1 |
|
$this->values = $values; |
|
48
|
1 |
|
} |
|
49
|
|
|
|
|
50
|
|
|
/** |
|
51
|
|
|
* @param string $statement |
|
52
|
|
|
* @param array $values |
|
53
|
|
|
* |
|
54
|
|
|
* @return static |
|
55
|
|
|
* |
|
56
|
|
|
*/ |
|
57
|
1 |
|
public static function build($statement, $values = array()) { |
|
58
|
1 |
|
return new static($statement, $values); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
|
|
62
|
|
|
public function setQuery($query) { |
|
63
|
|
|
$this->query = $query; |
|
64
|
|
|
|
|
65
|
|
|
return $this; |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
public function commentRemove() { |
|
69
|
|
|
if (!empty($this->values[static::COMMENT_PLACEHOLDER])) { |
|
70
|
|
|
unset($this->values[static::COMMENT_PLACEHOLDER]); |
|
71
|
|
|
$this->query = str_replace(' ' . static::COMMENT_PLACEHOLDER, '', $this->query); |
|
72
|
|
|
} |
|
73
|
|
|
} |
|
74
|
|
|
|
|
75
|
|
|
public function commentAdd($comment) { |
|
76
|
|
|
if (empty($this->values[static::COMMENT_PLACEHOLDER])) { |
|
77
|
|
|
$this->query .= ' ' . static::COMMENT_PLACEHOLDER; |
|
78
|
|
|
} |
|
79
|
|
|
$this->values[static::COMMENT_PLACEHOLDER] = $comment; |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* @param string $comment |
|
84
|
|
|
*/ |
|
85
|
|
|
public function comment($comment) { |
|
86
|
|
|
if (empty($comment)) { |
|
87
|
|
|
$this->commentRemove(); |
|
88
|
|
|
} else { |
|
89
|
|
|
$this->commentAdd($comment); |
|
90
|
|
|
} |
|
91
|
|
|
|
|
92
|
|
|
return $this; |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
1 |
|
public function __toString() { |
|
96
|
|
|
// $result = str_replace(array_keys($this->tables), $this->tables, $this->query); |
|
|
|
|
|
|
97
|
1 |
|
$result = str_replace(array_keys($this->values), $this->values, $this->query); |
|
98
|
|
|
|
|
99
|
1 |
|
return $result; |
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
public function compileMySqlI() { |
|
103
|
|
|
// print($this->statement); |
|
|
|
|
|
|
104
|
|
|
|
|
105
|
|
|
$this->queryPrepared = $this->query; |
|
106
|
|
|
$this->paramsPrepared = array(); |
|
107
|
|
|
$this->valuesPrepared = array(); |
|
108
|
|
|
$this->valueTypesPrepared = ''; |
|
109
|
|
|
|
|
110
|
|
|
if ($variableCount = preg_match_all(self::PARAM_PREG, $this->query, $matches, PREG_PATTERN_ORDER)) { |
|
|
|
|
|
|
111
|
|
|
$this->paramsPrepared = $matches[0]; |
|
112
|
|
|
if (in_array(static::COMMENT_PLACEHOLDER, $this->paramsPrepared)) { |
|
113
|
|
|
// Removing comment placeholder from statement |
|
114
|
|
|
$this->queryPrepared = str_replace(static::COMMENT_PLACEHOLDER, $this->comment, $this->queryPrepared); |
|
115
|
|
|
// Removing comment value from values list |
|
116
|
|
|
$this->paramsPrepared = array_filter($this->paramsPrepared, function ($value) { return $value != DbSqlPrepare::COMMENT_PLACEHOLDER; }); |
|
117
|
|
|
// TODO - Add comment value directly to statement |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
// pdump($this->valuesUsed, '$this->valuesUsed0'); |
|
|
|
|
|
|
121
|
|
|
|
|
122
|
|
|
// Using $matches array as keys for used values array |
|
|
|
|
|
|
123
|
|
|
// $this->valuesUsed = array_combine($matches, array_fill(0, count($matches), null)); |
|
124
|
|
|
|
|
125
|
|
|
// Now filling found parms with it values |
|
126
|
|
|
// We can't use param names as keys 'cause same param can be met twice |
|
127
|
|
|
// So one key would overwrite another and total number of valuesUsed will be less then actual values used |
|
128
|
|
|
foreach ($this->paramsPrepared as $key => &$value) { |
|
129
|
|
|
if (!key_exists($value, $this->values)) { |
|
130
|
|
|
// Throw exception if not key found in statement values list |
|
131
|
|
|
throw new Exception('DbSqlPrepare::compile() - values array has no match for statement params'); |
|
132
|
|
|
} |
|
133
|
|
|
// $value = array( |
|
|
|
|
|
|
134
|
|
|
// 'param' => $value, |
|
135
|
|
|
// 'value' => $this->values[$value], |
|
136
|
|
|
// ); |
|
137
|
|
|
// Reference need for call mysqli::bind_param later in execute() method |
|
138
|
|
|
$this->valuesPrepared[$key] = &$this->values[$value]; |
|
139
|
|
|
|
|
140
|
|
|
// i corresponding variable has type integer |
|
141
|
|
|
// d corresponding variable has type double |
|
142
|
|
|
// s corresponding variable has type string |
|
143
|
|
|
// b corresponding variable is a blob and will be sent in packets |
|
144
|
|
|
if (is_int($this->values[$value])) { |
|
145
|
|
|
$this->valueTypesPrepared .= 'i'; |
|
146
|
|
|
} elseif (is_double($this->values[$value])) { |
|
147
|
|
|
$this->valueTypesPrepared .= 'd'; |
|
148
|
|
|
} else { |
|
149
|
|
|
$this->valueTypesPrepared .= 's'; |
|
150
|
|
|
} |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
|
|
154
|
|
|
// foreach ($this->valuesUsed as $key => &$value) { |
|
|
|
|
|
|
155
|
|
|
// if (!key_exists($key, $this->values)) { |
|
156
|
|
|
// // Throw exception if not key found in statement values list |
|
157
|
|
|
// throw new Exception('DbSqlPrepare::compile() - values array has no match for statement params'); |
|
158
|
|
|
// } |
|
159
|
|
|
// $value = $this->values[$key]; |
|
160
|
|
|
// } |
|
161
|
|
|
// |
|
162
|
|
|
|
|
163
|
|
|
|
|
164
|
|
|
// pdump($matches, '$matches0'); |
|
|
|
|
|
|
165
|
|
|
// $this->valuesUsed = array_intersect_key($this->values, $matches); |
|
166
|
|
|
// pdump($matches, '$matches2 '); |
|
167
|
|
|
|
|
168
|
|
|
// Replacing actual param names with '?' - for mysqli_prepare |
|
169
|
|
|
$this->queryPrepared = preg_replace(self::PARAM_PREG, '?', $this->queryPrepared); |
|
170
|
|
|
|
|
171
|
|
|
// pdump($this->paramsPrepared, '$this->paramsUsed'); |
|
|
|
|
|
|
172
|
|
|
// pdump($this->valuesPrepared, '$this->valuesPrepared'); |
|
173
|
|
|
// pdump($this->valueTypesPrepared, '$valueTypes'); |
|
174
|
|
|
// |
|
175
|
|
|
// print($this->queryPrepared); |
|
176
|
|
|
// print($questioned); |
|
177
|
|
|
}; |
|
178
|
|
|
|
|
179
|
|
|
// pdie(); |
|
180
|
|
|
|
|
181
|
|
|
return $this; |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
public function getResult() { |
|
185
|
|
|
return $this->statement->get_result(); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* @param db_mysql $db |
|
190
|
|
|
* |
|
191
|
|
|
* @return DbSqlPrepare |
|
192
|
|
|
* @throws Exception |
|
193
|
|
|
*/ |
|
194
|
|
|
public function statementGet($db) { |
|
195
|
|
|
$md5 = md5($this->queryPrepared); |
|
196
|
|
|
|
|
197
|
|
|
if (empty(static::$statements[$md5])) { |
|
198
|
|
|
if (!(static::$statements[$md5] = $db->db_prepare($this->queryPrepared))) { |
|
199
|
|
|
throw new Exception('doQueryPhase1 - can not prepare statement'); |
|
200
|
|
|
} |
|
201
|
|
|
if (count($this->valuesPrepared)) { |
|
202
|
|
|
$params = array_merge(array(&$this->valueTypesPrepared), $this->valuesPrepared); |
|
203
|
|
|
// static::$bindParamMethod->invokeArgs($this->statement, $params); |
|
|
|
|
|
|
204
|
|
|
call_user_func_array(array(static::$statements[$md5], 'bind_param'), $params); |
|
205
|
|
|
} |
|
206
|
|
|
} |
|
207
|
|
|
$this->statement = static::$statements[$md5]; |
|
|
|
|
|
|
208
|
|
|
|
|
209
|
|
|
return $this; |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
/** |
|
213
|
|
|
* @return $this |
|
214
|
|
|
*/ |
|
215
|
|
|
public function execute() { |
|
216
|
|
|
//pdump($this->valuesPrepared); |
|
|
|
|
|
|
217
|
|
|
// if (count($this->valuesPrepared)) { |
|
218
|
|
|
// $params = array_merge(array(&$this->valueTypesPrepared), $this->valuesPrepared); |
|
219
|
|
|
//// static::$bindParamMethod->invokeArgs($this->statement, $params); |
|
220
|
|
|
// call_user_func_array(array($this->statement, 'bind_param'), $params); |
|
221
|
|
|
// } |
|
222
|
|
|
|
|
223
|
|
|
$this->statement->execute(); |
|
224
|
|
|
|
|
225
|
|
|
return $this; |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
} |
|
229
|
|
|
|
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.