Completed
Push — 944-bot_api_4.2_polls ( 50d732 )
by Armando
02:58
created

DB::insertEditedMessageRequest()   B

Complexity

Conditions 5
Paths 46

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 44
ccs 0
cts 24
cp 0
rs 8.9048
c 0
b 0
f 0
cc 5
nc 46
nop 1
crap 30
1
<?php
2
/**
3
 * This file is part of the TelegramBot package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 * Written by Marco Boretto <[email protected]>
10
 */
11
12
namespace Longman\TelegramBot;
13
14
use Exception;
15
use Longman\TelegramBot\Entities\CallbackQuery;
16
use Longman\TelegramBot\Entities\Chat;
17
use Longman\TelegramBot\Entities\ChosenInlineResult;
18
use Longman\TelegramBot\Entities\InlineQuery;
19
use Longman\TelegramBot\Entities\Message;
20
use Longman\TelegramBot\Entities\Poll;
21
use Longman\TelegramBot\Entities\ReplyToMessage;
22
use Longman\TelegramBot\Entities\Update;
23
use Longman\TelegramBot\Entities\User;
24
use Longman\TelegramBot\Exception\TelegramException;
25
use PDO;
26
use PDOException;
27
28
class DB
29
{
30
    /**
31
     * MySQL credentials
32
     *
33
     * @var array
34
     */
35
    protected static $mysql_credentials = [];
36
37
    /**
38
     * PDO object
39
     *
40
     * @var PDO
41
     */
42
    protected static $pdo;
43
44
    /**
45
     * Table prefix
46
     *
47
     * @var string
48
     */
49
    protected static $table_prefix;
50
51
    /**
52
     * Telegram class object
53
     *
54
     * @var Telegram
55
     */
56
    protected static $telegram;
57
58
    /**
59
     * Initialize
60
     *
61
     * @param array    $credentials  Database connection details
62
     * @param Telegram $telegram     Telegram object to connect with this object
63
     * @param string   $table_prefix Table prefix
64
     * @param string   $encoding     Database character encoding
65
     *
66
     * @return PDO PDO database object
67
     * @throws TelegramException
68
     */
69 9
    public static function initialize(
70
        array $credentials,
71
        Telegram $telegram,
72
        $table_prefix = null,
73
        $encoding = 'utf8mb4'
74
    ) {
75 9
        if (empty($credentials)) {
76
            throw new TelegramException('MySQL credentials not provided!');
77
        }
78
79 9
        $dsn = 'mysql:host=' . $credentials['host'] . ';dbname=' . $credentials['database'];
80 9
        if (!empty($credentials['port'])) {
81
            $dsn .= ';port=' . $credentials['port'];
82
        }
83
84 9
        $options = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $encoding];
85
        try {
86 9
            $pdo = new PDO($dsn, $credentials['user'], $credentials['password'], $options);
87 9
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
88
        } catch (PDOException $e) {
89
            throw new TelegramException($e->getMessage());
90
        }
91
92 9
        self::$pdo               = $pdo;
93 9
        self::$telegram          = $telegram;
94 9
        self::$mysql_credentials = $credentials;
95 9
        self::$table_prefix      = $table_prefix;
96
97 9
        self::defineTables();
98
99 9
        return self::$pdo;
100
    }
101
102
    /**
103
     * External Initialize
104
     *
105
     * Let you use the class with an external already existing Pdo Mysql connection.
106
     *
107
     * @param PDO      $external_pdo_connection PDO database object
108
     * @param Telegram $telegram                Telegram object to connect with this object
109
     * @param string   $table_prefix            Table prefix
110
     *
111
     * @return PDO PDO database object
112
     * @throws TelegramException
113
     */
114
    public static function externalInitialize(
115
        $external_pdo_connection,
116
        Telegram $telegram,
117
        $table_prefix = null
118
    ) {
119
        if ($external_pdo_connection === null) {
120
            throw new TelegramException('MySQL external connection not provided!');
121
        }
122
123
        self::$pdo               = $external_pdo_connection;
124
        self::$telegram          = $telegram;
125
        self::$mysql_credentials = [];
126
        self::$table_prefix      = $table_prefix;
127
128
        self::defineTables();
129
130
        return self::$pdo;
131
    }
132
133
    /**
134
     * Define all the tables with the proper prefix
135
     */
136 9
    protected static function defineTables()
137
    {
138
        $tables = [
139 9
            'callback_query',
140
            'chat',
141
            'chosen_inline_result',
142
            'edited_message',
143
            'inline_query',
144
            'message',
145
            'poll',
146
            'request_limiter',
147
            'telegram_update',
148
            'user',
149
            'user_chat',
150
        ];
151 9
        foreach ($tables as $table) {
152 9
            $table_name = 'TB_' . strtoupper($table);
153 9
            if (!defined($table_name)) {
154 1
                define($table_name, self::$table_prefix . $table);
155
            }
156
        }
157 9
    }
158
159
    /**
160
     * Check if database connection has been created
161
     *
162
     * @return bool
163
     */
164 9
    public static function isDbConnected()
165
    {
166 9
        return self::$pdo !== null;
167
    }
168
169
    /**
170
     * Get the PDO object of the connected database
171
     *
172
     * @return PDO
173
     */
174
    public static function getPdo()
175
    {
176
        return self::$pdo;
177
    }
178
179
    /**
180
     * Fetch update(s) from DB
181
     *
182
     * @param int    $limit Limit the number of updates to fetch
183
     * @param string $id    Check for unique update id
184
     *
185
     * @return array|bool Fetched data or false if not connected
186
     * @throws TelegramException
187
     */
188
    public static function selectTelegramUpdate($limit = null, $id = null)
