CrashPad::sendCrash()   A
last analyzed

Complexity

Conditions 5
Paths 7

Size

Total Lines 46
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 30
c 1
b 1
f 0
dl 0
loc 46
rs 9.1288
cc 5
nc 7
nop 3
1
<?php
2
declare(strict_types=1);
3
4
namespace TelegramBot;
5
6
use Exception;
7
use Throwable;
8
9
/**
10
 * CrashPad class
11
 *
12
 * @link    https://github.com/telegram-bot-php/core
13
 * @author  Shahrad Elahi (https://github.com/shahradelahi)
14
 * @license https://github.com/telegram-bot-php/core/blob/master/LICENSE (MIT License)
15
 */
16
class CrashPad
17
{
18
19
    /**
20
     * Enable the crash handler
21
     *
22
     * @return void
23
     */
24
    public static function enableCrashHandler(): void
25
    {
26
        $handler = function (Throwable $throwable) {
27
            if (Telegram::getAdminId() !== -1) {
28
                $input = Telegram::getInput();
29
                CrashPad::sendCrash(Telegram::getAdminId(), $throwable, $input);
30
            }
31
32
            if (!defined('DEBUG_MODE')) {
33
                throw new \RuntimeException(
34
                    'Something went wrong, Unfortunately, we can not handle this error.', 0, $throwable
35
                );
36
            }
37
38
            CrashPad::print($throwable);
39
        };
40
41
        set_exception_handler($handler);
42
    }
43
44
    /**
45
     * Debug mode. Catch the crash reports.
46
     *
47
     * @param int $admin_id (optional) The admin chat id.
48
     * @return void
49
     */
50
    public static function setDebugMode(int $admin_id = -1): void
51
    {
52
        error_reporting(E_ALL);
53
        ini_set('display_errors', '1');
54
55
        defined('DEBUG_MODE') or define('DEBUG_MODE', true);
56
        if ($admin_id !== -1) {
57
            Telegram::setAdminId($admin_id);
58
        }
59
60
        self::enableCrashHandler();
61
    }
62
63
    /**
64
     * Send crash message and log
65
     *
66
     * @param int $chat_id The chat id of the group to send the message to.
67
     * @param Exception|Throwable $exception The exception to report.
68
     * @param string|null $update (Optional) The update that caused the exception.
69
     *
70
     * @retrun bool
71
     */
72
    public static function sendCrash(int $chat_id, Exception|Throwable $exception, string|null $update = null): bool
73
    {
74
        if ($chat_id === -1) {
75
            throw new \RuntimeException(sprintf(
76
                'The given `chat_id` is not valid. given: %s',
77
                $chat_id
78
            ));
79
        }
80
81
        if (!Telegram::validateToken($_ENV['TELEGRAM_BOT_TOKEN'] ?? '')) {
82
          Telegram::tryAutoloadEnv();
83
        }
84
85
        if (($token = self::loadToken()) === null) {
86
            throw new \RuntimeException(
87
                'The token is not set. Please set the token using `Telegram::setToken()` method.'
88
            );
89
        }
90
91
        $text = Request::sendMessage([
92
            'bot_token' => $token,
93
            'chat_id' => $chat_id,
94
            'parse_mode' => 'HTML',
95
            'text' => sprintf(
96
                "<b>Message</b>: %s\n\n<b>File</b>: %s(%d)\n\n<b>Trace</b>: \n%s",
97
                $exception->getMessage(),
98
                $exception->getFile(),
99
                $exception->getLine(),
100
                $exception->getTraceAsString()
101
            ),
102
        ]);
103
104
        $document = Request::sendDocument([
105
            'bot_token' => $token,
106
            'chat_id' => $chat_id,
107
            'document' => self::createCrashFile(sprintf(
108
                "Message: %s\n\nFile: %s(%d)\n\nTrace: \n%s\n\nUpdate: \n%s",
109
                $exception->getMessage(),
110
                $exception->getFile(),
111
                $exception->getLine(),
112
                $exception->getTraceAsString(),
113
                $update ?? 'Did not receive update.'
114
            )),
115
        ]);
116
117
        return $text->isOk() && $document->isOk();
118
    }
119
120
    /**
121
     * Create a log file for the error.
122
     *
123
     * @param string $content The content of the log file.
124
     * @retrun string The path of the log file.
125
     */
126
    private static function createCrashFile(string $content): string
127
    {
128
        $base_path = $_SERVER['DOCUMENT_ROOT'] . '.telegram-bot/';
129
        if (!file_exists($base_path)) {
130
            mkdir($base_path, 0777, true);
131
        }
132
133
        file_put_contents(($file = $base_path . uniqid('error_') . '.log'), $content);
134
        return $file;
135
    }
136
137
    /**
138
     * Report the error to the developers from the Telegram Bot API.
139
     *
140
     * @param Exception|Throwable $exception The exception to report.
141
     * @retrun void
142
     */
143
    public static function print(Exception|Throwable $exception): void
144
    {
145
        TelegramLog::error(($message = sprintf(
146
            "%s(%d): %s\n%s",
147
            $exception->getFile(),
148
            $exception->getLine(),
149
            $exception->getMessage(),
150
            $exception->getTraceAsString()
151
        )));
152
        echo '<b>TelegramError:</b> ' . $message;
153
    }
154
155
    /**
156
     * Clear the crash logs.
157
     *
158
     * @return void
159
     */
160
    public static function clearCrashLogs(): void
161
    {
162
        $base_path = $_SERVER['DOCUMENT_ROOT'] . '.telegram-bot/';
163
        if (!file_exists($base_path)) {
164
            return;
165
        }
166
167
        $files = glob($base_path . '*');
168
        foreach ($files as $file) {
169
            if (is_file($file)) {
170
                unlink($file);
171
            }
172
        }
173
    }
174
175
    /**
176
     * Check is there any loaded token or any token in the environment file.
177
     *
178
     * @return string|null
179
     */
180
    protected static function loadToken(): string|null
181
    {
182
        if (($token = Telegram::getApiToken()) !== false) {
183
            return $token;
184
        }
185
186
        Telegram::tryAutoloadEnv();
187
        return $_ENV['TELEGRAM_BOT_TOKEN'] ?? null;
188
    }
189
190
}
191