Completed
Push — master ( 466468...8588b9 )
by Armando
02:38 queued 36s
created

DB::defineTables()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 21
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 18
nc 3
nop 0
dl 0
loc 21
ccs 6
cts 6
cp 1
crap 3
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the TelegramBot package.
5
 *
6
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 * Written by Marco Boretto <[email protected]>
11
 */
12
13
namespace Longman\TelegramBot;
14
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\Payments\PreCheckoutQuery;
21
use Longman\TelegramBot\Entities\Payments\ShippingQuery;
22
use Longman\TelegramBot\Entities\Poll;
23
use Longman\TelegramBot\Entities\ReplyToMessage;
24
use Longman\TelegramBot\Entities\Update;
25
use Longman\TelegramBot\Entities\User;
26
use Longman\TelegramBot\Exception\TelegramException;
27
use PDO;
28
use PDOException;
29
30
class DB
31
{
32
    /**
33
     * MySQL credentials
34
     *
35
     * @var array
36
     */
37
    protected static $mysql_credentials = [];
38
39
    /**
40
     * PDO object
41
     *
42
     * @var PDO
43
     */
44
    protected static $pdo;
45
46
    /**
47
     * Table prefix
48
     *
49
     * @var string
50
     */
51
    protected static $table_prefix;
52
53
    /**
54
     * Telegram class object
55
     *
56
     * @var Telegram
57
     */
58
    protected static $telegram;
59
60
    /**
61
     * Initialize
62
     *
63
     * @param array    $credentials  Database connection details
64
     * @param Telegram $telegram     Telegram object to connect with this object
65
     * @param string   $table_prefix Table prefix
66
     * @param string   $encoding     Database character encoding
67
     *
68
     * @return PDO PDO database object
69
     * @throws TelegramException
70
     */
71 9
    public static function initialize(
72
        array $credentials,
73
        Telegram $telegram,
74
        $table_prefix = null,
75
        $encoding = 'utf8mb4'
76
    ) {
77 9
        if (empty($credentials)) {
78
            throw new TelegramException('MySQL credentials not provided!');
79
        }
80
81 9
        $dsn = 'mysql:host=' . $credentials['host'] . ';dbname=' . $credentials['database'];
82 9
        if (!empty($credentials['port'])) {
83
            $dsn .= ';port=' . $credentials['port'];
84
        }
85
86 9
        $options = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $encoding];
87
        try {
88 9
            $pdo = new PDO($dsn, $credentials['user'], $credentials['password'], $options);
89 9
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
90
        } catch (PDOException $e) {
91
            throw new TelegramException($e->getMessage());
92
        }
93
94 9
        self::$pdo               = $pdo;
95 9
        self::$telegram          = $telegram;
96 9
        self::$mysql_credentials = $credentials;
97 9
        self::$table_prefix      = $table_prefix;
98
99 9
        self::defineTables();
100
101 9
        return self::$pdo;
102
    }
103
104
    /**
105
     * External Initialize
106
     *
107
     * Let you use the class with an external already existing Pdo Mysql connection.
108
     *
109
     * @param PDO      $external_pdo_connection PDO database object
110
     * @param Telegram $telegram                Telegram object to connect with this object
111
     * @param string   $table_prefix            Table prefix
112
     *
113
     * @return PDO PDO database object
114
     * @throws TelegramException
115
     */
116
    public static function externalInitialize(
117
        $external_pdo_connection,
118
        Telegram $telegram,
119
        $table_prefix = null
120
    ) {
121
        if ($external_pdo_connection === null) {
122
            throw new TelegramException('MySQL external connection not provided!');
123
        }
124
125
        self::$pdo               = $external_pdo_connection;
126
        self::$telegram          = $telegram;
127
        self::$mysql_credentials = [];
128
        self::$table_prefix      = $table_prefix;
129
130
        self::defineTables();
131
132
        return self::$pdo;
133
    }
134
135
    /**
136
     * Define all the tables with the proper prefix
137
     */
138 9
    protected static function defineTables()
139
    {
140
        $tables = [
141 9
            'callback_query',
142
            'chat',
143
            'chosen_inline_result',
144
            'edited_message',
145
            'inline_query',
146
            'message',
147
            'pre_checkout_query',
148
            'poll',
149
            'request_limiter',
150
            'shipping_query',
151
            'telegram_update',
152
            'user',
153
            'user_chat',
154
        ];
155 9
        foreach ($tables as $table) {
156 9
            $table_name = 'TB_' . strtoupper($table);
157 9
            if (!defined($table_name)) {
158 1
                define($table_name, self::$table_prefix . $table);
159
            }
160
        }
161 9
    }
162
163
    /**
164
     * Check if database connection has been created
165
     *
166
     * @return bool
167
     */
168 9
    public static function isDbConnected()
169
    {
170 9
        return self::$pdo !== null;
171
    }
172
173
    /**
174
     * Get the PDO object of the connected database
175
     *
176
     * @return PDO
177
     */
178
    public static function getPdo()
179
    {
180
        return self::$pdo;
181
    }
182
183
    /**
184
     * Fetch update(s) from DB
185
     *
186
     * @param int    $limit Limit the number of updates to fetch
187
     * @param string $id    Check for unique update id
188
     *
189
     * @return array|bool Fetched data or false if not connected
190
     * @throws TelegramException
191
     */
192
    public static function selectTelegramUpdate($limit = null, $id = null)
193
    {
194
        if (!self::isDbConnected()) {
195
            return false;
196
        }
197
198
        try {
199
            $sql = '
200
                SELECT `id`
201
                FROM `' . TB_TELEGRAM_UPDATE . '`
202
            ';
203
204
            if ($id !== null) {
205
                $sql .= ' WHERE `id` = :id';
206
            } else {
207
                $sql .= ' ORDER BY `id` DESC';
208
            }
209
210
            if ($limit !== null) {
211
                $sql .= ' LIMIT :limit';
212
            }
213
214
            $sth = self::$pdo->prepare($sql);
215
216
            if ($limit !== null) {
217
                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
218
            }
219
            if ($id !== null) {
220
                $sth->bindValue(':id', $id);
221
            }
222
223
            $sth->execute();
224
225
            return $sth->fetchAll(PDO::FETCH_ASSOC);
226
        } catch (PDOException $e) {
227
            throw new TelegramException($e->getMessage());
228
        }
229
    }
230
231
    /**
232
     * Fetch message(s) from DB
233
     *
234
     * @param int $limit Limit the number of messages to fetch
235
     *
236
     * @return array|bool Fetched data or false if not connected
237
     * @throws TelegramException
238
     */