189
    {
190
        if (!self::isDbConnected()) {
191
            return false;
192
        }
193
194
        try {
195
            $sql = '
196
                SELECT `id`
197
                FROM `' . TB_TELEGRAM_UPDATE . '`
198
            ';
199
200
            if ($id !== null) {
201
                $sql .= ' WHERE `id` = :id';
202
            } else {
203
                $sql .= ' ORDER BY `id` DESC';
204
            }
205
206
            if ($limit !== null) {
207
                $sql .= ' LIMIT :limit';
208
            }
209
210
            $sth = self::$pdo->prepare($sql);
211
212
            if ($limit !== null) {
213
                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
214
            }
215
            if ($id !== null) {
216
                $sth->bindValue(':id', $id);
217
            }
218
219
            $sth->execute();
220
221
            return $sth->fetchAll(PDO::FETCH_ASSOC);
222
        } catch (PDOException $e) {
223
            throw new TelegramException($e->getMessage());
224
        }
225
    }
226
227
    /**
228
     * Fetch message(s) from DB
229
     *
230
     * @param int $limit Limit the number of messages to fetch
231
     *
232
     * @return array|bool Fetched data or false if not connected
233
     * @throws TelegramException
234
     */
235
    public static function selectMessages($limit = null)
236
    {
237
        if (!self::isDbConnected()) {
238
            return false;
239
        }
240
241
        try {
242
            $sql = '
243
                SELECT *
244
                FROM `' . TB_MESSAGE . '`
245
                ORDER BY `id` DESC
246
            ';
247
248
            if ($limit !== null) {
249
                $sql .= ' LIMIT :limit';
250
            }
251
252
            $sth = self::$pdo->prepare($sql);
253
254
            if ($limit !== null) {
255
                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
256
            }
257
258
            $sth->execute();
259
260
            return $sth->fetchAll(PDO::FETCH_ASSOC);
261
        } catch (PDOException $e) {
262
            throw new TelegramException($e->getMessage());
263
        }
264
    }
265
266
    /**
267
     * Convert from unix timestamp to timestamp
268
     *
269
     * @param int $time Unix timestamp (if empty, current timestamp is used)
270
     *
271
     * @return string
272
     */
273 7
    protected static function getTimestamp($time = null)
274
    {
275 7
        return date('Y-m-d H:i:s', $time ?: time());
276
    }
277
278
    /**
279
     * Convert array of Entity items to a JSON array
280
     *
281
     * @todo Find a better way, as json_* functions are very heavy
282
     *
283
     * @param array|null $entities
284
     * @param mixed      $default
285
     *
286
     * @return mixed
287
     */
288 6
    public static function entitiesArrayToJson($entities, $default = null)
289
    {
290 6
        if (!is_array($entities)) {
291 6
            return $default;
292
        }
293
294
        // Convert each Entity item into an object based on its JSON reflection
295
        $json_entities = array_map(function ($entity) {
296
            return json_decode($entity, true);
297
        }, $entities);
298
299
        return json_encode($json_entities);
300
    }
301
302
    /**
303
     * Insert entry to telegram_update table
304
     *
305
     * @todo Add missing values! See https://core.telegram.org/bots/api#update
306
     *
307
     * @param string $id
308
     * @param string $chat_id
309
     * @param string $message_id
310
     * @param string $inline_query_id
311
     * @param string $chosen_inline_result_id
312
     * @param string $callback_query_id
313
     * @param string $edited_message_id
314
     * @param string $poll_id
315
     *
316
     * @return bool If the insert was successful
317
     * @throws TelegramException
318
     */
