1 | <?php |
||||
2 | |||||
3 | namespace BPT\database; |
||||
4 | |||||
5 | use BPT\BPT; |
||||
6 | use BPT\constants\chatMemberStatus; |
||||
7 | use BPT\constants\chatType; |
||||
8 | use BPT\constants\loggerTypes; |
||||
9 | use BPT\exception\bptException; |
||||
10 | use BPT\lock; |
||||
11 | use BPT\logger; |
||||
12 | use BPT\tools\tools; |
||||
13 | use BPT\types\callbackQuery; |
||||
14 | use BPT\types\chatMemberUpdated; |
||||
15 | use BPT\types\inlineQuery; |
||||
16 | use BPT\types\message; |
||||
17 | use Generator; |
||||
18 | use mysqli; |
||||
19 | use mysqli_result; |
||||
20 | |||||
21 | class mysql { |
||||
22 | private static mysqli $connection; |
||||
23 | |||||
24 | private static bool $auto_process = true; |
||||
25 | |||||
26 | private static string $db_name = ''; |
||||
27 | |||||
28 | private static array $default_where = []; |
||||
29 | |||||
30 | /** |
||||
31 | * If you want to use it in standalone mode , you MUST set `auto_process` to `false` |
||||
32 | */ |
||||
33 | public static function init (string $host = 'localhost', string $username = 'root', string $password = '', string $dbname = '', bool $auto_process = true, int $port = 3306, bool $auto_load = false): void { |
||||
34 | self::$auto_process = $auto_process; |
||||
35 | self::$db_name = $dbname; |
||||
36 | self::$connection = new mysqli($host, $username, $password, $dbname, $port); |
||||
37 | if (self::$connection->connect_errno) { |
||||
38 | logger::write('SQL connection has problem : ' . self::$connection->connect_error, loggerTypes::ERROR); |
||||
39 | throw new bptException('SQL_CONNECTION_PROBLEM'); |
||||
40 | } |
||||
41 | |||||
42 | if (!lock::exist('BPT-MYSQL')) { |
||||
43 | self::install($auto_load); |
||||
44 | } |
||||
45 | } |
||||
46 | |||||
47 | private static function install (bool $auto_load): void { |
||||
48 | if (self::$auto_process) { |
||||
49 | self::pureQuery(" |
||||
50 | CREATE TABLE `users` |
||||
51 | ( |
||||
52 | `id` BIGINT(20) NOT NULL, |
||||
53 | `username` VARCHAR(32) NULL DEFAULT NULL, |
||||
54 | `lang_code` VARCHAR(3) NULL DEFAULT NULL, |
||||
55 | `first_active` INT(11) NOT NULL DEFAULT '0', |
||||
56 | `last_active` INT(11) NOT NULL DEFAULT '0', |
||||
57 | `referral` BIGINT(20) NULL DEFAULT NULL, |
||||
58 | `blocked` BOOLEAN NOT NULL DEFAULT FALSE, |
||||
59 | `step` VARCHAR(64) NOT NULL DEFAULT 'main', |
||||
60 | `value` TEXT NULL DEFAULT NULL, |
||||
61 | PRIMARY KEY (`id`) |
||||
62 | ) ENGINE = InnoDB;"); |
||||
63 | self::pureQuery(' |
||||
64 | CREATE TABLE `orders` |
||||
65 | ( |
||||
66 | `id` INT NOT NULL AUTO_INCREMENT, |
||||
67 | `user_id` BIGINT(20) NOT NULL, |
||||
68 | `type` VARCHAR(30) NOT NULL, |
||||
69 | `amount` DOUBLE(13, 4) NOT NULL, |
||||
70 | `description` TEXT NOT NULL, |
||||
71 | `extra_info` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, |
||||
72 | PRIMARY KEY (`id`) |
||||
73 | ) ENGINE = InnoDB;'); |
||||
74 | } |
||||
75 | |||||
76 | if ($auto_load) { |
||||
77 | $allowed_file_names = ['db.sql', 'database.sql', 'mysql.sql', 'tables.sql']; |
||||
78 | $mysqli = self::getMysqli(); |
||||
79 | $loaded = false; |
||||
80 | foreach ($allowed_file_names as $allowed_file_name) { |
||||
81 | if (file_exists($allowed_file_name)) { |
||||
82 | $mysqli->multi_query(file_get_contents($allowed_file_name)); |
||||
83 | while ($mysqli->next_result()){if (!$mysqli->more_results()) break;} |
||||
84 | $loaded = true; |
||||
85 | } |
||||
86 | } |
||||
87 | } |
||||
88 | |||||
89 | if (self::$auto_process || (isset($loaded) && $loaded)) { |
||||
90 | lock::set('BPT-MYSQL'); |
||||
91 | } |
||||
92 | } |
||||
93 | |||||
94 | /** |
||||
95 | * @internal Only for BPT self usage , Don't use it in your source! |
||||
96 | */ |
||||
97 | public static function process (): void { |
||||
98 | if (self::$auto_process) { |
||||
99 | if (isset(BPT::$update->message)) { |
||||
100 | self::processMessage(BPT::$update->message); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
101 | } |
||||
102 | elseif (isset(BPT::$update->edited_message)) { |
||||
103 | self::processMessage(BPT::$update->edited_message); |
||||
104 | } |
||||
105 | elseif (isset(BPT::$update->callback_query)) { |
||||
106 | self::processCallbackQuery(BPT::$update->callback_query); |
||||
0 ignored issues
–
show
It seems like
BPT\BPT::update->callback_query can also be of type null ; however, parameter $update of BPT\database\mysql::processCallbackQuery() does only seem to accept BPT\types\callbackQuery , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
107 | } |
||||
108 | elseif (isset(BPT::$update->inline_query)) { |
||||
109 | self::processInlineQuery(BPT::$update->inline_query); |
||||
0 ignored issues
–
show
It seems like
BPT\BPT::update->inline_query can also be of type null ; however, parameter $update of BPT\database\mysql::processInlineQuery() does only seem to accept BPT\types\inlineQuery , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
110 | } |
||||
111 | elseif (isset(BPT::$update->my_chat_member)) { |
||||
112 | self::processMyChatMember(BPT::$update->my_chat_member); |
||||
0 ignored issues
–
show
It seems like
BPT\BPT::update->my_chat_member can also be of type null ; however, parameter $update of BPT\database\mysql::processMyChatMember() does only seem to accept BPT\types\chatMemberUpdated , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
113 | } |
||||
114 | } |
||||
115 | } |
||||
116 | |||||
117 | private static function processMessage (message $update): void { |
||||
118 | $type = $update->chat->type; |
||||
119 | if ($type === chatType::PRIVATE) { |
||||
120 | $user_id = $update->from->id; |
||||
121 | $first_active = $last_active = time(); |
||||
122 | $referral = null; |
||||
123 | $username = $update->from->username; |
||||
124 | $lang_code = $update->from->language_code; |
||||
125 | if (isset($update->command) && isset($update->command_payload) && $update->command === 'start' && str_starts_with($update->command_payload, 'ref_')) { |
||||
0 ignored issues
–
show
It seems like
$update->command_payload can also be of type null ; however, parameter $haystack of str_starts_with() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
126 | if (tools::isShorted(substr($update->command_payload, 4))) { |
||||
0 ignored issues
–
show
It seems like
$update->command_payload can also be of type null ; however, parameter $string of substr() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
127 | $referral = tools::shortDecode(substr($update->command_payload, 4)); |
||||
128 | } |
||||
129 | } |
||||
130 | self::query('INSERT INTO `users`(`id`, `username`, `lang_code`, `first_active`, `last_active`, `referral`) VALUES (?,?,?,?,?,?) on duplicate key update `last_active` = ?, `username` = ?', [ |
||||
131 | $user_id, |
||||
132 | $username, |
||||
133 | $lang_code, |
||||
134 | $first_active, |
||||
135 | $last_active, |
||||
136 | $referral, |
||||
137 | $last_active, |
||||
138 | $username |
||||
139 | ]); |
||||
140 | } |
||||
141 | } |
||||
142 | |||||
143 | private static function processCallbackQuery (callbackQuery $update): void { |
||||
144 | $type = $update->message->chat->type; |
||||
145 | if ($type === chatType::PRIVATE) { |
||||
146 | $user_id = $update->from->id; |
||||
147 | $last_active = time(); |
||||
148 | $username = $update->from->username; |
||||
149 | self::update('users', ['last_active' => $last_active, 'username' => $username], ['id' => $user_id], 1); |
||||
150 | } |
||||
151 | } |
||||
152 | |||||
153 | private static function processInlineQuery (inlineQuery $update): void { |
||||
154 | $type = $update->chat_type; |
||||
155 | if ($type === chatType::PRIVATE || $type === chatType::SENDER) { |
||||
156 | $user_id = $update->from->id; |
||||
157 | $last_active = time(); |
||||
158 | $username = $update->from->username; |
||||
159 | self::update('users', ['last_active' => $last_active, 'username' => $username], ['id' => $user_id], 1); |
||||
160 | } |
||||
161 | } |
||||
162 | |||||
163 | private static function processMyChatMember (chatMemberUpdated $update): void { |
||||
164 | $type = $update->chat->type; |
||||
165 | if ($type === chatType::PRIVATE) { |
||||
166 | if ($update->new_chat_member->status === chatMemberStatus::MEMBER) { |
||||
167 | self::update('users', ['blocked' => false], ['id' => $update->from->id], 1); |
||||
168 | } |
||||
169 | else { |
||||
170 | self::update('users', ['blocked' => true], ['id' => $update->from->id], 1); |
||||
171 | } |
||||
172 | } |
||||
173 | } |
||||
174 | |||||
175 | /** |
||||
176 | * Add default where to mysql queries, it will apply on every query automatically |
||||
177 | * |
||||
178 | * Note : where parameter in each query has more priority and will rewrite if already exist |
||||
179 | * |
||||
180 | * Note : only builder method support this(methods with where parameter like select, update and ...) |
||||
181 | * |
||||
182 | * @param array $where |
||||
183 | * |
||||
184 | * @return bool |
||||
185 | */ |
||||
186 | public static function addDefaultWhere (array $where): bool { |
||||
187 | if (empty(self::$default_where)) { |
||||
188 | self::$default_where = $where; |
||||
189 | } |
||||
190 | else { |
||||
191 | self::$default_where = array_merge(self::$default_where, $where); |
||||
192 | } |
||||
193 | return true; |
||||
194 | } |
||||
195 | |||||
196 | /** |
||||
197 | * Get real mysqli connections |
||||
198 | * |
||||
199 | * @return mysqli |
||||
200 | */ |
||||
201 | public static function getMysqli (): mysqli|false { |
||||
202 | return self::$connection ?? false; |
||||
0 ignored issues
–
show
The expression
return self::connection ?? false could also return false which is incompatible with the documented return type mysqli . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. ![]() |
|||||
203 | } |
||||
204 | |||||
205 | /** |
||||
206 | * Get affected rows |
||||
207 | * |
||||
208 | * same as affectedRows |
||||
209 | * |
||||
210 | * @return int|string |
||||
211 | */ |
||||
212 | public static function affected_rows (): int|string { |
||||
213 | return self::$connection->affected_rows; |
||||
214 | } |
||||
215 | |||||
216 | /** |
||||
217 | * Get affected rows |
||||
218 | * |
||||
219 | * same as affected_rows |
||||
220 | * |
||||
221 | * @return int|string |
||||
222 | */ |
||||
223 | public static function affectedRows (): int|string { |
||||
224 | return self::$connection->affected_rows; |
||||
225 | } |
||||
226 | |||||
227 | /** |
||||
228 | * Get inserted id |
||||
229 | * |
||||
230 | * same as insertId |
||||
231 | * |
||||
232 | * @return int|string |
||||
233 | */ |
||||
234 | public static function insert_id (): int|string { |
||||
235 | return self::$connection->insert_id; |
||||
236 | } |
||||
237 | |||||
238 | /** |
||||
239 | * Get inserted id |
||||
240 | * |
||||
241 | * same as insert_id |
||||
242 | * |
||||
243 | * @return int|string |
||||
244 | */ |
||||
245 | public static function insertId (): int|string { |
||||
246 | return self::$connection->insert_id; |
||||
247 | } |
||||
248 | |||||
249 | /** |
||||
250 | * Escape string with real_escape_string of mysqli class |
||||
251 | * |
||||
252 | * @param string $text |
||||
253 | * |
||||
254 | * @return string |
||||
255 | */ |
||||
256 | public static function escapeString (string $text): string { |
||||
257 | return self::$connection->real_escape_string($text); |
||||
258 | } |
||||
259 | |||||
260 | /** |
||||
261 | * Get last error |
||||
262 | * |
||||
263 | * @return string |
||||
264 | */ |
||||
265 | public static function error (): string { |
||||
266 | return self::$connection->error; |
||||
267 | } |
||||
268 | |||||
269 | /** |
||||
270 | * Get last error code |
||||
271 | * |
||||
272 | * @return int |
||||
273 | */ |
||||
274 | public static function errno (): int { |
||||
275 | return self::$connection->errno; |
||||
276 | } |
||||
277 | |||||
278 | /** |
||||
279 | * set database charset |
||||
280 | * |
||||
281 | * @param string $charset |
||||
282 | * |
||||
283 | * @return bool |
||||
284 | */ |
||||
285 | public static function setCharset (string $charset): bool { |
||||
286 | return self::$connection->set_charset($charset); |
||||
287 | } |
||||
288 | |||||
289 | /** |
||||
290 | * Run query as what is it |
||||
291 | * |
||||
292 | * The library doesn't do anything on it |
||||
293 | * |
||||
294 | * It's like calling mysqli->query(); |
||||
295 | * |
||||
296 | * @param string $query |
||||
297 | * |
||||
298 | * @return mysqli_result|bool |
||||
299 | */ |
||||
300 | public static function pureQuery (string $query): mysqli_result|bool { |
||||
301 | return self::$connection->query($query); |
||||
302 | } |
||||
303 | |||||
304 | /** |
||||
305 | * Run query with safe execution |
||||
306 | * |
||||
307 | * Replace inputs with `?` in query to be replaced safely with $vars in order |
||||
308 | * |
||||
309 | * it will use `pureQuery` if `$vars` be empty |
||||
310 | * |
||||
311 | * e.g. : mysql::query('select * from `users` where `id` = ? limit 1',[123456789]); |
||||
312 | * |
||||
313 | * e.g. : mysql::query('update `users` set `step` = ? where `id` = ? limit 1',['main',123456789]); |
||||
314 | * |
||||
315 | * @param string $query |
||||
316 | * @param array $vars default [] or empty |
||||
317 | * @param bool $need_result set if you want result be returned, default : true |
||||
318 | * |
||||
319 | * @return mysqli_result|bool |
||||
320 | */ |
||||
321 | public static function query (string $query, array $vars = [], bool $need_result = true): mysqli_result|bool { |
||||
322 | if (empty($vars)) { |
||||
323 | return self::pureQuery($query); |
||||
324 | } |
||||
325 | $prepare = self::$connection->prepare($query); |
||||
326 | $types = ''; |
||||
327 | foreach ($vars as $var) { |
||||
328 | if (is_int($var)) { |
||||
329 | $types .= 'i'; |
||||
330 | } |
||||
331 | elseif (is_double($var)) { |
||||
332 | $types .= 'd'; |
||||
333 | } |
||||
334 | else { |
||||
335 | $types .= 's'; |
||||
336 | } |
||||
337 | } |
||||
338 | $prepare->bind_param($types,...$vars); |
||||
339 | if (!$prepare->execute()) { |
||||
340 | logger::write(loggerTypes::WARNING, $prepare->error); |
||||
341 | return false; |
||||
342 | } |
||||
343 | return $need_result ? $prepare->get_result() : true; |
||||
344 | } |
||||
345 | |||||
346 | private static function whereBuilder(string &$query, array $where = [], bool $ignore_default_where = false): array { |
||||
347 | if (!$ignore_default_where) { |
||||
348 | $where = array_merge(self::$default_where, $where); |
||||
349 | } |
||||
350 | |||||
351 | if (empty($where)) { |
||||
352 | return []; |
||||
353 | } |
||||
354 | |||||
355 | $query .= ' WHERE'; |
||||
356 | $first = true; |
||||
357 | $values = []; |
||||
358 | |||||
359 | foreach ($where as $name => $value) { |
||||
360 | if ($first) { |
||||
361 | $first = false; |
||||
362 | } |
||||
363 | else { |
||||
364 | $query .= ' AND'; |
||||
365 | } |
||||
366 | |||||
367 | if (empty($value)) { |
||||
368 | $query .= " `$name` = ?"; |
||||
369 | $values[] = $value; |
||||
370 | continue; |
||||
371 | } |
||||
372 | |||||
373 | if (!is_array($value)) { |
||||
374 | $value = [$value]; |
||||
375 | } |
||||
376 | |||||
377 | $sub_first = true; |
||||
378 | foreach ($value as $sub_value) { |
||||
379 | if ($sub_first) { |
||||
380 | $sub_first = false; |
||||
381 | } |
||||
382 | else { |
||||
383 | $query .= ' AND'; |
||||
384 | } |
||||
385 | $operator = substr($sub_value,0,2); |
||||
386 | $operator_value = substr($sub_value,2); |
||||
387 | switch ($operator) { |
||||
388 | case '>=': |
||||
389 | $query .= " `$name` >= ?"; |
||||
390 | $sub_value = $operator_value; |
||||
391 | break; |
||||
392 | case '<=': |
||||
393 | $query .= " `$name` <= ?"; |
||||
394 | $sub_value = $operator_value; |
||||
395 | break; |
||||
396 | case '> ': |
||||
397 | $query .= " `$name` > ?"; |
||||
398 | $sub_value = $operator_value; |
||||
399 | break; |
||||
400 | case '< ': |
||||
401 | $query .= " `$name` < ?"; |
||||
402 | $sub_value = $operator_value; |
||||
403 | break; |
||||
404 | case '% ': |
||||
405 | $query .= " `$name` like ?"; |
||||
406 | $sub_value = $operator_value; |
||||
407 | break; |
||||
408 | case '!=': |
||||
409 | $query .= " `$name` != ?"; |
||||
410 | $sub_value = $operator_value; |
||||
411 | break; |
||||
412 | case '##': |
||||
413 | $operator = substr($operator_value,0,2); |
||||
414 | $column = substr($operator_value,2); |
||||
415 | $query .= match ($operator) { |
||||
416 | '>=' => " `$name` >= `$column`", |
||||
417 | '<=' => " `$name` <= `$column`", |
||||
418 | '> ' => " `$name` > `$column`", |
||||
419 | '< ' => " `$name` < `$column`", |
||||
420 | '% ' => " `$name` like `$column`", |
||||
421 | '!=' => " `$name` != `$column`", |
||||
422 | default => " `$name` = `$column`", |
||||
423 | }; |
||||
424 | continue 2; |
||||
425 | default: |
||||
426 | $query .= " `$name` = ?"; |
||||
427 | break; |
||||
428 | } |
||||
429 | |||||
430 | $values[] = $sub_value; |
||||
431 | } |
||||
432 | |||||
433 | } |
||||
434 | |||||
435 | return $values; |
||||
436 | } |
||||
437 | |||||
438 | private static function groupByBuilder(string &$query, string|array $group_by = []): void { |
||||
439 | if (empty($group_by)) { |
||||
440 | return; |
||||
441 | } |
||||
442 | if (is_string($group_by)) { |
||||
0 ignored issues
–
show
|
|||||
443 | $group_by = [$group_by]; |
||||
444 | } |
||||
445 | $query .= ' GROUP BY `' . implode('`, `',$group_by) . '`'; |
||||
446 | } |
||||
447 | |||||
448 | private static function orderByBuilder(string &$query, string|array $order_by = []): void { |
||||
449 | if (empty($order_by)) { |
||||
450 | return; |
||||
451 | } |
||||
452 | if (is_string($order_by)) { |
||||
0 ignored issues
–
show
|
|||||
453 | $order_by = [$order_by => 'ASC']; |
||||
454 | } |
||||
455 | |||||
456 | $query .= ' ORDER BY `'; |
||||
457 | |||||
458 | $first = true; |
||||
459 | foreach ($order_by as $key => $mode) { |
||||
460 | if ($first) { |
||||
461 | $first = false; |
||||
462 | } |
||||
463 | else { |
||||
464 | $query .= ', '; |
||||
465 | } |
||||
466 | if (is_numeric($key)) { |
||||
467 | $key = $mode; |
||||
468 | $mode = 'ASC'; |
||||
469 | } |
||||
470 | $query .= "$key` $mode"; |
||||
471 | } |
||||
472 | } |
||||
473 | |||||
474 | private static function countBuilder(string &$query, int $count = null, int $offset = null): void { |
||||
475 | if (!empty($count)) { |
||||
476 | $query .= !empty($offset) ? " LIMIT $offset, $count" : " LIMIT $count"; |
||||
477 | } |
||||
478 | elseif (!empty($offset)) { |
||||
479 | $query .= " OFFSET $offset"; |
||||
480 | } |
||||
481 | } |
||||
482 | |||||
483 | private static function updateBuilder(string &$query, array $modify): array { |
||||
484 | $first = true; |
||||
485 | $values = []; |
||||
486 | |||||
487 | foreach ($modify as $name => $value) { |
||||
488 | if ($first) { |
||||
489 | $first = false; |
||||
490 | } |
||||
491 | else { |
||||
492 | $query .= ','; |
||||
493 | } |
||||
494 | |||||
495 | if (empty($value)) { |
||||
496 | $query .= " `$name` = ?"; |
||||
497 | $values[] = $value; |
||||
498 | continue; |
||||
499 | } |
||||
500 | |||||
501 | $operator = substr($value,0,2); |
||||
502 | $operator_value = substr($value,2); |
||||
503 | switch ($operator) { |
||||
504 | case '+=': |
||||
505 | $query .= " `$name` = `$name` + ?"; |
||||
506 | $value = $operator_value; |
||||
507 | break; |
||||
508 | case '-=': |
||||
509 | $query .= " `$name` = `$name` - ?"; |
||||
510 | $value = $operator_value; |
||||
511 | break; |
||||
512 | case '*=': |
||||
513 | $query .= " `$name` = `$name` * ?"; |
||||
514 | $value = $operator_value; |
||||
515 | break; |
||||
516 | case '/=': |
||||
517 | $query .= " `$name` = `$name` / ?"; |
||||
518 | $value = $operator_value; |
||||
519 | break; |
||||
520 | case '%=': |
||||
521 | $query .= " `$name` = `$name` % ?"; |
||||
522 | $value = $operator_value; |
||||
523 | break; |
||||
524 | default: |
||||
525 | $query .= " `$name` = ?"; |
||||
526 | break; |
||||
527 | } |
||||
528 | |||||
529 | $values[] = $value; |
||||
530 | } |
||||
531 | |||||
532 | return $values; |
||||
533 | } |
||||
534 | |||||
535 | private static function insertBuilder(string &$query, string|array $columns, array|string $values): array { |
||||
536 | $query .= '(`' . (is_string($columns) ? $columns : implode('`, `', $columns)) . '`) VALUES ('; |
||||
0 ignored issues
–
show
|
|||||
537 | if (is_string($values)) $values = [$values]; |
||||
0 ignored issues
–
show
|
|||||
538 | $query .= '?' . str_repeat(', ?', count($values) - 1) . ')'; |
||||
539 | return $values; |
||||
540 | } |
||||
541 | |||||
542 | private static function selectBuilder (string &$query, string|array $columns): void { |
||||
543 | if ($columns == '*') { |
||||
0 ignored issues
–
show
|
|||||
544 | $query .= ' * '; |
||||
545 | return; |
||||
546 | } |
||||
547 | if (is_string($columns)) { |
||||
0 ignored issues
–
show
|
|||||
548 | $query .= " `$columns` "; |
||||
549 | return; |
||||
550 | } |
||||
551 | $query .= ' '; |
||||
552 | foreach ($columns as $key => $column) { |
||||
553 | if (is_array($column)) { |
||||
554 | $function = array_key_first($column); |
||||
555 | $column = $column[$function]; |
||||
556 | $formatted = "`$column`"; |
||||
557 | if ($column == '*') { |
||||
558 | $formatted = '*'; |
||||
559 | $column = 'all'; |
||||
560 | } |
||||
561 | $query .= strtoupper($function) . "($formatted) as `{$function}_$column`"; |
||||
562 | } |
||||
563 | else { |
||||
564 | $query .= "`$column`"; |
||||
565 | } |
||||
566 | |||||
567 | if ($key != array_key_last($columns)) { |
||||
568 | $query .= ', '; |
||||
569 | } |
||||
570 | } |
||||
571 | $query .= ' '; |
||||
572 | } |
||||
573 | |||||
574 | /** |
||||
575 | * Run delete query |
||||
576 | * |
||||
577 | * e.g. : `mysql::delete('users',['id'=>123456789],1);` |
||||
578 | * |
||||
579 | * @param string $table table name |
||||
580 | * @param array $where Set your ifs |
||||
581 | * @param int|null $count Set if you want to delete specific amount of row default : null |
||||
582 | * @param int|null $offset Set if you want to delete rows after specific row default : null |
||||
583 | * @param bool $ignore_default_where |
||||
584 | * |
||||
585 | * @return bool |
||||
586 | */ |
||||
587 | public static function delete (string $table, array $where = [], int $count = null, int $offset = null, bool $ignore_default_where = false): bool { |
||||
588 | $query = "DELETE FROM `$table`"; |
||||
589 | $vars = self::whereBuilder($query, $where, $ignore_default_where); |
||||
590 | self::countBuilder($query, $count, $offset); |
||||
591 | return self::query($query, $vars, false); |
||||
0 ignored issues
–
show
|
|||||
592 | } |
||||
593 | |||||
594 | /** |
||||
595 | * Run update query |
||||
596 | * |
||||
597 | * e.g. : mysql::update('users',['step'=>'panel'],['id'=>123456789],1); |
||||
598 | * |
||||
599 | * @param string $table table name |
||||
600 | * @param array $modify Set the data's you want to modify |
||||
601 | * @param array $where Set your ifs |
||||
602 | * @param int|null $count Set if you want to update specific amount of row default : null |
||||
603 | * @param int|null $offset Set if you want to update rows after specific row default : null |
||||
604 | * @param bool $ignore_default_where |
||||
605 | * |
||||
606 | * @return bool |
||||
607 | */ |
||||
608 | public static function update (string $table, array $modify, array $where = [], int $count = null, int $offset = null, bool $ignore_default_where = false): bool { |
||||
609 | $query = "UPDATE `$table` SET"; |
||||
610 | $modify_vars = self::updateBuilder($query, $modify); |
||||
611 | $where_vars = self::whereBuilder($query, $where, $ignore_default_where); |
||||
612 | self::countBuilder($query, $count, $offset); |
||||
613 | return self::query($query, array_merge($modify_vars, $where_vars), false); |
||||
0 ignored issues
–
show
|
|||||
614 | } |
||||
615 | |||||
616 | /** |
||||
617 | * Run insert query |
||||
618 | * |
||||
619 | * e.g. : `mysql::insert('users',['id','column1','column2','column3'],[123456789,'value1','value2','value3']);` |
||||
620 | * |
||||
621 | * @param string $table table name |
||||
622 | * @param string|array $columns sets columns that you want to fill |
||||
623 | * @param array|string $values sets value that you want to set |
||||
624 | * |
||||
625 | * @return bool |
||||
626 | */ |
||||
627 | public static function insert (string $table, string|array $columns, array|string $values): bool { |
||||
628 | $query = "INSERT INTO `$table`"; |
||||
629 | $values = self::insertBuilder($query, $columns, $values); |
||||
630 | return self::query($query, $values, false); |
||||
0 ignored issues
–
show
|
|||||
631 | } |
||||
632 | |||||
633 | /** |
||||
634 | * Run insert query |
||||
635 | * |
||||
636 | * e.g. : `mysql::multiInsert('users', ['id' => 123, 'name' => 'qwe'], ['id' => 234, 'name' => 'wer'], ['name' => 'ert', 'id' => 345]]);` |
||||
637 | * |
||||
638 | * @param string $table table name |
||||
639 | * @param array ...$inserts ['column_name' => 'value', 'column_name2' => 'value2'] |
||||
640 | * |
||||
641 | * @return bool |
||||
642 | */ |
||||
643 | public static function multiInsert (string $table, array ...$inserts): bool { |
||||
644 | $all_letters = true; |
||||
645 | foreach ($inserts as $insert) { |
||||
646 | foreach ($insert as $column => $value) { |
||||
647 | if (!is_string($column)) { |
||||
648 | $all_letters = false; |
||||
649 | break 2; |
||||
650 | } |
||||
651 | } |
||||
652 | } |
||||
653 | $columns = array_keys($inserts[0]); |
||||
654 | if ($all_letters) { |
||||
655 | sort($columns); |
||||
656 | } |
||||
657 | $query = "INSERT INTO `$table`(`" . '' . (is_string($columns) ? $columns : implode('`, `', $columns)) . '`) VALUES'; |
||||
658 | |||||
659 | $all_values = []; |
||||
660 | foreach ($inserts as $key => $insert) { |
||||
661 | if ($all_letters) { |
||||
662 | asort($insert); |
||||
663 | } |
||||
664 | $values = array_values($insert); |
||||
665 | $query .= ($key != 0 ? ',' : '') . ' (?' . str_repeat(', ?', count($values) - 1) . ')'; |
||||
666 | $all_values = array_merge($all_values, $values); |
||||
667 | } |
||||
668 | return self::query($query, $all_values, false); |
||||
0 ignored issues
–
show
|
|||||
669 | } |
||||
670 | |||||
671 | /** |
||||
672 | * Run insert query with update on duplicate key |
||||
673 | * |
||||
674 | * These kind of query need to act on a key(primary key, unique key, ...) |
||||
675 | * |
||||
676 | * So your table must have a key column and your query must use it somehow(in insert part) |
||||
677 | * |
||||
678 | * e.g.(date is our key) : `mysql::insertUpdate('stats', ['date', 'new_users'], ['2020/04/08', 1], ['new_users' => '+=1']);` |
||||
679 | * |
||||
680 | * |
||||
681 | * @param string $table table name |
||||
682 | * @param string|array $columns sets columns that you want to fill |
||||
683 | * @param array|string $values sets value that you want to set |
||||
684 | * @param array $modify Set the data's you want to modify |
||||
685 | * |
||||
686 | * @return bool |
||||
687 | */ |
||||
688 | public static function insertUpdate (string $table, string|array $columns, array|string $values, array $modify): bool { |
||||
689 | $query = "INSERT INTO `$table`"; |
||||
690 | $values = self::insertBuilder($query, $columns, $values); |
||||
691 | $query .= ' ON DUPLICATE KEY UPDATE'; |
||||
692 | $modify_vars = self::updateBuilder($query, $modify); |
||||
693 | return self::query($query, array_merge($values, $modify_vars), false); |
||||
0 ignored issues
–
show
|
|||||
694 | } |
||||
695 | |||||
696 | /** |
||||
697 | * Run select query |
||||
698 | * |
||||
699 | * e.g. : mysql::select('users','*',['id'=>123456789],1); |
||||
700 | * |
||||
701 | * e.g. : mysql::select('users',['step','referrals'],['id'=>123456789],1); |
||||
702 | * |
||||
703 | * @param string $table table name |
||||
704 | * @param array|string $columns sets column that you want to retrieve , set '*' to retrieve all , default : '*' |
||||
705 | * @param array $where Set your ifs |
||||
706 | * @param int|null $count Set if you want to select specific amount of row default : null |
||||
707 | * @param int|null $offset Set if you want to select rows after specific row default : null |
||||
708 | * @param array|string $group_by group result based on these columns |
||||
709 | * @param array|string $order_by order result based on these columns |
||||
710 | * @param bool $ignore_default_where |
||||
711 | * |
||||
712 | * @return mysqli_result|bool |
||||
713 | */ |
||||
714 | public static function select (string $table, array|string $columns = '*', array $where = [], int $count = null, int $offset = null, array|string $group_by = [], array|string $order_by = [], bool $ignore_default_where = false): mysqli_result|bool { |
||||
715 | $query = 'SELECT'; |
||||
716 | self::selectBuilder($query, $columns); |
||||
717 | $query .= "FROM `$table`"; |
||||
718 | $var = self::whereBuilder($query,$where, $ignore_default_where); |
||||
719 | self::groupByBuilder($query, $group_by); |
||||
720 | self::orderByBuilder($query, $order_by); |
||||
721 | self::countBuilder($query,$count,$offset); |
||||
722 | return self::query($query, $var); |
||||
723 | } |
||||
724 | |||||
725 | /** |
||||
726 | * Same as mysql::select but return first result as array |
||||
727 | * |
||||
728 | * mysql::selectArray('users','*',['id'=>123456789]); |
||||
729 | * |
||||
730 | * @param string $table table name |
||||
731 | * @param array|string $columns sets column that you want to retrieve , set '*' to retrieve all , default : '*' |
||||
732 | * @param array $where Set your ifs |
||||
733 | * @param array|string $group_by group result based on these columns |
||||
734 | * @param array|string $order_by order result based on these columns |
||||
735 | * @param bool $ignore_default_where |
||||
736 | * |
||||
737 | * @return null|bool|array |
||||
738 | */ |
||||
739 | public static function selectArray (string $table, array|string $columns = '*', array $where = [], array|string $group_by = [], array|string $order_by = [], bool $ignore_default_where = false): bool|array|null { |
||||
740 | $res = self::select($table, $columns, $where, 1, 0, $group_by, $order_by, ignore_default_where: $ignore_default_where); |
||||
741 | if ($res) { |
||||
742 | return $res->fetch_assoc(); |
||||
743 | } |
||||
744 | return $res; |
||||
745 | } |
||||
746 | |||||
747 | /** |
||||
748 | * Same as mysql::select but return first result as object(stdClass) |
||||
749 | * |
||||
750 | * mysql::selectObject('users','*',['id'=>123456789]); |
||||
751 | * |
||||
752 | * @param string $table table name |
||||
753 | * @param array|string $columns sets column that you want to retrieve , set '*' to retrieve all , default : '*' |
||||
754 | * @param array $where Set your ifs |
||||
755 | * @param array|string $group_by group result based on these columns |
||||
756 | * @param array|string $order_by order result based on these columns |
||||
757 | * @param bool $ignore_default_where |
||||
758 | * |
||||
759 | * @return null|object |
||||
760 | */ |
||||
761 | public static function selectObject (string $table, array|string $columns = '*', array $where = [], array|string $group_by = [], array|string $order_by = [], bool $ignore_default_where = false) { |
||||
762 | $res = self::select($table, $columns, $where, 1, 0, $group_by, $order_by, ignore_default_where: $ignore_default_where); |
||||
763 | if ($res) { |
||||
764 | return $res->fetch_object(); |
||||
765 | } |
||||
766 | return $res; |
||||
0 ignored issues
–
show
|
|||||
767 | } |
||||
768 | |||||
769 | /** |
||||
770 | * Same as mysql::select but return each row as generator |
||||
771 | * |
||||
772 | * e.g. : mysql::selectEach('users','*',['id'=>123456789],1); |
||||
773 | * e.g. : mysql::selectEach('users',['id']); |
||||
774 | * |
||||
775 | * @param string $table table name |
||||
776 | * @param array|string $columns sets column that you want to retrieve , set '*' to retrieve all , default : '*' |
||||
777 | * @param array $where Set your ifs |
||||
778 | * @param int|null $count Set if you want to select specific amount of row default : null |
||||
779 | * @param int|null $offset Set if you want to select rows after specific row default : null |
||||
780 | * @param array|string $group_by group result based on these columns |
||||
781 | * @param array|string $order_by order result based on these columns |
||||
782 | * @param bool $ignore_default_where |
||||
783 | * |
||||
784 | * @return bool|Generator |
||||
785 | */ |
||||
786 | public static function selectEach (string $table, array|string $columns = '*', array $where = [], int $count = null, int $offset = null, array|string $group_by = [], array|string $order_by = [], bool $ignore_default_where = false): bool|Generator { |
||||
787 | $res = self::select($table, $columns, $where, $count, $offset, $group_by, $order_by, ignore_default_where: $ignore_default_where); |
||||
788 | if ($res) { |
||||
789 | while ($row = $res->fetch_assoc()) yield $row; |
||||
790 | } |
||||
791 | else return $res; |
||||
792 | } |
||||
793 | |||||
794 | /** |
||||
795 | * get backup from database, you can get full backup or specific table backup |
||||
796 | * |
||||
797 | * @param array $wanted_tables set if you want specific table which exist |
||||
798 | * @param bool $table_data set false if you only want the creation queries(no data) |
||||
799 | * @param bool $save set false if you want to receive sql as string |
||||
800 | * @param string $file_name file name for saving |
||||
801 | * |
||||
802 | * @return string if save is true , return file name otherwise return sql data |
||||
803 | */ |
||||
804 | public static function backup (array $wanted_tables = [], bool $table_data = true, bool $save = true, string $file_name = ''): string { |
||||
805 | self::setCharset('utf8mb4'); |
||||
806 | |||||
807 | $tables = array_column(self::query('SHOW TABLES')->fetch_all(),0); |
||||
808 | if (!empty($wanted_tables)) { |
||||
809 | $tables = array_intersect($tables, $wanted_tables); |
||||
810 | } |
||||
811 | |||||
812 | $sql = ''; |
||||
813 | |||||
814 | if (empty($tables)) { |
||||
815 | logger::write('No table founded for backup, if your database has table : check $wanted_tables argument', loggerTypes::WARNING); |
||||
816 | } |
||||
817 | foreach ($tables as $table) { |
||||
818 | $sql .= self::query("SHOW CREATE TABLE `$table`")->fetch_row()[1] . ";\n\n"; |
||||
819 | if ($table_data) { |
||||
820 | $total_rows = self::query("SELECT COUNT(*) as `cnt` FROM `$table`")->fetch_object()->cnt; |
||||
821 | for ($i = 0; $i < $total_rows; $i = $i + 1000) { |
||||
822 | $sql .= 'INSERT INTO ' . $table . ' VALUES'; |
||||
823 | $result = self::select($table, '*' , [], 1000, $i); |
||||
824 | $field_count = $result->field_count; |
||||
825 | $affected_rows = self::affected_rows(); |
||||
826 | $counter = 1; |
||||
827 | while ($row = $result->fetch_row()) { |
||||
828 | $sql .= "\n("; |
||||
829 | for ($column = 0; $column < $field_count; $column++) { |
||||
830 | $row[$column] = str_replace("\n", "\\n", addslashes($row[$column])); |
||||
831 | $sql .= !empty($row[$column]) ? '"' . $row[$column] . '"' : '""'; |
||||
832 | if ($column < $field_count - 1) { |
||||
833 | $sql .= ','; |
||||
834 | } |
||||
835 | } |
||||
836 | $sql .= ')' . ($counter == $affected_rows ? ';' : ','); |
||||
837 | $counter++; |
||||
838 | } |
||||
839 | } |
||||
840 | if ($total_rows > 0) { |
||||
841 | $sql .= "\n\n"; |
||||
842 | } |
||||
843 | } |
||||
844 | $sql .= "\n"; |
||||
845 | } |
||||
846 | |||||
847 | if (!$save) { |
||||
848 | return $sql; |
||||
849 | } |
||||
850 | |||||
851 | if (empty($file_name)) { |
||||
852 | $file_name = self::$db_name . time() . '.sql'; |
||||
853 | } |
||||
854 | file_put_contents($file_name, $sql); |
||||
855 | return $file_name; |
||||
856 | } |
||||
857 | } |