239
    public static function selectMessages($limit = null)
240
    {
241
        if (!self::isDbConnected()) {
242
            return false;
243
        }
244
245
        try {
246
            $sql = '
247
                SELECT *
248
                FROM `' . TB_MESSAGE . '`
249
                ORDER BY `id` DESC
250
            ';
251
252
            if ($limit !== null) {
253
                $sql .= ' LIMIT :limit';
254
            }
255
256
            $sth = self::$pdo->prepare($sql);
257
258
            if ($limit !== null) {
259
                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
260
            }
261
262
            $sth->execute();
263
264
            return $sth->fetchAll(PDO::FETCH_ASSOC);
265
        } catch (PDOException $e) {
266
            throw new TelegramException($e->getMessage());
267
        }
268
    }
269
270
    /**
271
     * Convert from unix timestamp to timestamp
272
     *
273
     * @param int $time Unix timestamp (if empty, current timestamp is used)
274
     *
275
     * @return string
276
     */
277 7
    protected static function getTimestamp($time = null)
278
    {
279 7
        return date('Y-m-d H:i:s', $time ?: time());
280
    }
281
282
    /**
283
     * Convert array of Entity items to a JSON array
284
     *
285
     * @todo Find a better way, as json_* functions are very heavy
286
     *
287
     * @param array|null $entities
288
     * @param mixed      $default
289
     *
290
     * @return mixed
291
     */
292 6
    public static function entitiesArrayToJson($entities, $default = null)
293
    {
294 6
        if (!is_array($entities)) {
295 6
            return $default;
296
        }
297
298
        // Convert each Entity item into an object based on its JSON reflection
299
        $json_entities = array_map(function ($entity) {
300
            return json_decode($entity, true);
301
        }, $entities);
302
303
        return json_encode($json_entities);
304
    }
305
306
    /**
307
     * Insert entry to telegram_update table
308
     *
309
     * @param string      $update_id
310
     * @param string|null $chat_id
311
     * @param string|null $message_id
312
     * @param string|null $edited_message_id
313
     * @param string|null $channel_post_id
314
     * @param string|null $edited_channel_post_id
315
     * @param string|null $inline_query_id
316
     * @param string|null $chosen_inline_result_id
317
     * @param string|null $callback_query_id
318
     * @param string|null $shipping_query_id
319
     * @param string|null $pre_checkout_query_id
320
     * @param string|null $poll_id
321
     *
322
     * @return bool If the insert was successful
323
     * @throws TelegramException
324
     */