319
    public static function insertTelegramUpdate(
320
        $id,
321
        $chat_id = null,
322
        $message_id = null,
323
        $inline_query_id = null,
324
        $chosen_inline_result_id = null,
325
        $callback_query_id = null,
326
        $edited_message_id = null,
327
        $poll_id = null
328
    ) {
329
        if ($message_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $edited_message_id === null && $poll_id === null) {
330
            throw new TelegramException('message_id, inline_query_id, chosen_inline_result_id, callback_query_id, edited_message_id, poll_id are all null');
331
        }
332
333
        if (!self::isDbConnected()) {
334
            return false;
335
        }
336
337
        try {
338
            $sth = self::$pdo->prepare('
339
                INSERT IGNORE INTO `' . TB_TELEGRAM_UPDATE . '`
340
                (`id`, `chat_id`, `message_id`, `inline_query_id`, `chosen_inline_result_id`, `callback_query_id`, `edited_message_id`, `poll_id`)
341
                VALUES
342
                (:id, :chat_id, :message_id, :inline_query_id, :chosen_inline_result_id, :callback_query_id, :edited_message_id, :poll_id)
343
            ');
344
345
            $sth->bindValue(':id', $id);
346
            $sth->bindValue(':chat_id', $chat_id);
347
            $sth->bindValue(':message_id', $message_id);
348
            $sth->bindValue(':edited_message_id', $edited_message_id);
349
            $sth->bindValue(':inline_query_id', $inline_query_id);
350
            $sth->bindValue(':chosen_inline_result_id', $chosen_inline_result_id);
351
            $sth->bindValue(':callback_query_id', $callback_query_id);
352
            $sth->bindValue(':poll_id', $poll_id);
353
354
            return $sth->execute();
355
        } catch (PDOException $e) {
356
            throw new TelegramException($e->getMessage());
357
        }
358
    }
359
360
    /**
361
     * Insert users and save their connection to chats
362
     *
363
     * @param User   $user
364
     * @param string $date
365
     * @param Chat   $chat
366
     *
367
     * @return bool If the insert was successful
368
     * @throws TelegramException
369
     */
370 6
    public static function insertUser(User $user, $date = null, Chat $chat = null)
371
    {
372 6
        if (!self::isDbConnected()) {
373
            return false;
374
        }
375
376
        try {
377 6
            $sth = self::$pdo->prepare('
378 6
                INSERT INTO `' . TB_USER . '`
379
                (`id`, `is_bot`, `username`, `first_name`, `last_name`, `language_code`, `created_at`, `updated_at`)
380
                VALUES
381
                (:id, :is_bot, :username, :first_name, :last_name, :language_code, :created_at, :updated_at)
382
                ON DUPLICATE KEY UPDATE
383
                    `is_bot`         = VALUES(`is_bot`),
384
                    `username`       = VALUES(`username`),
385
                    `first_name`     = VALUES(`first_name`),
386
                    `last_name`      = VALUES(`last_name`),
387
                    `language_code`  = VALUES(`language_code`),
388
                    `updated_at`     = VALUES(`updated_at`)
389
            ');
390
391 6
            $sth->bindValue(':id', $user->getId());
392 6
            $sth->bindValue(':is_bot', $user->getIsBot(), PDO::PARAM_INT);
393 6
            $sth->bindValue(':username', $user->getUsername());
394 6
            $sth->bindValue(':first_name', $user->getFirstName());
395 6
            $sth->bindValue(':last_name', $user->getLastName());
396 6
            $sth->bindValue(':language_code', $user->getLanguageCode());
397 6
            $date = $date ?: self::getTimestamp();
398 6
            $sth->bindValue(':created_at', $date);
399 6
            $sth->bindValue(':updated_at', $date);
400
401 6
            $status = $sth->execute();
402
        } catch (PDOException $e) {
403
            throw new TelegramException($e->getMessage());
404
        }
405
406
        // Also insert the relationship to the chat into the user_chat table
407 6
        if ($chat instanceof Chat) {
408
            try {
409 6
                $sth = self::$pdo->prepare('
410 6
                    INSERT IGNORE INTO `' . TB_USER_CHAT . '`
411
                    (`user_id`, `chat_id`)
412
                    VALUES
413
                    (:user_id, :chat_id)
414
                ');
415
416 6
                $sth->bindValue(':user_id', $user->getId());
417 6
                $sth->bindValue(':chat_id', $chat->getId());
418
419 6
                $status = $sth->execute();
420
            } catch (PDOException $e) {
421
                throw new TelegramException($e->getMessage());
422
            }
423
        }
424
425 6
        return $status;
426
    }
427
428
    /**
429
     * Insert chat
430
     *
431
     * @param Chat   $chat
432
     * @param string $date
433
     * @param string $migrate_to_chat_id
434
     *
435
     * @return bool If the insert was successful
436
     * @throws TelegramException
437
     */
438 6
    public static function insertChat(Chat $chat, $date = null, $migrate_to_chat_id = null)
439
    {
440 6
        if (!self::isDbConnected()) {
441
            return false;
442
        }
443
444
        try {
445 6
            $sth = self::$pdo->prepare('
446 6
                INSERT IGNORE INTO `' . TB_CHAT . '`
447
                (`id`, `type`, `title`, `username`, `all_members_are_administrators`, `created_at` ,`updated_at`, `old_id`)
448
                VALUES
449
                (:id, :type, :title, :username, :all_members_are_administrators, :created_at, :updated_at, :old_id)
450
                ON DUPLICATE KEY UPDATE
451
                    `type`                           = VALUES(`type`),
452
                    `title`                          = VALUES(`title`),
453
                    `username`                       = VALUES(`username`),
454
                    `all_members_are_administrators` = VALUES(`all_members_are_administrators`),
455
                    `updated_at`                     = VALUES(`updated_at`)
456
            ');
457
458 6
            $chat_id   = $chat->getId();
459 6
            $chat_type = $chat->getType();
460
461 6
            if ($migrate_to_chat_id !== null) {
462
                $chat_type = 'supergroup';
463
464
                $sth->bindValue(':id', $migrate_to_chat_id);
465
                $sth->bindValue(':old_id', $chat_id);
466
            } else {
467 6
                $sth->bindValue(':id', $chat_id);
468 6
                $sth->bindValue(':old_id', $migrate_to_chat_id);
469
            }
470
471 6
            $sth->bindValue(':type', $chat_type);
472 6
            $sth->bindValue(':title', $chat->getTitle());
473 6
            $sth->bindValue(':username', $chat->getUsername());
474 6
            $sth->bindValue(':all_members_are_administrators', $chat->getAllMembersAreAdministrators(), PDO::PARAM_INT);
475 6
            $date = $date ?: self::getTimestamp();
476 6
            $sth->bindValue(':created_at', $date);
477 6
            $sth->bindValue(':updated_at', $date);
478
479 6
            return $sth->execute();
480
        } catch (PDOException $e) {
481
            throw new TelegramException($e->getMessage());
482
        }
483
    }
484
485
    /**
486
     * Insert request into database
487
     *
488
     * @todo self::$pdo->lastInsertId() - unsafe usage if expected previous insert fails?
489
     *
490
     * @param Update $update
491
     *
492
     * @return bool
493
     * @throws TelegramException
494
     */
495
    public static function insertRequest(Update $update)
496
    {
497
        if (!self::isDbConnected()) {
498
            return false;
499
        }
500
501
        $update_id   = $update->getUpdateId();
502
        $update_type = $update->getUpdateType();
503
504
        // @todo Make this simpler: if ($message = $update->getMessage()) ...
505
        if ($update_type === 'message') {
506
            $message = $update->getMessage();
507
508 View Code Duplication
            if (self::insertMessageRequest($message)) {
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...
509
                $message_id = $message->getMessageId();
510
                $chat_id    = $message->getChat()->getId();
511
512
                return self::insertTelegramUpdate(
513
                    $update_id,
514
                    $chat_id,
515
                    $message_id
516
                );
517
            }
518 View Code Duplication
        } elseif ($update_type === 'edited_message') {
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...
519
            $edited_message = $update->getEditedMessage();
520
521
            if (self::insertEditedMessageRequest($edited_message)) {
522
                $edited_message_local_id = self::$pdo->lastInsertId();
523
                $chat_id                 = $edited_message->getChat()->getId();
524
525
                return self::insertTelegramUpdate(
526
                    $update_id,
527
                    $chat_id,
528
                    null,
529
                    null,
530
                    null,
531
                    null,
532
                    $edited_message_local_id
533
                );
534
            }
535
        } elseif ($update_type === 'channel_post') {
536
            $channel_post = $update->getChannelPost();
537
538 View Code Duplication
            if (self::insertMessageRequest($channel_post)) {
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...
539
                $message_id = $channel_post->getMessageId();
540
                $chat_id    = $channel_post->getChat()->getId();
541
542
                return self::insertTelegramUpdate(
543
                    $update_id,
544
                    $chat_id,
545
                    $message_id
546
                );
547
            }
548 View Code Duplication
        } elseif ($update_type === 'edited_channel_post') {
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...
549
            $edited_channel_post = $update->getEditedChannelPost();
550
551
            if (self::insertEditedMessageRequest($edited_channel_post)) {
552
                $edited_channel_post_local_id = self::$pdo->lastInsertId();
553
                $chat_id                      = $edited_channel_post->getChat()->getId();
554
555
                return self::insertTelegramUpdate(
556
                    $update_id,
557
                    $chat_id,
558
                    null,
559
                    null,
560
                    null,
561
                    null,
562
                    $edited_channel_post_local_id
563
                );
564
            }
565
        } elseif ($update_type === 'inline_query') {
566
            $inline_query = $update->getInlineQuery();
567
568
            if (self::insertInlineQueryRequest($inline_query)) {
569
                $inline_query_id = $inline_query->getId();
570
571
                return self::insertTelegramUpdate(
572
                    $update_id,
573
                    null,
574
                    null,
575
                    $inline_query_id
576
                );
577
            }
578
        } elseif ($update_type === 'chosen_inline_result') {
579
            $chosen_inline_result = $update->getChosenInlineResult();
580
581
            if (self::insertChosenInlineResultRequest($chosen_inline_result)) {
582
                $chosen_inline_result_local_id = self::$pdo->lastInsertId();
583
584
                return self::insertTelegramUpdate(
585
                    $update_id,
586
                    null,
587
                    null,
588
                    null,
589
                    $chosen_inline_result_local_id
590
                );
591
            }
592
        } elseif ($update_type === 'callback_query') {
593
            $callback_query = $update->getCallbackQuery();
594
595
            if (self::insertCallbackQueryRequest($callback_query)) {
596
                $callback_query_id = $callback_query->getId();
597
598
                return self::insertTelegramUpdate(
599
                    $update_id,
600
                    null,
601
                    null,
602
                    null,
603
                    null,
604
                    $callback_query_id
605
                );
606
            }
607
        } elseif ($update_type === 'poll') {
608
            $poll = $update->getPoll();
609
610
            if (self::insertPollRequest($poll)) {
611
                $poll_id = $poll->getId();
612
613
                return self::insertTelegramUpdate(
614
                    $update_id,
615
                    null,
616
                    null,
617
                    null,
618
                    null,
619
                    null,
620
                    null,
621
                    $poll_id
622
                );
623
            }
624
        }
625
626
        return false;
627
    }
628
629
    /**
630
     * Insert inline query request into database
631
     *
632
     * @param InlineQuery $inline_query
633
     *
634
     * @return bool If the insert was successful
635
     * @throws TelegramException
636
     */
637 View Code Duplication
    public static function insertInlineQueryRequest(InlineQuery $inline_query)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
638
    {
639
        if (!self::isDbConnected()) {
640
            return false;
641
        }
642
643
        try {
644
            $sth = self::$pdo->prepare('
645
                INSERT IGNORE INTO `' . TB_INLINE_QUERY . '`
646
                (`id`, `user_id`, `location`, `query`, `offset`, `created_at`)
647
                VALUES
648
                (:id, :user_id, :location, :query, :offset, :created_at)
649
            ');
650
651
            $date    = self::getTimestamp();
652
            $user_id = null;
653
654
            $user = $inline_query->getFrom();
655
            if ($user instanceof User) {
656
                $user_id = $user->getId();
657
                self::insertUser($user, $date);
658
            }
659
660
            $sth->bindValue(':id', $inline_query->getId());
661
            $sth->bindValue(':user_id', $user_id);
662
            $sth->bindValue(':location', $inline_query->getLocation());
663
            $sth->bindValue(':query', $inline_query->getQuery());
664
            $sth->bindValue(':offset', $inline_query->getOffset());
665
            $sth->bindValue(':created_at', $date);
666
667
            return $sth->execute();
668
        } catch (PDOException $e) {
669
            throw new TelegramException($e->getMessage());
670
        }
671
    }
672
673
    /**
674
     * Insert chosen inline result request into database
675
     *
676
     * @param ChosenInlineResult $chosen_inline_result
677
     *
678
     * @return bool If the insert was successful
679
     * @throws TelegramException
680
     */
681 View Code Duplication
    public static function insertChosenInlineResultRequest(ChosenInlineResult $chosen_inline_result)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
682
    {
683
        if (!self::isDbConnected()) {
684
            return false;
685
        }
686
687
        try {
688
            $sth = self::$pdo->prepare('
689
                INSERT INTO `' . TB_CHOSEN_INLINE_RESULT . '`
690
                (`result_id`, `user_id`, `location`, `inline_message_id`, `query`, `created_at`)
691
                VALUES
692
                (:result_id, :user_id, :location, :inline_message_id, :query, :created_at)
693
            ');
694
695
            $date    = self::getTimestamp();
696
            $user_id = null;
697
698
            $user = $chosen_inline_result->getFrom();
699
            if ($user instanceof User) {
700
                $user_id = $user->getId();
701
                self::insertUser($user, $date);
702
            }
703
704
            $sth->bindValue(':result_id', $chosen_inline_result->getResultId());
705
            $sth->bindValue(':user_id', $user_id);
706
            $sth->bindValue(':location', $chosen_inline_result->getLocation());
707
            $sth->bindValue(':inline_message_id', $chosen_inline_result->getInlineMessageId());
708
            $sth->bindValue(':query', $chosen_inline_result->getQuery());
709
            $sth->bindValue(':created_at', $date);
710
711
            return $sth->execute();
712
        } catch (PDOException $e) {
713
            throw new TelegramException($e->getMessage());
714
        }
715
    }
716
717
    /**
718
     * Insert callback query request into database
719
     *
720
     * @param CallbackQuery $callback_query
721
     *
722
     * @return bool If the insert was successful
723
     * @throws TelegramException
724
     */
725
    public static function insertCallbackQueryRequest(CallbackQuery $callback_query)
726
    {
727
        if (!self::isDbConnected()) {
728
            return false;
729
        }
730
731
        try {
732
            $sth = self::$pdo->prepare('
733
                INSERT IGNORE INTO `' . TB_CALLBACK_QUERY . '`
734
                (`id`, `user_id`, `chat_id`, `message_id`, `inline_message_id`, `data`, `created_at`)
735
                VALUES
736
                (:id, :user_id, :chat_id, :message_id, :inline_message_id, :data, :created_at)
737
            ');
738
739
            $date    = self::getTimestamp();
740
            $user_id = null;
741
742
            $user = $callback_query->getFrom();
743
            if ($user instanceof User) {
744
                $user_id = $user->getId();
745
                self::insertUser($user, $date);
746
            }
747
748
            $message    = $callback_query->getMessage();
749
            $chat_id    = null;
750
            $message_id = null;
751
            if ($message instanceof Message) {
752
                $chat_id    = $message->getChat()->getId();
753
                $message_id = $message->getMessageId();
754
755
                $is_message = self::$pdo->query('
756
                    SELECT *
757
                    FROM `' . TB_MESSAGE . '`
758
                    WHERE `id` = ' . $message_id . '
759
                      AND `chat_id` = ' . $chat_id . '
760
                    LIMIT 1
761
                ')->rowCount();
762
763
                if ($is_message) {
764
                    self::insertEditedMessageRequest($message);
765
                } else {
766
                    self::insertMessageRequest($message);
767
                }
768
            }
769
770
            $sth->bindValue(':id', $callback_query->getId());
771
            $sth->bindValue(':user_id', $user_id);
772
            $sth->bindValue(':chat_id', $chat_id);
773
            $sth->bindValue(':message_id', $message_id);
774
            $sth->bindValue(':inline_message_id', $callback_query->getInlineMessageId());
775
            $sth->bindValue(':data', $callback_query->getData());
776
            $sth->bindValue(':created_at', $date);
777
778
            return $sth->execute();
779
        } catch (PDOException $e) {
780
            throw new TelegramException($e->getMessage());
781
        }
782
    }
783
784
    /**
785
     * Insert poll request into database
786
     *
787
     * @param Poll $poll
788
     *
789
     * @return bool If the insert was successful
790
     * @throws TelegramException
791
     */
792
    public static function insertPollRequest(Poll $poll)
793
    {
794
        if (!self::isDbConnected()) {
795
            return false;
796
        }
797
798
        try {
799
            $sth = self::$pdo->prepare('
800
                INSERT INTO `' . TB_POLL . '`
801
                (`id`, `question`, `options`, `is_closed`, `created_at`)
802
                VALUES
803
                (:id, :question, :options, :is_closed, :created_at)
804
                ON DUPLICATE KEY UPDATE
805
                    `options`   = VALUES(`options`),
806
                    `is_closed` = VALUES(`is_closed`)
807
            ');
808
809
            $sth->bindValue(':id', $poll->getId());
810
            $sth->bindValue(':question', $poll->getQuestion());
811
            $sth->bindValue(':options', self::entitiesArrayToJson($poll->getOptions(), null));
812
            $sth->bindValue(':is_closed', $poll->getIsClosed());
813
            $sth->bindValue(':created_at', self::getTimestamp());
814
815
            return $sth->execute();
816
        } catch (PDOException $e) {
817
            throw new TelegramException($e->getMessage());
818
        }
819
    }
820
821
    /**
822
     * Insert Message request in db
823
     *
824
     * @todo Complete with new fields: https://core.telegram.org/bots/api#message
825
     *
826
     * @param Message $message
827
     *
828
     * @return bool If the insert was successful
829
     * @throws TelegramException
830
     */
831 6
    public static function insertMessageRequest(Message $message)
832
    {
833 6
        if (!self::isDbConnected()) {
834
            return false;
835
        }
836
837 6
        $date = self::getTimestamp($message->getDate());
838
839
        // Insert chat, update chat id in case it migrated
840 6
        $chat = $message->getChat();
841 6
        self::insertChat($chat, $date, $message->getMigrateToChatId());
842
843
        // Insert user and the relation with the chat
844 6
        $user = $message->getFrom();
845 6
        if ($user instanceof User) {
846 6
            self::insertUser($user, $date, $chat);
847
        }
848
849
        // Insert the forwarded message user in users table
850 6
        $forward_date = null;
851 6
        $forward_from = $message->getForwardFrom();
852 6
        if ($forward_from instanceof User) {
853
            self::insertUser($forward_from, $forward_date);
854
            $forward_from = $forward_from->getId();
855
            $forward_date = self::getTimestamp($message->getForwardDate());
856
        }
857 6
        $forward_from_chat = $message->getForwardFromChat();
858 6
        if ($forward_from_chat instanceof Chat) {
859
            self::insertChat($forward_from_chat, $forward_date);
860
            $forward_from_chat = $forward_from_chat->getId();
861
            $forward_date      = self::getTimestamp($message->getForwardDate());
862
        }
863
864
        // New and left chat member
865 6
        $new_chat_members_ids = null;
866 6
        $left_chat_member_id  = null;
867
868 6
        $new_chat_members = $message->getNewChatMembers();
869 6
        $left_chat_member = $message->getLeftChatMember();
870 6
        if (!empty($new_chat_members)) {
871
            foreach ($new_chat_members as $new_chat_member) {
872
                if ($new_chat_member instanceof User) {
873
                    // Insert the new chat user
874
                    self::insertUser($new_chat_member, $date, $chat);
875
                    $new_chat_members_ids[] = $new_chat_member->getId();
876
                }
877
            }
878
            $new_chat_members_ids = implode(',', $new_chat_members_ids);
879 6
        } elseif ($left_chat_member instanceof User) {
880
            // Insert the left chat user
881
            self::insertUser($left_chat_member, $date, $chat);
882
            $left_chat_member_id = $left_chat_member->getId();
883
        }
884
885
        try {
886 6
            $sth = self::$pdo->prepare('
887 6
                INSERT IGNORE INTO `' . TB_MESSAGE . '`
888
                (
889
                    `id`, `user_id`, `chat_id`, `date`, `forward_from`, `forward_from_chat`, `forward_from_message_id`,
890
                    `forward_signature`, `forward_sender_name`, `forward_date`,
891
                    `reply_to_chat`, `reply_to_message`, `media_group_id`, `text`, `entities`, `audio`, `document`,
892
                    `animation`, `game`, `photo`, `sticker`, `video`, `voice`, `video_note`, `caption`, `contact`,
893
                    `location`, `venue`, `poll`, `new_chat_members`, `left_chat_member`,
894
                    `new_chat_title`,`new_chat_photo`, `delete_chat_photo`, `group_chat_created`,
895
                    `supergroup_chat_created`, `channel_chat_created`,
896
                    `migrate_from_chat_id`, `migrate_to_chat_id`, `pinned_message`, `connected_website`, `passport_data`
897
                ) VALUES (
898
                    :message_id, :user_id, :chat_id, :date, :forward_from, :forward_from_chat, :forward_from_message_id,
899
                    :forward_signature, :forward_sender_name, :forward_date,
900
                    :reply_to_chat, :reply_to_message, :media_group_id, :text, :entities, :audio, :document,
901
                    :animation, :game, :photo, :sticker, :video, :voice, :video_note, :caption, :contact,
902
                    :location, :venue, :poll, :new_chat_members, :left_chat_member,
903
                    :new_chat_title, :new_chat_photo, :delete_chat_photo, :group_chat_created,
904
                    :supergroup_chat_created, :channel_chat_created,
905
                    :migrate_from_chat_id, :migrate_to_chat_id, :pinned_message, :connected_website, :passport_data
906
                )
907
            ');
908
909 6
            $user_id = null;
910 6
            if ($user instanceof User) {
911 6
                $user_id = $user->getId();
912
            }
913 6
            $chat_id = $chat->getId();
914
915 6
            $reply_to_message    = $message->getReplyToMessage();
916 6
            $reply_to_message_id = null;
917 6
            if ($reply_to_message instanceof ReplyToMessage) {
918
                $reply_to_message_id = $reply_to_message->getMessageId();
919
                // please notice that, as explained in the documentation, reply_to_message don't contain other
920
                // reply_to_message field so recursion deep is 1
921
                self::insertMessageRequest($reply_to_message);
922
            }
923
924 6
            $sth->bindValue(':message_id', $message->getMessageId());
925 6
            $sth->bindValue(':chat_id', $chat_id);
926 6
            $sth->bindValue(':user_id', $user_id);
927 6
            $sth->bindValue(':date', $date);
928 6
            $sth->bindValue(':forward_from', $forward_from);
929 6
            $sth->bindValue(':forward_from_chat', $forward_from_chat);
930 6
            $sth->bindValue(':forward_from_message_id', $message->getForwardFromMessageId());
931 6
            $sth->bindValue(':forward_signature', $message->getForwardSignature());
932 6
            $sth->bindValue(':forward_sender_name', $message->getForwardSenderName());
933 6
            $sth->bindValue(':forward_date', $forward_date);
934
935 6
            $reply_to_chat_id = null;
936 6
            if ($reply_to_message_id !== null) {
937
                $reply_to_chat_id = $chat_id;
938
            }
939 6
            $sth->bindValue(':reply_to_chat', $reply_to_chat_id);
940 6
            $sth->bindValue(':reply_to_message', $reply_to_message_id);
941
942 6
            $sth->bindValue(':media_group_id', $message->getMediaGroupId());
943 6
            $sth->bindValue(':text', $message->getText());
944 6
            $sth->bindValue(':entities', $t = self::entitiesArrayToJson($message->getEntities(), null));
945 6
            $sth->bindValue(':audio', $message->getAudio());
946 6
            $sth->bindValue(':document', $message->getDocument());
947 6
            $sth->bindValue(':animation', $message->getAnimation());
948 6
            $sth->bindValue(':game', $message->getGame());
949 6
            $sth->bindValue(':photo', $t = self::entitiesArrayToJson($message->getPhoto(), null));
950 6
            $sth->bindValue(':sticker', $message->getSticker());
951 6
            $sth->bindValue(':video', $message->getVideo());
952 6
            $sth->bindValue(':voice', $message->getVoice());
953 6
            $sth->bindValue(':video_note', $message->getVideoNote());
954 6
            $sth->bindValue(':caption', $message->getCaption());
955 6
            $sth->bindValue(':contact', $message->getContact());
956 6
            $sth->bindValue(':location', $message->getLocation());
957 6
            $sth->bindValue(':venue', $message->getVenue());
958 6
            $sth->bindValue(':poll', $message->getPoll());
959 6
            $sth->bindValue(':new_chat_members', $new_chat_members_ids);
960 6
            $sth->bindValue(':left_chat_member', $left_chat_member_id);
961 6
            $sth->bindValue(':new_chat_title', $message->getNewChatTitle());
962 6
            $sth->bindValue(':new_chat_photo', $t = self::entitiesArrayToJson($message->getNewChatPhoto(), null));
963 6
            $sth->bindValue(':delete_chat_photo', $message->getDeleteChatPhoto());
964 6
            $sth->bindValue(':group_chat_created', $message->getGroupChatCreated());
965 6
            $sth->bindValue(':supergroup_chat_created', $message->getSupergroupChatCreated());
966 6
            $sth->bindValue(':channel_chat_created', $message->getChannelChatCreated());
967 6
            $sth->bindValue(':migrate_from_chat_id', $message->getMigrateFromChatId());
968 6
            $sth->bindValue(':migrate_to_chat_id', $message->getMigrateToChatId());
969 6
            $sth->bindValue(':pinned_message', $message->getPinnedMessage());
970 6
            $sth->bindValue(':connected_website', $message->getConnectedWebsite());
971 6
            $sth->bindValue(':passport_data', $message->getPassportData());
972
973 6
            return $sth->execute();
974
        } catch (PDOException $e) {
975
            throw new TelegramException($e->getMessage());
976
        }
977
    }
978
979
    /**
980
     * Insert Edited Message request in db
981
     *
982
     * @param Message $edited_message
983
     *
984
     * @return bool If the insert was successful
985
     * @throws TelegramException
986
     */
987
    public static function insertEditedMessageRequest(Message $edited_message)
988
    {
989
        if (!self::isDbConnected()) {
990
            return false;
991
        }
992
993
        try {
994
            $edit_date = self::getTimestamp($edited_message->getEditDate());
995
996
            // Insert chat
997
            $chat = $edited_message->getChat();
998
            self::insertChat($chat, $edit_date);
999
1000
            // Insert user and the relation with the chat
1001
            $user = $edited_message->getFrom();
1002
            if ($user instanceof User) {
1003
                self::insertUser($user, $edit_date, $chat);
1004
            }
1005
1006
            $sth = self::$pdo->prepare('
1007
                INSERT IGNORE INTO `' . TB_EDITED_MESSAGE . '`
1008
                (`chat_id`, `message_id`, `user_id`, `edit_date`, `text`, `entities`, `caption`)
1009
                VALUES
1010
                (:chat_id, :message_id, :user_id, :edit_date, :text, :entities, :caption)
1011
            ');
1012
1013
            $user_id = null;
1014
            if ($user instanceof User) {
1015
                $user_id = $user->getId();
1016
            }
1017
1018
            $sth->bindValue(':chat_id', $chat->getId());
1019
            $sth->bindValue(':message_id', $edited_message->getMessageId());
1020
            $sth->bindValue(':user_id', $user_id);
1021
            $sth->bindValue(':edit_date', $edit_date);
1022
            $sth->bindValue(':text', $edited_message->getText());
1023
            $sth->bindValue(':entities', self::entitiesArrayToJson($edited_message->getEntities(), null));
1024
            $sth->bindValue(':caption', $edited_message->getCaption());
1025
1026
            return $sth->execute();
1027
        } catch (PDOException $e) {
1028
            throw new TelegramException($e->getMessage());
1029
        }
1030
    }
1031
1032
    /**
1033
     * Select Groups, Supergroups, Channels and/or single user Chats (also by ID or text)
1034
     *
1035
     * @param $select_chats_params
1036
     *
1037
     * @return array|bool
1038
     * @throws TelegramException
1039
     */
1040
    public static function selectChats($select_chats_params)
1041
    {
1042
        if (!self::isDbConnected()) {
1043
            return false;
1044
        }
1045
1046
        // Set defaults for omitted values.
1047
        $select = array_merge([
1048
            'groups'      => true,
1049
            'supergroups' => true,
1050
            'channels'    => true,
1051
            'users'       => true,
1052
            'date_from'   => null,
1053
            'date_to'     => null,
1054
            'chat_id'     => null,
1055
            'text'        => null,
1056
        ], $select_chats_params);
1057
1058
        if (!$select['groups'] && !$select['users'] && !$select['supergroups'] && !$select['channels']) {
1059
            return false;
1060
        }
1061
1062
        try {
1063
            $query = '
1064
                SELECT * ,
1065
                ' . TB_CHAT . '.`id` AS `chat_id`,
1066
                ' . TB_CHAT . '.`username` AS `chat_username`,
1067
                ' . TB_CHAT . '.`created_at` AS `chat_created_at`,
1068
                ' . TB_CHAT . '.`updated_at` AS `chat_updated_at`
1069
            ';
1070
            if ($select['users']) {
1071
                $query .= '
1072
                    , ' . TB_USER . '.`id` AS `user_id`
1073
                    FROM `' . TB_CHAT . '`
1074
                    LEFT JOIN `' . TB_USER . '`
1075
                    ON ' . TB_CHAT . '.`id`=' . TB_USER . '.`id`
1076
                ';
1077
            } else {
1078
                $query .= 'FROM `' . TB_CHAT . '`';
1079
            }
1080
1081
            // Building parts of query
1082
            $where  = [];
1083
            $tokens = [];
1084
1085
            if (!$select['groups'] || !$select['users'] || !$select['supergroups'] || !$select['channels']) {
1086
                $chat_or_user = [];
1087
1088
                $select['groups'] && $chat_or_user[] = TB_CHAT . '.`type` = "group"';
1089
                $select['supergroups'] && $chat_or_user[] = TB_CHAT . '.`type` = "supergroup"';
1090
                $select['channels'] && $chat_or_user[] = TB_CHAT . '.`type` = "channel"';
1091
                $select['users'] && $chat_or_user[] = TB_CHAT . '.`type` = "private"';
1092
1093
                $where[] = '(' . implode(' OR ', $chat_or_user) . ')';
1094
            }
1095
1096
            if (null !== $select['date_from']) {
1097
                $where[]              = TB_CHAT . '.`updated_at` >= :date_from';
1098
                $tokens[':date_from'] = $select['date_from'];
1099
            }
1100
1101
            if (null !== $select['date_to']) {
1102
                $where[]            = TB_CHAT . '.`updated_at` <= :date_to';
1103
                $tokens[':date_to'] = $select['date_to'];
1104
            }
1105
1106
            if (null !== $select['chat_id']) {
1107
                $where[]            = TB_CHAT . '.`id` = :chat_id';
1108
                $tokens[':chat_id'] = $select['chat_id'];
1109
            }
1110
1111
            if (null !== $select['text']) {
1112
                $text_like = '%' . strtolower($select['text']) . '%';
1113
                if ($select['users']) {
1114
                    $where[]          = '(
1115
                        LOWER(' . TB_CHAT . '.`title`) LIKE :text1
1116
                        OR LOWER(' . TB_USER . '.`first_name`) LIKE :text2
1117
                        OR LOWER(' . TB_USER . '.`last_name`) LIKE :text3
1118
                        OR LOWER(' . TB_USER . '.`username`) LIKE :text4
1119
                    )';
1120
                    $tokens[':text1'] = $text_like;
1121
                    $tokens[':text2'] = $text_like;
1122
                    $tokens[':text3'] = $text_like;
1123
                    $tokens[':text4'] = $text_like;
1124
                } else {
1125
                    $where[]         = 'LOWER(' . TB_CHAT . '.`title`) LIKE :text';
1126
                    $tokens[':text'] = $text_like;
1127
                }
1128
            }
1129
1130
            if (!empty($where)) {
1131
                $query .= ' WHERE ' . implode(' AND ', $where);
1132
            }
1133
1134
            $query .= ' ORDER BY ' . TB_CHAT . '.`updated_at` ASC';
1135
1136
            $sth = self::$pdo->prepare($query);
1137
            $sth->execute($tokens);
1138
1139
            return $sth->fetchAll(PDO::FETCH_ASSOC);
1140
        } catch (PDOException $e) {
1141
            throw new TelegramException($e->getMessage());
1142
        }
1143
    }
1144
1145
    /**
1146
     * Get Telegram API request count for current chat / message
1147
     *
1148
     * @param integer $chat_id
1149
     * @param string  $inline_message_id
1150
     *
1151
     * @return array|bool Array containing TOTAL and CURRENT fields or false on invalid arguments
1152
     * @throws TelegramException
1153
     */
1154
    public static function getTelegramRequestCount($chat_id = null, $inline_message_id = null)
1155
    {
1156
        if (!self::isDbConnected()) {
1157
            return false;
1158
        }
1159
1160
        try {
1161
            $sth = self::$pdo->prepare('SELECT 
1162
                (SELECT COUNT(DISTINCT `chat_id`) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_1) AS LIMIT_PER_SEC_ALL,
1163
                (SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_2 AND ((`chat_id` = :chat_id_1 AND `inline_message_id` IS NULL) OR (`inline_message_id` = :inline_message_id AND `chat_id` IS NULL))) AS LIMIT_PER_SEC,
1164
                (SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_minute AND `chat_id` = :chat_id_2) AS LIMIT_PER_MINUTE
1165
            ');
1166
1167
            $date        = self::getTimestamp();
1168
            $date_minute = self::getTimestamp(strtotime('-1 minute'));
1169
1170
            $sth->bindValue(':chat_id_1', $chat_id);
1171
            $sth->bindValue(':chat_id_2', $chat_id);
1172
            $sth->bindValue(':inline_message_id', $inline_message_id);
1173
            $sth->bindValue(':created_at_1', $date);
1174
            $sth->bindValue(':created_at_2', $date);
1175
            $sth->bindValue(':created_at_minute', $date_minute);
1176
1177
            $sth->execute();
1178
1179
            return $sth->fetch();
1180
        } catch (Exception $e) {
1181
            throw new TelegramException($e->getMessage());
1182
        }
1183
    }
1184
1185
    /**
1186
     * Insert Telegram API request in db
1187
     *
1188
     * @param string $method
1189
     * @param array  $data
1190
     *
1191
     * @return bool If the insert was successful
1192
     * @throws TelegramException
1193
     */
1194
    public static function insertTelegramRequest($method, $data)
1195
    {
1196
        if (!self::isDbConnected()) {
1197
            return false;
1198
        }
1199
1200
        try {
1201
            $sth = self::$pdo->prepare('INSERT INTO `' . TB_REQUEST_LIMITER . '`
1202
                (`method`, `chat_id`, `inline_message_id`, `created_at`)
1203
                VALUES
1204
                (:method, :chat_id, :inline_message_id, :created_at);
1205
            ');
1206
1207
            $chat_id           = isset($data['chat_id']) ? $data['chat_id'] : null;
1208
            $inline_message_id = isset($data['inline_message_id']) ? $data['inline_message_id'] : null;
1209
1210
            $sth->bindValue(':chat_id', $chat_id);
1211
            $sth->bindValue(':inline_message_id', $inline_message_id);
1212
            $sth->bindValue(':method', $method);
1213
            $sth->bindValue(':created_at', self::getTimestamp());
1214
1215
            return $sth->execute();
1216
        } catch (Exception $e) {
1217
            throw new TelegramException($e->getMessage());
1218
        }
1219
    }
1220
1221
    /**
1222
     * Bulk update the entries of any table
1223
     *
1224
     * @param string $table
1225
     * @param array  $fields_values
1226
     * @param array  $where_fields_values
1227
     *
1228
     * @return bool
1229
     * @throws TelegramException
1230
     */
1231 3
    public static function update($table, array $fields_values, array $where_fields_values)
1232
    {
1233 3
        if (empty($fields_values) || !self::isDbConnected()) {
1234
            return false;
1235
        }
1236
1237
        try {
1238
            // Building parts of query
1239 3
            $tokens = $fields = $where = [];
1240
1241
            // Fields with values to update
1242 3 View Code Duplication
            foreach ($fields_values as $field => $value) {
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...
1243 3
                $token          = ':' . count($tokens);
1244 3
                $fields[]       = "`{$field}` = {$token}";
1245 3
                $tokens[$token] = $value;
1246
            }
1247
1248
            // Where conditions
1249 3 View Code Duplication
            foreach ($where_fields_values as $field => $value) {
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...
1250 3
                $token          = ':' . count($tokens);
1251 3
                $where[]        = "`{$field}` = {$token}";
1252 3
                $tokens[$token] = $value;
1253
            }
1254
1255 3
            $sql = 'UPDATE `' . $table . '` SET ' . implode(', ', $fields);
1256 3
            $sql .= count($where) > 0 ? ' WHERE ' . implode(' AND ', $where) : '';
1257
1258 3
            return self::$pdo->prepare($sql)->execute($tokens);
1259
        } catch (Exception $e) {
1260
            throw new TelegramException($e->getMessage());
1261
        }
1262
    }
1263
}
1264