|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace BfwSql; |
|
4
|
|
|
|
|
5
|
|
|
use \Exception; |
|
6
|
|
|
|
|
7
|
|
|
/** |
|
8
|
|
|
* Abstract class used for all query writer class. |
|
9
|
|
|
* |
|
10
|
|
|
* @package bfw-sql |
|
11
|
|
|
* @author Vermeulen Maxime <[email protected]> |
|
12
|
|
|
* @version 2.0 |
|
13
|
|
|
*/ |
|
14
|
|
|
abstract class SqlActions |
|
15
|
|
|
{ |
|
16
|
|
|
/** |
|
17
|
|
|
* @const QUOTE_ALL To automatic quote all string values. |
|
18
|
|
|
* Used by SqlInsert and SqlUpdate. |
|
19
|
|
|
*/ |
|
20
|
|
|
const QUOTE_ALL = 'all'; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* @const QUOTE_ALL To not automatic quote string values. |
|
24
|
|
|
* Used by SqlInsert and SqlUpdate. |
|
25
|
|
|
*/ |
|
26
|
|
|
const QUOTE_NONE = 'none'; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* @const QUOTE_ALL To automatic quote string values only for somes columns |
|
30
|
|
|
* Used by SqlInsert and SqlUpdate. |
|
31
|
|
|
*/ |
|
32
|
|
|
const QUOTE_PARTIALLY = 'partially'; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* @var \BfwSql\SqlConnect $sqlConnect SqlConnect object |
|
36
|
|
|
*/ |
|
37
|
|
|
protected $sqlConnect; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* @var string $assembledRequest The request will be executed |
|
41
|
|
|
*/ |
|
42
|
|
|
protected $assembledRequest = ''; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @var boolean $isPreparedRequest If is a prepared request |
|
46
|
|
|
*/ |
|
47
|
|
|
protected $isPreparedRequest = true; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* @var string $tableName The main table name for request |
|
51
|
|
|
*/ |
|
52
|
|
|
protected $tableName = ''; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* @var array $columns List of impacted columns by the request |
|
56
|
|
|
*/ |
|
57
|
|
|
protected $columns = array(); |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* @var string $quoteStatus The current automic quote status. |
|
61
|
|
|
*/ |
|
62
|
|
|
protected $quoteStatus = self::QUOTE_ALL; |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* @var array $quotedColumns List of columns where value will be quoted if |
|
66
|
|
|
* is string. |
|
67
|
|
|
*/ |
|
68
|
|
|
protected $quotedColumns = []; |
|
69
|
|
|
|
|
70
|
|
|
/** |
|
71
|
|
|
* @var array $notQuotedColumns List of columns where value will not be |
|
72
|
|
|
* quoted if is string. |
|
73
|
|
|
*/ |
|
74
|
|
|
protected $notQuotedColumns = []; |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* @var string[] $where All filter use in where part of the request |
|
78
|
|
|
*/ |
|
79
|
|
|
protected $where = array(); |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* @var string[] $preparedRequestArgs Arguments used by prepared request |
|
83
|
|
|
*/ |
|
84
|
|
|
protected $preparedRequestArgs = array(); |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* @var array $prepareDriversOptions SGBD driver option used for |
|
88
|
|
|
* prepared request |
|
89
|
|
|
* |
|
90
|
|
|
* @link http://php.net/manual/en/pdo.prepare.php |
|
91
|
|
|
*/ |
|
92
|
|
|
protected $prepareDriversOptions = array(); |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* @var boolean $noResult If request has sent no result. |
|
96
|
|
|
*/ |
|
97
|
|
|
protected $noResult = false; |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* @var \PDOStatement $lastRequestStatement The PDOStatement pour the |
|
101
|
|
|
* last request executed. |
|
102
|
|
|
*/ |
|
103
|
|
|
protected $lastRequestStatement; |
|
104
|
|
|
|
|
105
|
|
|
/** |
|
106
|
|
|
* Constructor |
|
107
|
|
|
* |
|
108
|
|
|
* @param \BfwSql\SqlConnect $sqlConnect Instance of SGBD connexion |
|
109
|
|
|
*/ |
|
110
|
|
|
public function __construct(\BfwSql\SqlConnect $sqlConnect) |
|
111
|
|
|
{ |
|
112
|
|
|
$this->sqlConnect = $sqlConnect; |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
/** |
|
116
|
|
|
* Getter to access at sqlConnect property |
|
117
|
|
|
* |
|
118
|
|
|
* @return \BfwSql\SqlConnect |
|
119
|
|
|
*/ |
|
120
|
|
|
public function getSqlConnect() |
|
121
|
|
|
{ |
|
122
|
|
|
return $this->sqlConnect; |
|
123
|
|
|
} |
|
124
|
|
|
|
|
125
|
|
|
/** |
|
126
|
|
|
* Setter to enable or disable prepared request |
|
127
|
|
|
* |
|
128
|
|
|
* @param boolean $preparedRequestStatus The new status for prepared request |
|
129
|
|
|
* |
|
130
|
|
|
* @return \BfwSql\SqlActions |
|
131
|
|
|
*/ |
|
132
|
|
|
public function setIsPreparedRequest($preparedRequestStatus) |
|
133
|
|
|
{ |
|
134
|
|
|
$this->isPreparedRequest = (bool) $preparedRequestStatus; |
|
135
|
|
|
|
|
136
|
|
|
return $this; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* Getter to access at preparedRequestArgs property |
|
141
|
|
|
* |
|
142
|
|
|
* @return array |
|
143
|
|
|
*/ |
|
144
|
|
|
public function getPreparedRequestArgs() |
|
145
|
|
|
{ |
|
146
|
|
|
return $this->preparedRequestArgs; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Getter to access at prepareDriversOptions property |
|
151
|
|
|
* |
|
152
|
|
|
* @return array |
|
153
|
|
|
*/ |
|
154
|
|
|
public function getPrepareDriversOptions() |
|
155
|
|
|
{ |
|
156
|
|
|
return $this->prepareDriversOptions; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* Define driver options to prepared request |
|
161
|
|
|
* |
|
162
|
|
|
* @link http://php.net/manual/fr/pdo.prepare.php |
|
163
|
|
|
* |
|
164
|
|
|
* @param array $driverOptions Drivers options |
|
165
|
|
|
* |
|
166
|
|
|
* @return \BfwSql\SqlActions |
|
167
|
|
|
*/ |
|
168
|
|
|
public function setPrepareDriversOptions($driverOptions) |
|
169
|
|
|
{ |
|
170
|
|
|
$this->prepareDriversOptions = $driverOptions; |
|
171
|
|
|
|
|
172
|
|
|
return $this; |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
/** |
|
176
|
|
|
* Check if a request is assemble or not. |
|
177
|
|
|
* If not, run the method assembleRequest. |
|
178
|
|
|
* |
|
179
|
|
|
* @return boolean |
|
180
|
|
|
*/ |
|
181
|
|
|
public function isAssembled() |
|
182
|
|
|
{ |
|
183
|
|
|
if ($this->assembledRequest === '') { |
|
184
|
|
|
return false; |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
return true; |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
/** |
|
191
|
|
|
* Write the query |
|
192
|
|
|
* |
|
193
|
|
|
* @return void |
|
194
|
|
|
*/ |
|
195
|
|
|
protected abstract function assembleRequest(); |
|
196
|
|
|
|
|
197
|
|
|
/** |
|
198
|
|
|
* Return the assembled request |
|
199
|
|
|
* |
|
200
|
|
|
* @param boolean $force : Force to re-assemble request |
|
201
|
|
|
* |
|
202
|
|
|
* @return string |
|
203
|
|
|
*/ |
|
204
|
|
|
public function assemble($force = false) |
|
205
|
|
|
{ |
|
206
|
|
|
if ($this->isAssembled() === false || $force === true) { |
|
207
|
|
|
$this->assembleRequest(); |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
return $this->assembledRequest; |
|
211
|
|
|
} |
|
212
|
|
|
|
|
213
|
|
|
/** |
|
214
|
|
|
* Execute the assembled request |
|
215
|
|
|
* |
|
216
|
|
|
* @return array The pdo errorInfo array |
|
217
|
|
|
*/ |
|
218
|
|
|
protected function executeQuery() |
|
219
|
|
|
{ |
|
220
|
|
|
$pdo = $this->sqlConnect->getPDO(); |
|
221
|
|
|
$this->sqlConnect->upNbQuery(); |
|
222
|
|
|
$this->assemble(); |
|
223
|
|
|
|
|
224
|
|
|
if ($this->isPreparedRequest) { |
|
225
|
|
|
$req = $pdo->prepare( |
|
226
|
|
|
$this->assembledRequest, |
|
227
|
|
|
$this->prepareDriversOptions |
|
228
|
|
|
); |
|
229
|
|
|
|
|
230
|
|
|
$req->execute($this->preparedRequestArgs); |
|
231
|
|
|
$error = $req->errorInfo(); |
|
232
|
|
|
} else { |
|
233
|
|
|
$pdoMethodToCall = 'exec'; |
|
234
|
|
|
if ($this instanceof \BfwSql\SqlSelect) { |
|
235
|
|
|
$pdoMethodToCall = 'query'; |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
|
|
$req = $pdo->{$pdoMethodToCall}($this->assembledRequest); |
|
239
|
|
|
$error = $pdo->errorInfo(); |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
$this->lastRequestStatement = $req; |
|
243
|
|
|
|
|
244
|
|
|
return $error; |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
/** |
|
248
|
|
|
* Execute the assembled request and check if there are errors |
|
249
|
|
|
* Update property noResult |
|
250
|
|
|
* |
|
251
|
|
|
* @throws \Exception If the request fail |
|
252
|
|
|
* |
|
253
|
|
|
* @return \PDOStatement|integer |
|
254
|
|
|
*/ |
|
255
|
|
|
public function execute() |
|
256
|
|
|
{ |
|
257
|
|
|
$error = $this->executeQuery(); |
|
258
|
|
|
|
|
259
|
|
|
//Throw an exception if they are an error with the request |
|
260
|
|
|
if ($error[0] !== null && $error[0] !== '00000') { |
|
261
|
|
|
throw new Exception($error[2]); |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
if ($this->lastRequestStatement === false) { |
|
265
|
|
|
throw new Exception( |
|
266
|
|
|
'An error occurred during the execution of the request' |
|
267
|
|
|
); |
|
268
|
|
|
} |
|
269
|
|
|
|
|
270
|
|
|
$this->noResult = false; |
|
271
|
|
|
if ($this->obtainImpactedRows() === 0) { |
|
272
|
|
|
$this->noResult = true; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
return $this->lastRequestStatement; |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
|
|
/** |
|
279
|
|
|
* Closes the cursor, enabling the statement to be executed again. |
|
280
|
|
|
* |
|
281
|
|
|
* @link http://php.net/manual/fr/pdostatement.closecursor.php |
|
282
|
|
|
* |
|
283
|
|
|
* @return void |
|
284
|
|
|
*/ |
|
285
|
|
|
public function closeCursor() |
|
286
|
|
|
{ |
|
287
|
|
|
return $this->lastRequestStatement->closeCursor(); |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* Return the number of impacted rows by the last request |
|
292
|
|
|
* |
|
293
|
|
|
* @return int|bool |
|
294
|
|
|
*/ |
|
295
|
|
|
public function obtainImpactedRows() |
|
296
|
|
|
{ |
|
297
|
|
|
if (is_object($this->lastRequestStatement)) { |
|
298
|
|
|
//If pdo::query or pdo::prepare |
|
299
|
|
|
return $this->lastRequestStatement->rowCount(); |
|
300
|
|
|
} elseif (is_integer($this->lastRequestStatement)) { |
|
301
|
|
|
//If pdo::exec |
|
302
|
|
|
return $this->lastRequestStatement; |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
//Security if call without executed a request |
|
306
|
|
|
return false; |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
/** |
|
310
|
|
|
* To call this own request without use query writer |
|
311
|
|
|
* |
|
312
|
|
|
* @param string $request The user request |
|
313
|
|
|
* |
|
314
|
|
|
* @return void |
|
315
|
|
|
*/ |
|
316
|
|
|
public function query($request) |
|
317
|
|
|
{ |
|
318
|
|
|
$this->assembledRequest = $request; |
|
319
|
|
|
} |
|
320
|
|
|
|
|
321
|
|
|
/** |
|
322
|
|
|
* Add a filter to where part of the request |
|
323
|
|
|
* |
|
324
|
|
|
* @param string $filter The filter to add |
|
325
|
|
|
* @param array|null $preparedFilters (default: null) Filters to add |
|
326
|
|
|
* in prepared request |
|
327
|
|
|
* |
|
328
|
|
|
* @throws \Exception If key on prepared request is already used |
|
329
|
|
|
* |
|
330
|
|
|
* @return \BfwSql\SqlActions |
|
331
|
|
|
*/ |
|
332
|
|
|
public function where($filter, $preparedFilters = null) |
|
333
|
|
|
{ |
|
334
|
|
|
$this->where[] = $filter; |
|
335
|
|
|
|
|
336
|
|
|
if (is_array($preparedFilters)) { |
|
337
|
|
|
$this->addPreparedFilters($preparedFilters); |
|
338
|
|
|
} |
|
339
|
|
|
|
|
340
|
|
|
return $this; |
|
341
|
|
|
} |
|
342
|
|
|
|
|
343
|
|
|
/** |
|
344
|
|
|
* Add filters to prepared requests |
|
345
|
|
|
* |
|
346
|
|
|
* @param array $preparedFilters Filters to add in prepared request |
|
347
|
|
|
* |
|
348
|
|
|
* @return void |
|
349
|
|
|
*/ |
|
350
|
|
|
protected function addPreparedFilters($preparedFilters) |
|
351
|
|
|
{ |
|
352
|
|
|
foreach ($preparedFilters as $prepareKey => $prepareValue) { |
|
353
|
|
|
$this->preparedRequestArgs[$prepareKey] = $prepareValue; |
|
354
|
|
|
} |
|
355
|
|
|
} |
|
356
|
|
|
|
|
357
|
|
|
/** |
|
358
|
|
|
* Write the where part of a sql query and return it |
|
359
|
|
|
* |
|
360
|
|
|
* @return string |
|
361
|
|
|
*/ |
|
362
|
|
|
protected function generateWhere() |
|
363
|
|
|
{ |
|
364
|
|
|
$where = ''; |
|
365
|
|
|
|
|
366
|
|
|
//check if there are filters to write |
|
367
|
|
|
if (count($this->where) > 0) { |
|
368
|
|
|
$where = ' WHERE '; |
|
369
|
|
|
|
|
370
|
|
|
foreach ($this->where as $filter) { |
|
371
|
|
|
|
|
372
|
|
|
if ($where != ' WHERE ') { |
|
373
|
|
|
$where .= ' AND '; |
|
374
|
|
|
} |
|
375
|
|
|
|
|
376
|
|
|
$where .= $filter; |
|
377
|
|
|
} |
|
378
|
|
|
} |
|
379
|
|
|
|
|
380
|
|
|
return $where; |
|
381
|
|
|
} |
|
382
|
|
|
|
|
383
|
|
|
/** |
|
384
|
|
|
* Add datas to insert or update for a column. |
|
385
|
|
|
* Used by UPDATE and INSERT requests |
|
386
|
|
|
* |
|
387
|
|
|
* @param array $columns Datas to add or update |
|
388
|
|
|
* Format : array('sqlColumnName' => 'valueForThisColumn', ...); |
|
389
|
|
|
* |
|
390
|
|
|
* @return \BfwSql\SqlActions |
|
391
|
|
|
*/ |
|
392
|
|
|
public function addDatasForColumns(array $columns) |
|
393
|
|
|
{ |
|
394
|
|
|
foreach ($columns as $columnName => $data) { |
|
395
|
|
|
if ( |
|
396
|
|
|
isset($this->columns[$columnName]) |
|
397
|
|
|
&& $this->columns[$columnName] != $data |
|
398
|
|
|
) { |
|
399
|
|
|
throw new \Exception( |
|
400
|
|
|
'A different data is already declared for the column ' |
|
401
|
|
|
.$columnName |
|
402
|
|
|
); |
|
403
|
|
|
} |
|
404
|
|
|
|
|
405
|
|
|
$this->columns[$columnName] = $data; |
|
406
|
|
|
} |
|
407
|
|
|
|
|
408
|
|
|
return $this; |
|
409
|
|
|
} |
|
410
|
|
|
|
|
411
|
|
|
/** |
|
412
|
|
|
* Declare columns should be automatic quoted if value is string. |
|
413
|
|
|
* |
|
414
|
|
|
* @param string ...$columns Columns name |
|
415
|
|
|
* |
|
416
|
|
|
* @throws Exception If the column is already declared to be not quoted |
|
417
|
|
|
* |
|
418
|
|
|
* @return \BfwSql\SqlActions |
|
419
|
|
|
*/ |
|
420
|
|
View Code Duplication |
public function addQuotedColumns(...$columns) |
|
|
|
|
|
|
421
|
|
|
{ |
|
422
|
|
|
if ($this instanceof SqlSelect || $this instanceof SqlDelete) { |
|
423
|
|
|
throw new Exception( |
|
424
|
|
|
'Sorry, automatic quoted value is not supported into ' |
|
425
|
|
|
.get_called_class().' class' |
|
426
|
|
|
); |
|
427
|
|
|
} |
|
428
|
|
|
|
|
429
|
|
|
foreach ($columns as $columnName) { |
|
430
|
|
|
if (isset($this->notQuotedColumns[$columnName])) { |
|
431
|
|
|
throw new Exception( |
|
432
|
|
|
'The column '.$columnName.' is already declared to be a' |
|
433
|
|
|
.' not quoted value.' |
|
434
|
|
|
); |
|
435
|
|
|
} |
|
436
|
|
|
|
|
437
|
|
|
$this->quotedColumns[$columnName] = true; |
|
438
|
|
|
} |
|
439
|
|
|
|
|
440
|
|
|
return $this; |
|
441
|
|
|
} |
|
442
|
|
|
|
|
443
|
|
|
/** |
|
444
|
|
|
* Declare columns should not be automatic quoted if value is string. |
|
445
|
|
|
* |
|
446
|
|
|
* @param string ...$columns Columns name |
|
447
|
|
|
* |
|
448
|
|
|
* @throws Exception If the column is already declared to be quoted |
|
449
|
|
|
* |
|
450
|
|
|
* @return \BfwSql\SqlActions |
|
451
|
|
|
*/ |
|
452
|
|
View Code Duplication |
public function addNotQuotedColumns(...$columns) |
|
|
|
|
|
|
453
|
|
|
{ |
|
454
|
|
|
if ($this instanceof SqlSelect || $this instanceof SqlDelete) { |
|
455
|
|
|
throw new Exception( |
|
456
|
|
|
'Sorry, automatic quoted value is not supported into ' |
|
457
|
|
|
.get_called_class().' class' |
|
458
|
|
|
); |
|
459
|
|
|
} |
|
460
|
|
|
|
|
461
|
|
|
foreach ($columns as $columnName) { |
|
462
|
|
|
if (isset($this->quotedColumns[$columnName])) { |
|
463
|
|
|
throw new Exception( |
|
464
|
|
|
'The column '.$columnName.' is already declared to be a' |
|
465
|
|
|
.' quoted value.' |
|
466
|
|
|
); |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
|
|
$this->notQuotedColumns[$columnName] = true; |
|
470
|
|
|
} |
|
471
|
|
|
|
|
472
|
|
|
return $this; |
|
473
|
|
|
} |
|
474
|
|
|
|
|
475
|
|
|
/** |
|
476
|
|
|
* Quote a value if need, else return the value passed in parameter |
|
477
|
|
|
* |
|
478
|
|
|
* @param string $columnName The column corresponding to the value |
|
479
|
|
|
* @param string $value The value to quote |
|
480
|
|
|
* |
|
481
|
|
|
* @return string |
|
482
|
|
|
*/ |
|
483
|
|
|
protected function quoteValue($columnName, $value) |
|
484
|
|
|
{ |
|
485
|
|
|
if ($this->quoteStatus === self::QUOTE_NONE) { |
|
486
|
|
|
return $value; |
|
487
|
|
|
} |
|
488
|
|
|
|
|
489
|
|
|
/** |
|
490
|
|
|
* If the status allow to quote partially, check if the column is |
|
491
|
|
|
* not declared to be quoted or is declared to be not quote. |
|
492
|
|
|
*/ |
|
493
|
|
|
if ( |
|
494
|
|
|
( |
|
495
|
|
|
!isset($this->quotedColumns[$columnName]) |
|
496
|
|
|
|| isset($this->notQuotedColumns[$columnName]) |
|
497
|
|
|
) |
|
498
|
|
|
&& $this->quoteStatus === self::QUOTE_PARTIALLY |
|
499
|
|
|
) { |
|
500
|
|
|
return $value; |
|
501
|
|
|
} |
|
502
|
|
|
|
|
503
|
|
|
if (!is_string($value)) { |
|
504
|
|
|
return $value; |
|
505
|
|
|
} |
|
506
|
|
|
|
|
507
|
|
|
return '"'.$value.'"'; |
|
508
|
|
|
} |
|
509
|
|
|
|
|
510
|
|
|
/** |
|
511
|
|
|
* Send a notify to application observers |
|
512
|
|
|
* |
|
513
|
|
|
* @return void |
|
514
|
|
|
*/ |
|
515
|
|
|
protected function callObserver() |
|
516
|
|
|
{ |
|
517
|
|
|
$app = \BFW\Application::getInstance(); |
|
518
|
|
|
$app->setContext($this); |
|
519
|
|
|
$app->notifyAction('BfwSqlRequest'); |
|
520
|
|
|
} |
|
521
|
|
|
} |
|
522
|
|
|
|
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.