325
    protected static function insertTelegramUpdate(
326
        $update_id,
327
        $chat_id = null,
328
        $message_id = null,
329
        $edited_message_id = null,
330
        $channel_post_id = null,
331
        $edited_channel_post_id = null,
332
        $inline_query_id = null,
333
        $chosen_inline_result_id = null,
334
        $callback_query_id = null,
335
        $shipping_query_id = null,
336
        $pre_checkout_query_id = null,
337
        $poll_id = null
338
    ) {
339
        if ($message_id === null && $edited_message_id === null && $channel_post_id === null && $edited_channel_post_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $shipping_query_id === null && $pre_checkout_query_id === null && $poll_id === null) {
340
            throw new TelegramException('message_id, edited_message_id, channel_post_id, edited_channel_post_id, inline_query_id, chosen_inline_result_id, callback_query_id, shipping_query_id, pre_checkout_query_id, poll_id are all null');
341
        }
342
343
        if (!self::isDbConnected()) {
344
            return false;
345
        }
346
347
        try {
348
            $sth = self::$pdo->prepare('
349
                INSERT IGNORE INTO `' . TB_TELEGRAM_UPDATE . '`
350
                (`id`, `chat_id`, `message_id`, `edited_message_id`, `channel_post_id`, `edited_channel_post_id`, `inline_query_id`, `chosen_inline_result_id`, `callback_query_id`, `shipping_query_id`, `pre_checkout_query_id`, `poll_id`)
351
                VALUES
352
                (:id, :chat_id, :message_id, :edited_message_id, :channel_post_id, :edited_channel_post_id, :inline_query_id, :chosen_inline_result_id, :callback_query_id, :shipping_query_id, :pre_checkout_query_id, :poll_id)
353
            ');
354
355
            $sth->bindValue(':id', $update_id);
356
            $sth->bindValue(':chat_id', $chat_id);
357
            $sth->bindValue(':message_id', $message_id);
358
            $sth->bindValue(':edited_message_id', $edited_message_id);
359
            $sth->bindValue(':channel_post_id', $channel_post_id);
360
            $sth->bindValue(':edited_channel_post_id', $edited_channel_post_id);
361
            $sth->bindValue(':inline_query_id', $inline_query_id);
362
            $sth->bindValue(':chosen_inline_result_id', $chosen_inline_result_id);
363
            $sth->bindValue(':callback_query_id', $callback_query_id);
364
            $sth->bindValue(':shipping_query_id', $shipping_query_id);
365
            $sth->bindValue(':pre_checkout_query_id', $pre_checkout_query_id);
366
            $sth->bindValue(':poll_id', $poll_id);
367
368
            return $sth->execute();
369
        } catch (PDOException $e) {
370
            throw new TelegramException($e->getMessage());
371
        }
372
    }
373
374
    /**
375
     * Insert users and save their connection to chats
376
     *
377
     * @param User   $user
378
     * @param string $date
379
     * @param Chat   $chat
380
     *
381
     * @return bool If the insert was successful
382
     * @throws TelegramException
383
     */
384 6
    public static function insertUser(User $user, $date = null, Chat $chat = null)
385
    {
386 6
        if (!self::isDbConnected()) {
387
            return false;
388
        }
389
390
        try {
391 6
            $sth = self::$pdo->prepare('
392 6
                INSERT INTO `' . TB_USER . '`
393
                (`id`, `is_bot`, `username`, `first_name`, `last_name`, `language_code`, `created_at`, `updated_at`)
394
                VALUES
395
                (:id, :is_bot, :username, :first_name, :last_name, :language_code, :created_at, :updated_at)
396
                ON DUPLICATE KEY UPDATE
397
                    `is_bot`         = VALUES(`is_bot`),
398
                    `username`       = VALUES(`username`),
399
                    `first_name`     = VALUES(`first_name`),
400
                    `last_name`      = VALUES(`last_name`),
401
                    `language_code`  = VALUES(`language_code`),
402
                    `updated_at`     = VALUES(`updated_at`)
403
            ');
404
405 6
            $sth->bindValue(':id', $user->getId());
406 6
            $sth->bindValue(':is_bot', $user->getIsBot(), PDO::PARAM_INT);
407 6
            $sth->bindValue(':username', $user->getUsername());
408 6
            $sth->bindValue(':first_name', $user->getFirstName());
409 6
            $sth->bindValue(':last_name', $user->getLastName());
410 6
            $sth->bindValue(':language_code', $user->getLanguageCode());
411 6
            $date = $date ?: self::getTimestamp();
412 6
            $sth->bindValue(':created_at', $date);
413 6
            $sth->bindValue(':updated_at', $date);
414
415 6
            $status = $sth->execute();
416
        } catch (PDOException $e) {
417
            throw new TelegramException($e->getMessage());
418
        }
419
420
        // Also insert the relationship to the chat into the user_chat table
421 6
        if ($chat instanceof Chat) {
422
            try {
423 6
                $sth = self::$pdo->prepare('
424 6
                    INSERT IGNORE INTO `' . TB_USER_CHAT . '`
425
                    (`user_id`, `chat_id`)
426
                    VALUES
427
                    (:user_id, :chat_id)
428
                ');
429
430 6
                $sth->bindValue(':user_id', $user->getId());
431 6
                $sth->bindValue(':chat_id', $chat->getId());
432
433 6
                $status = $sth->execute();
434
            } catch (PDOException $e) {
435
                throw new TelegramException($e->getMessage());
436
            }
437
        }
438
439 6
        return $status;
440
    }
441
442
    /**
443
     * Insert chat
444
     *
445
     * @param Chat   $chat
446
     * @param string $date
447
     * @param string $migrate_to_chat_id
448
     *
449
     * @return bool If the insert was successful
450
     * @throws TelegramException
451
     */
452 6
    public static function insertChat(Chat $chat, $date = null, $migrate_to_chat_id = null)
453
    {
454 6
        if (!self::isDbConnected()) {
455
            return false;
456
        }
457
458
        try {
459 6
            $sth = self::$pdo->prepare('
460 6
                INSERT IGNORE INTO `' . TB_CHAT . '`
461
                (`id`, `type`, `title`, `username`, `first_name`, `last_name`, `all_members_are_administrators`, `created_at` ,`updated_at`, `old_id`)
462
                VALUES
463
                (:id, :type, :title, :username, :first_name, :last_name, :all_members_are_administrators, :created_at, :updated_at, :old_id)
464
                ON DUPLICATE KEY UPDATE
465
                    `type`                           = VALUES(`type`),
466
                    `title`                          = VALUES(`title`),
467
                    `username`                       = VALUES(`username`),
468
                    `first_name`                     = VALUES(`first_name`),
469
                    `last_name`                      = VALUES(`last_name`),
470
                    `all_members_are_administrators` = VALUES(`all_members_are_administrators`),
471
                    `updated_at`                     = VALUES(`updated_at`)
472
            ');
473
474 6
            $chat_id   = $chat->getId();
475 6
            $chat_type = $chat->getType();
476
477 6
            if ($migrate_to_chat_id !== null) {
478
                $chat_type = 'supergroup';
479
480
                $sth->bindValue(':id', $migrate_to_chat_id);
481
                $sth->bindValue(':old_id', $chat_id);
482
            } else {
483 6
                $sth->bindValue(':id', $chat_id);
484 6
                $sth->bindValue(':old_id', $migrate_to_chat_id);
485
            }
486
487 6
            $sth->bindValue(':type', $chat_type);
488 6
            $sth->bindValue(':title', $chat->getTitle());
489 6
            $sth->bindValue(':username', $chat->getUsername());
490 6
            $sth->bindValue(':first_name', $chat->getFirstName());
491 6
            $sth->bindValue(':last_name', $chat->getLastName());
492 6
            $sth->bindValue(':all_members_are_administrators', $chat->getAllMembersAreAdministrators(), PDO::PARAM_INT);
0 ignored issues
show
Bug introduced by
The method getAllMembersAreAdministrators() does not exist on Longman\TelegramBot\Entities\Chat. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

492
            $sth->bindValue(':all_members_are_administrators', $chat->/** @scrutinizer ignore-call */ getAllMembersAreAdministrators(), PDO::PARAM_INT);
Loading history...
493 6
            $date = $date ?: self::getTimestamp();
494 6
            $sth->bindValue(':created_at', $date);
495 6
            $sth->bindValue(':updated_at', $date);
496
497 6
            return $sth->execute();
498
        } catch (PDOException $e) {
499
            throw new TelegramException($e->getMessage());
500
        }
501
    }
502
503
    /**
504
     * Insert request into database
505
     *
506
     * @todo self::$pdo->lastInsertId() - unsafe usage if expected previous insert fails?
507
     *
508
     * @param Update $update
509
     *
510
     * @return bool
511
     * @throws TelegramException
512
     */
513
    public static function insertRequest(Update $update)
514
    {
515
        if (!self::isDbConnected()) {
516
            return false;
517
        }
518
519
        $chat_id                 = null;
520
        $message_id              = null;
521
        $edited_message_id       = null;
522
        $channel_post_id         = null;
523
        $edited_channel_post_id  = null;
524
        $inline_query_id         = null;
525
        $chosen_inline_result_id = null;
526
        $callback_query_id       = null;
527
        $shipping_query_id       = null;
528
        $pre_checkout_query_id   = null;
529
        $poll_id                 = null;
530
531
        if (($message = $update->getMessage()) && self::insertMessageRequest($message)) {
532
            $chat_id    = $message->getChat()->getId();
533
            $message_id = $message->getMessageId();
534
        } elseif (($edited_message = $update->getEditedMessage()) && self::insertEditedMessageRequest($edited_message)) {
535
            $chat_id           = $edited_message->getChat()->getId();
536
            $edited_message_id = self::$pdo->lastInsertId();
537
        } elseif (($channel_post = $update->getChannelPost()) && self::insertMessageRequest($channel_post)) {
538
            $chat_id         = $channel_post->getChat()->getId();
539
            $channel_post_id = $channel_post->getMessageId();
540
        } elseif (($edited_channel_post = $update->getEditedChannelPost()) && self::insertEditedMessageRequest($edited_channel_post)) {
541
            $chat_id                = $edited_channel_post->getChat()->getId();
542
            $edited_channel_post_id = self::$pdo->lastInsertId();
543
        } elseif (($inline_query = $update->getInlineQuery()) && self::insertInlineQueryRequest($inline_query)) {
544
            $inline_query_id = $inline_query->getId();
545
        } elseif (($chosen_inline_result = $update->getChosenInlineResult()) && self::insertChosenInlineResultRequest($chosen_inline_result)) {
546
            $chosen_inline_result_id = self::$pdo->lastInsertId();
547
        } elseif (($callback_query = $update->getCallbackQuery()) && self::insertCallbackQueryRequest($callback_query)) {
548
            $callback_query_id = $callback_query->getId();
549
        } elseif (($shipping_query = $update->getShippingQuery()) && self::insertShippingQueryRequest($shipping_query)) {
550
            $shipping_query_id = $shipping_query->getId();
551
        } elseif (($pre_checkout_query = $update->getPreCheckoutQuery()) && self::insertPreCheckoutQueryRequest($pre_checkout_query)) {
552
            $pre_checkout_query_id = $pre_checkout_query->getId();
553
        } elseif (($poll = $update->getPoll()) && self::insertPollRequest($poll)) {
554
            $poll_id = $poll->getId();
555
        } else {
556
            return false;
557
        }
558
559
        return self::insertTelegramUpdate(
560
            $update->getUpdateId(),
561
            $chat_id,
562
            $message_id,
563
            $edited_message_id,
564
            $channel_post_id,
565
            $edited_channel_post_id,
566
            $inline_query_id,
567
            $chosen_inline_result_id,
568
            $callback_query_id,
569
            $shipping_query_id,
570
            $pre_checkout_query_id,
571
            $poll_id
572
        );
573
    }
574
575
    /**
576
     * Insert inline query request into database
577
     *
578
     * @param InlineQuery $inline_query
579
     *
580
     * @return bool If the insert was successful
581
     * @throws TelegramException
582
     */
583
    public static function insertInlineQueryRequest(InlineQuery $inline_query)
584
    {
585
        if (!self::isDbConnected()) {
586
            return false;
587
        }
588
589
        try {
590
            $sth = self::$pdo->prepare('
591
                INSERT IGNORE INTO `' . TB_INLINE_QUERY . '`
592
                (`id`, `user_id`, `location`, `query`, `offset`, `created_at`)
593
                VALUES
594
                (:id, :user_id, :location, :query, :offset, :created_at)
595
            ');
596
597
            $date    = self::getTimestamp();
598
            $user_id = null;
599
600
            $user = $inline_query->getFrom();
601
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
602
                $user_id = $user->getId();
603
                self::insertUser($user, $date);
604
            }
605
606
            $sth->bindValue(':id', $inline_query->getId());
607
            $sth->bindValue(':user_id', $user_id);
608
            $sth->bindValue(':location', $inline_query->getLocation());
609
            $sth->bindValue(':query', $inline_query->getQuery());
610
            $sth->bindValue(':offset', $inline_query->getOffset());
611
            $sth->bindValue(':created_at', $date);
612
613
            return $sth->execute();
614
        } catch (PDOException $e) {
615
            throw new TelegramException($e->getMessage());
616
        }
617
    }
618
619
    /**
620
     * Insert chosen inline result request into database
621
     *
622
     * @param ChosenInlineResult $chosen_inline_result
623
     *
624
     * @return bool If the insert was successful
625
     * @throws TelegramException
626
     */
627
    public static function insertChosenInlineResultRequest(ChosenInlineResult $chosen_inline_result)
628
    {
629
        if (!self::isDbConnected()) {
630
            return false;
631
        }
632
633
        try {
634
            $sth = self::$pdo->prepare('
635
                INSERT INTO `' . TB_CHOSEN_INLINE_RESULT . '`
636
                (`result_id`, `user_id`, `location`, `inline_message_id`, `query`, `created_at`)
637
                VALUES
638
                (:result_id, :user_id, :location, :inline_message_id, :query, :created_at)
639
            ');
640
641
            $date    = self::getTimestamp();
642
            $user_id = null;
643
644
            $user = $chosen_inline_result->getFrom();
645
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
646
                $user_id = $user->getId();
647
                self::insertUser($user, $date);
648
            }
649
650
            $sth->bindValue(':result_id', $chosen_inline_result->getResultId());
651
            $sth->bindValue(':user_id', $user_id);
652
            $sth->bindValue(':location', $chosen_inline_result->getLocation());
653
            $sth->bindValue(':inline_message_id', $chosen_inline_result->getInlineMessageId());
654
            $sth->bindValue(':query', $chosen_inline_result->getQuery());
655
            $sth->bindValue(':created_at', $date);
656
657
            return $sth->execute();
658
        } catch (PDOException $e) {
659
            throw new TelegramException($e->getMessage());
660
        }
661
    }
662
663
    /**
664
     * Insert callback query request into database
665
     *
666
     * @param CallbackQuery $callback_query
667
     *
668
     * @return bool If the insert was successful
669
     * @throws TelegramException
670
     */
671
    public static function insertCallbackQueryRequest(CallbackQuery $callback_query)
672
    {
673
        if (!self::isDbConnected()) {
674
            return false;
675
        }
676
677
        try {
678
            $sth = self::$pdo->prepare('
679
                INSERT IGNORE INTO `' . TB_CALLBACK_QUERY . '`
680
                (`id`, `user_id`, `chat_id`, `message_id`, `inline_message_id`, `chat_instance`, `data`, `game_short_name`, `created_at`)
681
                VALUES
682
                (:id, :user_id, :chat_id, :message_id, :inline_message_id, :chat_instance, :data, :game_short_name, :created_at)
683
            ');
684
685
            $date    = self::getTimestamp();
686
            $user_id = null;
687
688
            $user = $callback_query->getFrom();
689
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
690
                $user_id = $user->getId();
691
                self::insertUser($user, $date);
692
            }
693
694
            $message    = $callback_query->getMessage();
695
            $chat_id    = null;
696
            $message_id = null;
697
            if ($message instanceof Message) {
0 ignored issues
show
introduced by
$message is always a sub-type of Longman\TelegramBot\Entities\Message.
Loading history...
698
                $chat_id    = $message->getChat()->getId();
699
                $message_id = $message->getMessageId();
700
701
                $is_message = self::$pdo->query('
702
                    SELECT *
703
                    FROM `' . TB_MESSAGE . '`
704
                    WHERE `id` = ' . $message_id . '
705
                      AND `chat_id` = ' . $chat_id . '
706
                    LIMIT 1
707
                ')->rowCount();
708
709
                if ($is_message) {
710
                    self::insertEditedMessageRequest($message);
711
                } else {
712
                    self::insertMessageRequest($message);
713
                }
714
            }
715
716
            $sth->bindValue(':id', $callback_query->getId());
717
            $sth->bindValue(':user_id', $user_id);
718
            $sth->bindValue(':chat_id', $chat_id);
719
            $sth->bindValue(':message_id', $message_id);
720
            $sth->bindValue(':inline_message_id', $callback_query->getInlineMessageId());
721
            $sth->bindValue(':chat_instance', $callback_query->getChatInstance());
722
            $sth->bindValue(':data', $callback_query->getData());
723
            $sth->bindValue(':game_short_name', $callback_query->getGameShortName());
724
            $sth->bindValue(':created_at', $date);
725
726
            return $sth->execute();
727
        } catch (PDOException $e) {
728
            throw new TelegramException($e->getMessage());
729
        }
730
    }
731
732
    /**
733
     * Insert shipping query request into database
734
     *
735
     * @param ShippingQuery $shipping_query
736
     *
737
     * @return bool If the insert was successful
738
     * @throws TelegramException
739
     */
740
    public static function insertShippingQueryRequest(ShippingQuery $shipping_query)
741
    {
742
        if (!self::isDbConnected()) {
743
            return false;
744
        }
745
746
        try {
747
            $sth = self::$pdo->prepare('
748
                INSERT IGNORE INTO `' . TB_SHIPPING_QUERY . '`
749
                (`id`, `user_id`, `invoice_payload`, `shipping_address`, `created_at`)
750
                VALUES
751
                (:id, :user_id, :invoice_payload, :shipping_address, :created_at)
752
            ');
753
754
            $date    = self::getTimestamp();
755
            $user_id = null;
756
757
            $user = $shipping_query->getFrom();
758
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
759
                $user_id = $user->getId();
760
                self::insertUser($user, $date);
761
            }
762
763
            $sth->bindValue(':id', $shipping_query->getId());
764
            $sth->bindValue(':user_id', $user_id);
765
            $sth->bindValue(':invoice_payload', $shipping_query->getInvoicePayload());
766
            $sth->bindValue(':shipping_address', $shipping_query->getShippingAddress());
767
            $sth->bindValue(':created_at', $date);
768
769
            return $sth->execute();
770
        } catch (PDOException $e) {
771
            throw new TelegramException($e->getMessage());
772
        }
773
    }
774
775
    /**
776
     * Insert pre checkout query request into database
777
     *
778
     * @param PreCheckoutQuery $pre_checkout_query
779
     *
780
     * @return bool If the insert was successful
781
     * @throws TelegramException
782
     */
783
    public static function insertPreCheckoutQueryRequest(PreCheckoutQuery $pre_checkout_query)
784
    {
785
        if (!self::isDbConnected()) {
786
            return false;
787
        }
788
789
        try {
790
            $sth = self::$pdo->prepare('
791
                INSERT IGNORE INTO `' . TB_PRE_CHECKOUT_QUERY . '`
792
                (`id`, `user_id`, `currency`, `total_amount`, `invoice_payload`, `shipping_option_id`, `order_info`, `created_at`)
793
                VALUES
794
                (:id, :user_id, :currency, :total_amount, :invoice_payload, :shipping_option_id, :order_info, :created_at)
795
            ');
796
797
            $date    = self::getTimestamp();
798
            $user_id = null;
799
800
            $user = $pre_checkout_query->getFrom();
801
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
802
                $user_id = $user->getId();
803
                self::insertUser($user, $date);
804
            }
805
806
            $sth->bindValue(':id', $pre_checkout_query->getId());
807
            $sth->bindValue(':user_id', $user_id);
808
            $sth->bindValue(':currency', $pre_checkout_query->getCurrency());
809
            $sth->bindValue(':total_amount', $pre_checkout_query->getTotalAmount());
810
            $sth->bindValue(':invoice_payload', $pre_checkout_query->getInvoicePayload());
811
            $sth->bindValue(':shipping_option_id', $pre_checkout_query->getShippingOptionId());
812
            $sth->bindValue(':order_info', $pre_checkout_query->getOrderInfo());
813
            $sth->bindValue(':created_at', $date);
814
815
            return $sth->execute();
816
        } catch (PDOException $e) {
817
            throw new TelegramException($e->getMessage());
818
        }
819
    }
820
821
    /**
822
     * Insert poll request into database
823
     *
824
     * @param Poll $poll
825
     *
826
     * @return bool If the insert was successful
827
     * @throws TelegramException
828
     */
829
    public static function insertPollRequest(Poll $poll)
830
    {
831
        if (!self::isDbConnected()) {
832
            return false;
833
        }
834
835
        try {
836
            $sth = self::$pdo->prepare('
837
                INSERT INTO `' . TB_POLL . '`
838
                (`id`, `question`, `options`, `is_closed`, `created_at`)
839
                VALUES
840
                (:id, :question, :options, :is_closed, :created_at)
841
                ON DUPLICATE KEY UPDATE
842
                    `options`   = VALUES(`options`),
843
                    `is_closed` = VALUES(`is_closed`)
844
            ');
845
846
            $sth->bindValue(':id', $poll->getId());
847
            $sth->bindValue(':question', $poll->getQuestion());
848
            $sth->bindValue(':options', self::entitiesArrayToJson($poll->getOptions() ?: null));
849
            $sth->bindValue(':is_closed', $poll->getIsClosed(), PDO::PARAM_INT);
850
            $sth->bindValue(':created_at', self::getTimestamp());
851
852
            return $sth->execute();
853
        } catch (PDOException $e) {
854
            throw new TelegramException($e->getMessage());
855
        }
856
    }
857
858
    /**
859
     * Insert Message request in db
860
     *
861
     * @param Message $message
862
     *
863
     * @return bool If the insert was successful
864
     * @throws TelegramException
865
     */
866 6
    public static function insertMessageRequest(Message $message)
867
    {
868 6
        if (!self::isDbConnected()) {
869
            return false;
870
        }
871
872 6
        $date = self::getTimestamp($message->getDate());
873
874
        // Insert chat, update chat id in case it migrated
875 6
        $chat = $message->getChat();
876 6
        self::insertChat($chat, $date, $message->getMigrateToChatId());
877
878
        // Insert user and the relation with the chat
879 6
        $user = $message->getFrom();
880 6
        if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
881 6
            self::insertUser($user, $date, $chat);
882
        }
883
884
        // Insert the forwarded message user in users table
885 6
        $forward_date = $message->getForwardDate() ? self::getTimestamp($message->getForwardDate()) : null;
886
887 6
        $forward_from = $message->getForwardFrom();
888 6
        if ($forward_from instanceof User) {
0 ignored issues
show
introduced by
$forward_from is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
889
            self::insertUser($forward_from);
890
            $forward_from = $forward_from->getId();
891
        }
892 6
        $forward_from_chat = $message->getForwardFromChat();
893 6
        if ($forward_from_chat instanceof Chat) {
0 ignored issues
show
introduced by
$forward_from_chat is always a sub-type of Longman\TelegramBot\Entities\Chat.
Loading history...
894
            self::insertChat($forward_from_chat);
895
            $forward_from_chat = $forward_from_chat->getId();
896
        }
897
898
        // New and left chat member
899 6
        $new_chat_members_ids = null;
900 6
        $left_chat_member_id  = null;
901
902 6
        $new_chat_members = $message->getNewChatMembers();
0 ignored issues
show
Bug introduced by
The method getNewChatMembers() does not exist on Longman\TelegramBot\Entities\Message. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

902
        /** @scrutinizer ignore-call */ 
903
        $new_chat_members = $message->getNewChatMembers();
Loading history...
903 6
        $left_chat_member = $message->getLeftChatMember();
904 6
        if (!empty($new_chat_members)) {
905
            foreach ($new_chat_members as $new_chat_member) {
906
                if ($new_chat_member instanceof User) {
907
                    // Insert the new chat user
908
                    self::insertUser($new_chat_member, $date, $chat);
909
                    $new_chat_members_ids[] = $new_chat_member->getId();
910
                }
911
            }
912
            $new_chat_members_ids = implode(',', $new_chat_members_ids);
913 6
        } elseif ($left_chat_member instanceof User) {
0 ignored issues
show
introduced by
$left_chat_member is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
914
            // Insert the left chat user
915
            self::insertUser($left_chat_member, $date, $chat);
916
            $left_chat_member_id = $left_chat_member->getId();
917
        }
918
919
        try {
920 6
            $sth = self::$pdo->prepare('
921 6
                INSERT IGNORE INTO `' . TB_MESSAGE . '`
922
                (
923
                    `id`, `user_id`, `chat_id`, `date`, `forward_from`, `forward_from_chat`, `forward_from_message_id`,
924
                    `forward_signature`, `forward_sender_name`, `forward_date`,
925
                    `reply_to_chat`, `reply_to_message`, `edit_date`, `media_group_id`, `author_signature`, `text`, `entities`, `caption_entities`,
926
                    `audio`, `document`, `animation`, `game`, `photo`, `sticker`, `video`, `voice`, `video_note`, `caption`, `contact`,
927
                    `location`, `venue`, `poll`, `new_chat_members`, `left_chat_member`,
928
                    `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`,
929
                    `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`,
930
                    `pinned_message`, `invoice`, `successful_payment`, `connected_website`, `passport_data`, `reply_markup`
931
                ) VALUES (
932
                    :message_id, :user_id, :chat_id, :date, :forward_from, :forward_from_chat, :forward_from_message_id,
933
                    :forward_signature, :forward_sender_name, :forward_date,
934
                    :reply_to_chat, :reply_to_message, :edit_date, :media_group_id, :author_signature, :text, :entities, :caption_entities,
935
                    :audio, :document, :animation, :game, :photo, :sticker, :video, :voice, :video_note, :caption, :contact,
936
                    :location, :venue, :poll, :new_chat_members, :left_chat_member,
937
                    :new_chat_title, :new_chat_photo, :delete_chat_photo, :group_chat_created,
938
                    :supergroup_chat_created, :channel_chat_created, :migrate_to_chat_id, :migrate_from_chat_id,
939
                    :pinned_message, :invoice, :successful_payment, :connected_website, :passport_data, :reply_markup
940
                )
941
            ');
942
943 6
            $user_id = null;
944 6
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
945 6
                $user_id = $user->getId();
946
            }
947 6
            $chat_id = $chat->getId();
948
949 6
            $reply_to_message    = $message->getReplyToMessage();
950 6
            $reply_to_message_id = null;
951 6
            if ($reply_to_message instanceof ReplyToMessage) {
952
                $reply_to_message_id = $reply_to_message->getMessageId();
953
                // please notice that, as explained in the documentation, reply_to_message don't contain other
954
                // reply_to_message field so recursion deep is 1
955
                self::insertMessageRequest($reply_to_message);
956
            }
957
958 6
            $sth->bindValue(':message_id', $message->getMessageId());
959 6
            $sth->bindValue(':chat_id', $chat_id);
960 6
            $sth->bindValue(':user_id', $user_id);
961 6
            $sth->bindValue(':date', $date);
962 6
            $sth->bindValue(':forward_from', $forward_from);
963 6
            $sth->bindValue(':forward_from_chat', $forward_from_chat);
964 6
            $sth->bindValue(':forward_from_message_id', $message->getForwardFromMessageId());
965 6
            $sth->bindValue(':forward_signature', $message->getForwardSignature());
966 6
            $sth->bindValue(':forward_sender_name', $message->getForwardSenderName());
967 6
            $sth->bindValue(':forward_date', $forward_date);
968
969 6
            $reply_to_chat_id = null;
970 6
            if ($reply_to_message_id !== null) {
971
                $reply_to_chat_id = $chat_id;
972
            }
973 6
            $sth->bindValue(':reply_to_chat', $reply_to_chat_id);
974 6
            $sth->bindValue(':reply_to_message', $reply_to_message_id);
975
976 6
            $sth->bindValue(':edit_date', $message->getEditDate());
977 6
            $sth->bindValue(':media_group_id', $message->getMediaGroupId());
978 6
            $sth->bindValue(':author_signature', $message->getAuthorSignature());
979 6
            $sth->bindValue(':text', $message->getText());
980 6
            $sth->bindValue(':entities', self::entitiesArrayToJson($message->getEntities() ?: null));
981 6
            $sth->bindValue(':caption_entities', self::entitiesArrayToJson($message->getCaptionEntities() ?: null));
982 6
            $sth->bindValue(':audio', $message->getAudio());
983 6
            $sth->bindValue(':document', $message->getDocument());
984 6
            $sth->bindValue(':animation', $message->getAnimation());
985 6
            $sth->bindValue(':game', $message->getGame());
986 6
            $sth->bindValue(':photo', self::entitiesArrayToJson($message->getPhoto() ?: null));
987 6
            $sth->bindValue(':sticker', $message->getSticker());
988 6
            $sth->bindValue(':video', $message->getVideo());
989 6
            $sth->bindValue(':voice', $message->getVoice());
990 6
            $sth->bindValue(':video_note', $message->getVideoNote());
991 6
            $sth->bindValue(':caption', $message->getCaption());
992 6
            $sth->bindValue(':contact', $message->getContact());
993 6
            $sth->bindValue(':location', $message->getLocation());
994 6
            $sth->bindValue(':venue', $message->getVenue());
995 6
            $sth->bindValue(':poll', $message->getPoll());
996 6
            $sth->bindValue(':new_chat_members', $new_chat_members_ids);
997 6
            $sth->bindValue(':left_chat_member', $left_chat_member_id);
998 6
            $sth->bindValue(':new_chat_title', $message->getNewChatTitle());
999 6
            $sth->bindValue(':new_chat_photo', self::entitiesArrayToJson($message->getNewChatPhoto() ?: null));
1000 6
            $sth->bindValue(':delete_chat_photo', $message->getDeleteChatPhoto());
1001 6
            $sth->bindValue(':group_chat_created', $message->getGroupChatCreated());
1002 6
            $sth->bindValue(':supergroup_chat_created', $message->getSupergroupChatCreated());
1003 6
            $sth->bindValue(':channel_chat_created', $message->getChannelChatCreated());
1004 6
            $sth->bindValue(':migrate_to_chat_id', $message->getMigrateToChatId());
1005 6
            $sth->bindValue(':migrate_from_chat_id', $message->getMigrateFromChatId());
1006 6
            $sth->bindValue(':pinned_message', $message->getPinnedMessage());
1007 6
            $sth->bindValue(':invoice', $message->getInvoice());
1008 6
            $sth->bindValue(':successful_payment', $message->getSuccessfulPayment());
1009 6
            $sth->bindValue(':connected_website', $message->getConnectedWebsite());
1010 6
            $sth->bindValue(':passport_data', $message->getPassportData());
1011 6
            $sth->bindValue(':reply_markup', $message->getReplyMarkup());
1012
1013 6
            return $sth->execute();
1014
        } catch (PDOException $e) {
1015
            throw new TelegramException($e->getMessage());
1016
        }
1017
    }
1018
1019
    /**
1020
     * Insert Edited Message request in db
1021
     *
1022
     * @param Message $edited_message
1023
     *
1024
     * @return bool If the insert was successful
1025
     * @throws TelegramException
1026
     */
1027
    public static function insertEditedMessageRequest(Message $edited_message)
1028
    {
1029
        if (!self::isDbConnected()) {
1030
            return false;
1031
        }
1032
1033
        try {
1034
            $edit_date = self::getTimestamp($edited_message->getEditDate());
1035
1036
            // Insert chat
1037
            $chat = $edited_message->getChat();
1038
            self::insertChat($chat, $edit_date);
1039
1040
            // Insert user and the relation with the chat
1041
            $user = $edited_message->getFrom();
1042
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
1043
                self::insertUser($user, $edit_date, $chat);
1044
            }
1045
1046
            $sth = self::$pdo->prepare('
1047
                INSERT IGNORE INTO `' . TB_EDITED_MESSAGE . '`
1048
                (`chat_id`, `message_id`, `user_id`, `edit_date`, `text`, `entities`, `caption`)
1049
                VALUES
1050
                (:chat_id, :message_id, :user_id, :edit_date, :text, :entities, :caption)
1051
            ');
1052
1053
            $user_id = null;
1054
            if ($user instanceof User) {
0 ignored issues
show
introduced by
$user is always a sub-type of Longman\TelegramBot\Entities\User.
Loading history...
1055
                $user_id = $user->getId();
1056
            }
1057
1058
            $sth->bindValue(':chat_id', $chat->getId());
1059
            $sth->bindValue(':message_id', $edited_message->getMessageId());
1060
            $sth->bindValue(':user_id', $user_id);
1061
            $sth->bindValue(':edit_date', $edit_date);
1062
            $sth->bindValue(':text', $edited_message->getText());
1063
            $sth->bindValue(':entities', self::entitiesArrayToJson($edited_message->getEntities() ?: null));
1064
            $sth->bindValue(':caption', $edited_message->getCaption());
1065
1066
            return $sth->execute();
1067
        } catch (PDOException $e) {
1068
            throw new TelegramException($e->getMessage());
1069
        }
1070
    }
1071
1072
    /**
1073
     * Select Groups, Supergroups, Channels and/or single user Chats (also by ID or text)
1074
     *
1075
     * @param $select_chats_params
1076
     *
1077
     * @return array|bool
1078
     * @throws TelegramException
1079
     */
1080
    public static function selectChats($select_chats_params)
1081
    {
1082
        if (!self::isDbConnected()) {
1083
            return false;
1084
        }
1085
1086
        // Set defaults for omitted values.
1087
        $select = array_merge([
1088
            'groups'      => true,
1089
            'supergroups' => true,
1090
            'channels'    => true,
1091
            'users'       => true,
1092
            'date_from'   => null,
1093
            'date_to'     => null,
1094
            'chat_id'     => null,
1095
            'text'        => null,
1096
        ], $select_chats_params);
1097
1098
        if (!$select['groups'] && !$select['users'] && !$select['supergroups'] && !$select['channels']) {
1099
            return false;
1100
        }
1101
1102
        try {
1103
            $query = '
1104
                SELECT * ,
1105
                ' . TB_CHAT . '.`id` AS `chat_id`,
1106
                ' . TB_CHAT . '.`username` AS `chat_username`,
1107
                ' . TB_CHAT . '.`created_at` AS `chat_created_at`,
1108
                ' . TB_CHAT . '.`updated_at` AS `chat_updated_at`
1109
            ';
1110
            if ($select['users']) {
1111
                $query .= '
1112
                    , ' . TB_USER . '.`id` AS `user_id`
1113
                    FROM `' . TB_CHAT . '`
1114
                    LEFT JOIN `' . TB_USER . '`
1115
                    ON ' . TB_CHAT . '.`id`=' . TB_USER . '.`id`
1116
                ';
1117
            } else {
1118
                $query .= 'FROM `' . TB_CHAT . '`';
1119
            }
1120
1121
            // Building parts of query
1122
            $where  = [];
1123
            $tokens = [];
1124
1125
            if (!$select['groups'] || !$select['users'] || !$select['supergroups'] || !$select['channels']) {
1126
                $chat_or_user = [];
1127
1128
                $select['groups'] && $chat_or_user[] = TB_CHAT . '.`type` = "group"';
1129
                $select['supergroups'] && $chat_or_user[] = TB_CHAT . '.`type` = "supergroup"';
1130
                $select['channels'] && $chat_or_user[] = TB_CHAT . '.`type` = "channel"';
1131
                $select['users'] && $chat_or_user[] = TB_CHAT . '.`type` = "private"';
1132
1133
                $where[] = '(' . implode(' OR ', $chat_or_user) . ')';
1134
            }
1135
1136
            if (null !== $select['date_from']) {
1137
                $where[]              = TB_CHAT . '.`updated_at` >= :date_from';
1138
                $tokens[':date_from'] = $select['date_from'];
1139
            }
1140
1141
            if (null !== $select['date_to']) {
1142
                $where[]            = TB_CHAT . '.`updated_at` <= :date_to';
1143
                $tokens[':date_to'] = $select['date_to'];
1144
            }
1145
1146
            if (null !== $select['chat_id']) {
1147
                $where[]            = TB_CHAT . '.`id` = :chat_id';
1148
                $tokens[':chat_id'] = $select['chat_id'];
1149
            }
1150
1151
            if (null !== $select['text']) {
1152
                $text_like = '%' . strtolower($select['text']) . '%';
1153
                if ($select['users']) {
1154
                    $where[]          = '(
1155
                        LOWER(' . TB_CHAT . '.`title`) LIKE :text1
1156
                        OR LOWER(' . TB_USER . '.`first_name`) LIKE :text2
1157
                        OR LOWER(' . TB_USER . '.`last_name`) LIKE :text3
1158
                        OR LOWER(' . TB_USER . '.`username`) LIKE :text4
1159
                    )';
1160
                    $tokens[':text1'] = $text_like;
1161
                    $tokens[':text2'] = $text_like;
1162
                    $tokens[':text3'] = $text_like;
1163
                    $tokens[':text4'] = $text_like;
1164
                } else {
1165
                    $where[]         = 'LOWER(' . TB_CHAT . '.`title`) LIKE :text';
1166
                    $tokens[':text'] = $text_like;
1167
                }
1168
            }
1169
1170
            if (!empty($where)) {
1171
                $query .= ' WHERE ' . implode(' AND ', $where);
1172
            }
1173
1174
            $query .= ' ORDER BY ' . TB_CHAT . '.`updated_at` ASC';
1175
1176
            $sth = self::$pdo->prepare($query);
1177
            $sth->execute($tokens);
1178
1179
            return $sth->fetchAll(PDO::FETCH_ASSOC);
1180
        } catch (PDOException $e) {
1181
            throw new TelegramException($e->getMessage());
1182
        }
1183
    }
1184
1185
    /**
1186
     * Get Telegram API request count for current chat / message
1187
     *
1188
     * @param integer $chat_id
1189
     * @param string  $inline_message_id
1190
     *
1191
     * @return array Array containing TOTAL and CURRENT fields or false on invalid arguments
1192
     * @throws TelegramException
1193
     */
1194
    public static function getTelegramRequestCount($chat_id = null, $inline_message_id = null)
1195
    {
1196
        if (!self::isDbConnected()) {
1197
            return [];
1198
        }
1199
1200
        try {
1201
            $sth = self::$pdo->prepare('SELECT
1202
                (SELECT COUNT(DISTINCT `chat_id`) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_1) AS LIMIT_PER_SEC_ALL,
1203
                (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,
1204
                (SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_minute AND `chat_id` = :chat_id_2) AS LIMIT_PER_MINUTE
1205
            ');
1206
1207
            $date        = self::getTimestamp();
1208
            $date_minute = self::getTimestamp(strtotime('-1 minute'));
1209
1210
            $sth->bindValue(':chat_id_1', $chat_id);
1211
            $sth->bindValue(':chat_id_2', $chat_id);
1212
            $sth->bindValue(':inline_message_id', $inline_message_id);
1213
            $sth->bindValue(':created_at_1', $date);
1214
            $sth->bindValue(':created_at_2', $date);
1215
            $sth->bindValue(':created_at_minute', $date_minute);
1216
1217
            $sth->execute();
1218
1219
            return $sth->fetch();
1220
        } catch (PDOException $e) {
1221
            throw new TelegramException($e->getMessage());
1222
        }
1223
    }
1224
1225
    /**
1226
     * Insert Telegram API request in db
1227
     *
1228
     * @param string $method
1229
     * @param array  $data
1230
     *
1231
     * @return bool If the insert was successful
1232
     * @throws TelegramException
1233
     */
1234
    public static function insertTelegramRequest($method, $data)
1235
    {
1236
        if (!self::isDbConnected()) {
1237
            return false;
1238
        }
1239
1240
        try {
1241
            $sth = self::$pdo->prepare('INSERT INTO `' . TB_REQUEST_LIMITER . '`
1242
                (`method`, `chat_id`, `inline_message_id`, `created_at`)
1243
                VALUES
1244
                (:method, :chat_id, :inline_message_id, :created_at);
1245
            ');
1246
1247
            $chat_id           = isset($data['chat_id']) ? $data['chat_id'] : null;
1248
            $inline_message_id = isset($data['inline_message_id']) ? $data['inline_message_id'] : null;
1249
1250
            $sth->bindValue(':chat_id', $chat_id);
1251
            $sth->bindValue(':inline_message_id', $inline_message_id);
1252
            $sth->bindValue(':method', $method);
1253
            $sth->bindValue(':created_at', self::getTimestamp());
1254
1255
            return $sth->execute();
1256
        } catch (PDOException $e) {
1257
            throw new TelegramException($e->getMessage());
1258
        }
1259
    }
1260
1261
    /**
1262
     * Bulk update the entries of any table
1263
     *
1264
     * @param string $table
1265
     * @param array  $fields_values
1266
     * @param array  $where_fields_values
1267
     *
1268
     * @return bool
1269
     * @throws TelegramException
1270
     */
1271 3
    public static function update($table, array $fields_values, array $where_fields_values)
1272
    {
1273 3
        if (empty($fields_values) || !self::isDbConnected()) {
1274
            return false;
1275
        }
1276
1277
        try {
1278
            // Building parts of query
1279 3
            $tokens = $fields = $where = [];
1280
1281
            // Fields with values to update
1282 3
            foreach ($fields_values as $field => $value) {
1283 3
                $token          = ':' . count($tokens);
1284 3
                $fields[]       = "`{$field}` = {$token}";
1285 3
                $tokens[$token] = $value;
1286
            }
1287
1288
            // Where conditions
1289 3
            foreach ($where_fields_values as $field => $value) {
1290 3
                $token          = ':' . count($tokens);
1291 3
                $where[]        = "`{$field}` = {$token}";
1292 3
                $tokens[$token] = $value;
1293
            }
1294
1295 3
            $sql = 'UPDATE `' . $table . '` SET ' . implode(', ', $fields);
1296 3
            $sql .= count($where) > 0 ? ' WHERE ' . implode(' AND ', $where) : '';
1297
1298 3
            return self::$pdo->prepare($sql)->execute($tokens);
1299
        } catch (PDOException $e) {
1300
            throw new TelegramException($e->getMessage());
1301
        }
1302
    }
1303
}
1304