Passed
Push — main ( aeb352...67a086 )
by Miaad
02:37 queued 01:12
created

tools.php (4 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
     * @param string $token your token e.g. => '123123123:abcabcabcabc'
95
     * @param bool $verify check token with telegram or not
96
     * @return bool|array return array when verify is active and token is true array of telegram getMe result
97
     */
98
    public static function isToken (string $token, bool $verify = false): bool|array {
99
        if (preg_match('/^(\d{8,10}):[\w\-]{35}$/', $token)) {
100
            if ($verify){
101
                $res = telegram::me($token);
0 ignored issues
show
Are you sure the assignment to $res is correct as BPT\api\telegram::me($token) targeting BPT\api\telegram::__callStatic() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
The method me() does not exist on BPT\api\telegram. Since you implemented __callStatic, 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

101
                /** @scrutinizer ignore-call */ $res = telegram::me($token);
Loading history...
102
                if ($res['ok']) {
103
                    return $res['result'];
104
                }
105
                return false;
106
            }
107
            return true;
108
        }
109
        return false;
110
    }
111
112
    /**
113
     * check user joined in channels or not
114
     *
115
     * this method only return true or false, if user join in all channels true, and if user not joined in one channel false
116
     *
117
     * this method does not care about not founded channel and count them as joined channel
118
     *
119
     * ids parameter can be array for multi channels or can be string for one channel
120
     *
121
     * user_id parameter have default value => generated by catchFields method
122
     *
123
     * NOTE : each channel will decrease speed a little(because of request count)
124
     *
125
     * e.g. => tools::isJoined('BPT_CH','442109602');
126
     *
127
     * e.g. => tools::isJoined(['BPT_CH','-1005465465454']);
128
     *
129
     * @param array|string|int $ids e.g. => ['BPT_CH'] , 'BPT_CH' , -100131231313
130
     * @param int|null $user_id e.g. => '442109602'
131
     * @return bool
132
     */
133
    public static function isJoined (array|string|int $ids , int|null $user_id = null): bool {
134
        if (!is_array($ids)) {
135
            $ids = [$ids];
136
        }
137
        //$user_id = $user_id ?? $this->catchFields(['field' => 'user_id']);
138
139
        foreach ($ids as $id) {
140
            $check = telegram::getChatMember($id,$user_id);
0 ignored issues
show
Are you sure the assignment to $check is correct as BPT\api\telegram::getChatMember($id, $user_id) targeting BPT\api\telegram::__callStatic() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
The method getChatMember() does not exist on BPT\api\telegram. Since you implemented __callStatic, 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

140
            /** @scrutinizer ignore-call */ $check = telegram::getChatMember($id,$user_id);
Loading history...
141
            if (isset($check['result'])) {
142
                $check = $check['result']['status'];
143
                return !($check === chatMemberStatus::LEFT || $check === chatMemberStatus::KICKED);
144
            }
145
        }
146
        return true;
147
    }
148
149
    /**
150
     * Generate random string
151
     *
152
     * you can use this method without any input
153
     *
154
     * length parameter have default value => 16
155
     *
156
     * characters parameter have default value => aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
157
     *
158
     * e.g. => tools::randomString();
159
     *
160
     * e.g. => tools::randomString(16,'abcdefg');
161
     *
162
     * e.g. => tools::randomString(length: 16,characters: 'abcdefg');
163
     *
164
     * @param int $length e.g. => 16
165
     * @param string $characters e.g. => 'abcdefg'
166
     * @return string
167
     */
168
    public static function randomString (int $length = 16, string $characters = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'): string {
169
        $rand_string = '';
170
        $char_len = strlen($characters) - 1;
171
        for ($i = 0; $i < $length; $i ++) {
172
            $rand_string .= $characters[rand(0, $char_len)];
173
        }
174
        return $rand_string;
175
    }
176
177
    /**
178
     * Escape text for different parse_modes
179
     *
180
     * type parameter can be : `MarkdownV2` , `Markdown` , `HTML` , default : `parseMode::HTML`(`HTML`)
181
     *
182
     * e.g. => tools::modeEscape('hello men! *I* Have nothing anymore');
183
     *
184
     * e.g. => tools::modeEscape(text: 'hello men! *I* Have nothing anymore');
185
     *
186
     * @param string $text Your text e.g. => 'hello men! *I* Have nothing anymore'
187
     * @param string $mode Your selected mode e.g. => `parseMode::HTML` | `HTML`
188
     * @return string|false return false when mode is incorrect
189
     */
190
    public static function modeEscape (string $text, string $mode = parseMode::HTML): string|false {
191
        return match ($mode) {
192
            parseMode::HTML => str_replace(['&', '<', '>',], ["&amp;", "&lt;", "&gt;",], $text),
193
            parseMode::MARKDOWN => str_replace(['\\', '_', '*', '`', '['], ['\\\\', '\_', '\*', '\`', '\[',], $text),
194
            parseMode::MARKDOWNV2 => str_replace(
195
                ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!', '\\'],
196
                ['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!', '\\\\'],
197
                $text),
198
            default => false
199
        };
200
    }
201
202
    /**
203
     * Convert byte to symbolic size like 2.98 MB
204
     *
205
     * Supp
206
     *
207
     * You could set `precision` to configure decimals after number(2 for 2.98 and 3 for 2.987)
208
     *
209
     * `precision` parameter have default value => 2
210
     *
211
     * e.g. => tools::byteFormat(123456789);
212
     *
213
     * e.g. => tools::byteFormat(byte: 123456789);
214
     *
215
     * @param int $byte e.g. => 29123452912
216
     * @param int $precision e.g. => 2
217
     * @return string
218
     */
219
    public static function byteFormat (int $byte, int $precision = 2): string {
220
        $rate_counter = 0;
221
222
        while ($byte > 1024){
223
            $byte /= 1024;
224
            $rate_counter++;
225
        }
226
227
        if ($rate_counter !== 0) {
228
            $byte = round($byte, $precision);
229
        }
230
231
        return $byte . ' ' . ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'][$rate_counter];
232
    }
233
234
    /**
235
     * receive size from path(can be url or file path)
236
     *
237
     * if format parameter has true value , the returned size converted to symbolic format
238
     *
239
     * format parameter have default value => true
240
     *
241
     * NOTE : some url will not return real size!
242
     *
243
     * e.g. => tools::size('xFile.zip');
244
     *
245
     * e.g. => tools::size(path: 'xFile.zip');
246
     *
247
     * @param string $path e.g. => 'xFile.zip'
248
     * @param bool $format if you set this true , you will receive symbolic string like 2.76MB
249
     * @return string|int|false string for formatted data , int for normal data , false when size can not be found(file not found or ...)
250
     */
251
    public static function size (string $path, bool $format = true): string|int|false {
252
        if (filter_var($path, FILTER_VALIDATE_URL)) {
253
            $ch = curl_init($path);
254
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
255
            curl_setopt($ch, CURLOPT_HEADER, true);
256
            curl_setopt($ch, CURLOPT_NOBODY, true);
257
            curl_exec($ch);
258
            $size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
259
            curl_close($ch);
260
        }
261
        else {
262
            $size = file_exists($path) ? filesize($path) : false;
263
        }
264
265
        if (isset($size) && is_numeric($size)) {
266
            return $format ? tools::byteFormat($size) : $size;
267
        }
268
        else return false;
269
    }
270
271
    //fix methods after here comments
272
273
    /**
274
     * Delete a folder or file if exist
275
     *
276
     * if the folder have subFiles , need to set sub parameter to true. if you don't , you will receive error
277
     *
278
     * sub parameter have default value => true
279
     *
280
     * e.g. => $this->delete(['path'=>'xfolder/yfolder']);
281
     *
282
     * e.g. => $this->delete(['path'=>'xfolder/yfolder','sub'=>false]);
283
     * @param array $array e.g. => ['path'=>'xfolder/yfolder','sub'=>true]
284
     * @return bool
285
     */
286
    public static function delete (string $path, bool $sub = true): bool {
287
        if (is_dir($path)) {
288
            if (count(scandir($path)) > 2) {
289
                if ($sub) {
290
                    $it = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
291
                    $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
292
                    foreach ($files as $file) {
293
                        $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
294
                    }
295
                    rmdir($path);
296
                }
297
                else {
298
                    logger::write("BPT delete function used\ndelete function cannot delete folder because its have subFiles and sub parameter haven't true value",loggerTypes::WARNING);
299
                    return false;
300
                }
301
            }
302
            else rmdir($path);
303
        }
304
        else unlink($path);
305
306
        return true;
307
    }
308
309
    /**
310
     * Convert datetime or timestamp to array
311
     *
312
     * Its calculated different between given time and now
313
     *
314
     * e.g. => $this->time2string(['datetime'=>1636913656]);
315
     *
316
     * @param array $array e.g. => ['datetime'=>1636913656]
317
     *
318
     * @return array
319
     * @throws Exception
320
     */
321
    public static function time2string (int $datetime): array {
322
        $now = new DateTime;
323
        $input = new DateTime('@' . $datetime);
324
        $status = $now < $input ? 'later' : 'ago';
325
        $diff = $now->diff($input);
326
        $diff->w = floor($diff->d / 7);
327
        $string = ['year' => 'y', 'month' => 'm', 'week' => 'w', 'day' => 'd', 'hour' => 'h', 'minute' => 'i', 'second' => 's'];
328
        foreach ($string as $k => &$v) {
329
            if ($diff->$v) {
330
                $v = $diff->$v;
331
            }
332
            else unset($string[$k]);
333
        }
334
        $string['status'] = $status;
335
        return count($string) > 1 ? $string : ['status' => 'now'];
336
    }
337
338
    public static function clearText(string $text): string {
339
        return htmlentities(strip_tags(htmlspecialchars(stripslashes(trim($text)))));
340
    }
341
}