Passed
Push — main ( 831caf...676412 )
by Miaad
01:25
created

tools.php (5 issues)

1
<?php
2
3
namespace BPT;
4
5
use BPT\api\telegram;
6
use BPT\constants\chatMemberStatus;
7
use BPT\constants\loggerTypes;
8
use BPT\constants\parseMode;
9
use DateTime;
10
use Exception;
11
use RecursiveDirectoryIterator;
12
use RecursiveIteratorIterator;
13
14
class tools {
15
    /**
16
     * Check the given username format
17
     *
18
     * e.g. => tools::isUsername('BPT_CH');
19
     *
20
     * e.g. => tools::isUsername(username: 'BPT_CH');
21
     *
22
     * @param string $username Your text to be check is username or not e.g. : 'BPT_CH' | '@BPT_CH'
23
     * @return bool
24
     */
25
    public static function isUsername (string $username): bool {
26
        $length = strlen($username);
27
        return strpos($username, '__') === false && $length >= 5 && $length <= 33 && preg_match('/^@?([a-zA-Z])(\w{4,31})$/', $username);
28
    }
29
30
    /**
31
     * Check given IP is in the given IP range or not
32
     *
33
     * e.g. => tools::ipInRange('192.168.1.1','149.154.160.0/20');
34
     *
35
     * e.g. => tools::ipInRange(ip: '192.168.1.1',range: '149.154.160.0/20');
36
     *
37
     * @param string $ip Your ip
38
     * @param string $range Your range ip for check , if you didn't specify the block , it will be 32
39
     * @return bool
40
     */
41
    public static function ipInRange (string $ip, string $range): bool {
42
        if (!str_contains($range, '/')) {
43
            $range .= '/32';
44
        }
45
        $range_full = explode('/', $range, 2);
46
        $netmask_decimal = ~(pow(2, (32 - $range_full[1])) - 1);
47
        return (ip2long($ip) & $netmask_decimal) == (ip2long($range_full[0]) & $netmask_decimal);
48
    }
49
50
    /**
51
     * Check the given IP is from telegram or not
52
     *
53
     * e.g. => tools::isTelegram('192.168.1.1');
54
     *
55
     * e.g. => tools::isTelegram(ip: '192.168.1.1');
56
     *
57
     * @param string $ip     Your ip to be check is telegram or not e.g. '192.168.1.1'
58
     * @return bool
59
     */
60
    public static function isTelegram (string $ip): bool {
61
        return self::ipInRange($ip, '149.154.160.0/20') || self::ipInRange($ip, '91.108.4.0/22');
62
    }
63
64
    /**
65
     * Check the given IP is from CloudFlare or not
66
     *
67
     * e.g. => tools::isCloudFlare('192.168.1.1');
68
     *
69
     * e.g. =>tools::isCloudFlare(ip: '192.168.1.1');
70
     *
71
     * @param string $ip Your ip to be check is CloudFlare or not e.g. '192.168.1.1'
72
     * @return bool
73
     */
74
    public static function isCloudFlare (string $ip): bool {
75
        $cf_ips = ['173.245.48.0/20', '103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22', '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20', '197.234.240.0/22', '198.41.128.0/17', '162.158.0.0/15', '104.16.0.0/12', '172.64.0.0/13', '131.0.72.0/22'];
76
        foreach ($cf_ips as $cf_ip) {
77
            if (self::ipInRange($ip,$cf_ip)) {
78
                return true;
79
            }
80
        }
81
        return false;
82
    }
83
84
    /**
85
     * Check the given token format
86
     *
87
     * if you want to verify token with telegram , you should set verify parameter => true
88
     *
89
     * in that case , if token was right , you will receive getMe result , otherwise you will receive false
90
     *
91
     * verify parameter has default value => false
92
     *
93
     * e.g. => tools::isToken('123123123:abcabcabcabc');
94
     *
95
     * @param string $token your token e.g. => '123123123:abcabcabcabc'
96
     * @param bool $verify check token with telegram or not
97
     *
98
     * @return types\responseError|types\user return array when verify is active and token is true array of telegram getMe result
99
     */
100
    public static function isToken (string $token, bool $verify = false): bool|types\user {
101
        if (preg_match('/^(\d{8,10}):[\w\-]{35}$/', $token)) {
102
            if ($verify){
103
                $res = telegram::me($token);
104
                if (telegram::$status) {
105
                    return $res;
106
                }
107
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type BPT\types\responseError|BPT\types\user.
Loading history...
108
            }
109
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type BPT\types\responseError|BPT\types\user.
Loading history...
110
        }
111
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type BPT\types\responseError|BPT\types\user.
Loading history...
112
    }
113
114
    /**
115
     * check user joined in channels or not
116
     *
117
     * this method only return true or false, if user join in all channels true, and if user not joined in one channel false
118
     *
119
     * this method does not care about not founded channel and count them as joined channel
120
     *
121
     * ids parameter can be array for multi channels or can be string for one channel
122
     *
123
     * user_id parameter have default value => generated by catchFields method
124
     *
125
     * NOTE : each channel will decrease speed a little(because of request count)
126
     *
127
     * e.g. => tools::isJoined('BPT_CH','442109602');
128
     *
129
     * e.g. => tools::isJoined(['BPT_CH','-1005465465454']);
130
     *
131
     * @param array|string|int $ids e.g. => ['BPT_CH'] , 'BPT_CH' , -100131231313
132
     * @param int|null $user_id e.g. => '442109602'
133
     * @return bool
134
     */
135
    public static function isJoined (array|string|int $ids , int|null $user_id = null): bool {
136
        if (!is_array($ids)) {
0 ignored issues
show
The condition is_array($ids) is always true.
Loading history...
137
            $ids = [$ids];
138
        }
139
        //$user_id = $user_id ?? $this->catchFields(['field' => 'user_id']);
140
141
        foreach ($ids as $id) {
142
            $check = telegram::getChatMember($id,$user_id);
143
            if (telegram::$status) {
144
                $check = $check->status;
145
                return !($check === chatMemberStatus::LEFT || $check === chatMemberStatus::KICKED);
146
            }
147
        }
148
        return true;
149
    }
150
151
    /**
152
     * Generate random string
153
     *
154
     * you can use this method without any input
155
     *
156
     * length parameter have default value => 16
157
     *
158
     * characters parameter have default value => aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
159
     *
160
     * e.g. => tools::randomString();
161
     *
162
     * e.g. => tools::randomString(16,'abcdefg');
163
     *
164
     * e.g. => tools::randomString(length: 16,characters: 'abcdefg');
165
     *
166
     * @param int $length e.g. => 16
167
     * @param string $characters e.g. => 'abcdefg'
168
     * @return string
169
     */
170
    public static function randomString (int $length = 16, string $characters = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'): string {
171
        $rand_string = '';
172
        $char_len = strlen($characters) - 1;
173
        for ($i = 0; $i < $length; $i ++) {
174
            $rand_string .= $characters[rand(0, $char_len)];
175
        }
176
        return $rand_string;
177
    }
178
179
    /**
180
     * Escape text for different parse_modes
181
     *
182
     * type parameter can be : `MarkdownV2` , `Markdown` , `HTML` , default : `parseMode::HTML`(`HTML`)
183
     *
184
     * e.g. => tools::modeEscape('hello men! *I* Have nothing anymore');
185
     *
186
     * e.g. => tools::modeEscape(text: 'hello men! *I* Have nothing anymore');
187
     *
188
     * @param string $text Your text e.g. => 'hello men! *I* Have nothing anymore'
189
     * @param string $mode Your selected mode e.g. => `parseMode::HTML` | `HTML`
190
     * @return string|false return false when mode is incorrect
191
     */
192
    public static function modeEscape (string $text, string $mode = parseMode::HTML): string|false {
193
        return match ($mode) {
194
            parseMode::HTML => str_replace(['&', '<', '>',], ["&amp;", "&lt;", "&gt;",], $text),
195
            parseMode::MARKDOWN => str_replace(['\\', '_', '*', '`', '['], ['\\\\', '\_', '\*', '\`', '\[',], $text),
196
            parseMode::MARKDOWNV2 => str_replace(
197
                ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!', '\\'],
198
                ['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!', '\\\\'],
199
                $text),
200
            default => false
201
        };
202
    }
203
204
    /**
205
     * Convert byte to symbolic size like 2.98 MB
206
     *
207
     * Supp
208
     *
209
     * You could set `precision` to configure decimals after number(2 for 2.98 and 3 for 2.987)
210
     *
211
     * `precision` parameter have default value => 2
212
     *
213
     * e.g. => tools::byteFormat(123456789);
214
     *
215
     * e.g. => tools::byteFormat(byte: 123456789);
216
     *
217
     * @param int $byte e.g. => 29123452912
218
     * @param int $precision e.g. => 2
219
     * @return string
220
     */
221
    public static function byteFormat (int $byte, int $precision = 2): string {
222
        $rate_counter = 0;
223
224
        while ($byte > 1024){
225
            $byte /= 1024;
226
            $rate_counter++;
227
        }
228
229
        if ($rate_counter !== 0) {
230
            $byte = round($byte, $precision);
231
        }
232
233
        return $byte . ' ' . ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'][$rate_counter];
234
    }
235
236
    /**
237
     * receive size from path(can be url or file path)
238
     *
239
     * if format parameter has true value , the returned size converted to symbolic format
240
     *
241
     * format parameter have default value => true
242
     *
243
     * NOTE : some url will not return real size!
244
     *
245
     * e.g. => tools::size('xFile.zip');
246
     *
247
     * e.g. => tools::size(path: 'xFile.zip');
248
     *
249
     * @param string $path e.g. => 'xFile.zip'
250
     * @param bool $format if you set this true , you will receive symbolic string like 2.76MB
251
     * @return string|int|false string for formatted data , int for normal data , false when size can not be found(file not found or ...)
252
     */
253
    public static function size (string $path, bool $format = true): string|int|false {
254
        if (filter_var($path, FILTER_VALIDATE_URL)) {
255
            $ch = curl_init($path);
256
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
257
            curl_setopt($ch, CURLOPT_HEADER, true);
258
            curl_setopt($ch, CURLOPT_NOBODY, true);
259
            curl_exec($ch);
260
            $size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
261
            curl_close($ch);
262
        }
263
        else {
264
            $size = file_exists($path) ? filesize($path) : false;
265
        }
266
267
        if (isset($size) && is_numeric($size)) {
268
            return $format ? tools::byteFormat($size) : $size;
269
        }
270
        else return false;
271
    }
272
273
    //fix methods after here comments
274
275
    /**
276
     * Delete a folder or file if exist
277
     *
278
     * if the folder have subFiles , need to set sub parameter to true. if you don't , you will receive error
279
     *
280
     * sub parameter have default value => true
281
     *
282
     * e.g. => $this->delete(['path'=>'xfolder/yfolder']);
283
     *
284
     * e.g. => $this->delete(['path'=>'xfolder/yfolder','sub'=>false]);
285
     * @param array $array e.g. => ['path'=>'xfolder/yfolder','sub'=>true]
286
     * @return bool
287
     */
288
    public static function delete (string $path, bool $sub = true): bool {
289
        if (is_dir($path)) {
290
            if (count(scandir($path)) > 2) {
291
                if ($sub) {
292
                    $it = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
293
                    $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
294
                    foreach ($files as $file) {
295
                        $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
296
                    }
297
                    rmdir($path);
298
                }
299
                else {
300
                    logger::write("BPT delete function used\ndelete function cannot delete folder because its have subFiles and sub parameter haven't true value",loggerTypes::WARNING);
301
                    return false;
302
                }
303
            }
304
            else rmdir($path);
305
        }
306
        else unlink($path);
307
308
        return true;
309
    }
310
311
    /**
312
     * Convert datetime or timestamp to array
313
     *
314
     * Its calculated different between given time and now
315
     *
316
     * e.g. => $this->time2string(['datetime'=>1636913656]);
317
     *
318
     * @param array $array e.g. => ['datetime'=>1636913656]
319
     *
320
     * @return array
321
     * @throws Exception
322
     */
323
    public static function time2string (int $datetime): array {
324
        $now = new DateTime;
325
        $input = new DateTime('@' . $datetime);
326
        $status = $now < $input ? 'later' : 'ago';
327
        $diff = $now->diff($input);
328
        $diff->w = floor($diff->d / 7);
0 ignored issues
show
The property w does not seem to exist on DateInterval.
Loading history...
329
        $string = ['year' => 'y', 'month' => 'm', 'week' => 'w', 'day' => 'd', 'hour' => 'h', 'minute' => 'i', 'second' => 's'];
330
        foreach ($string as $k => &$v) {
331
            if ($diff->$v) {
332
                $v = $diff->$v;
333
            }
334
            else unset($string[$k]);
335
        }
336
        $string['status'] = $status;
337
        return count($string) > 1 ? $string : ['status' => 'now'];
338
    }
339
340
    public static function clearText(string $text): string {
341
        return htmlentities(strip_tags(htmlspecialchars(stripslashes(trim($text)))));
342
    }
343
}