Passed
Pull Request — master (#412)
by Alexander
01:57
created

Client   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 396
Duplicated Lines 0 %

Test Coverage

Coverage 33.56%

Importance

Changes 10
Bugs 1 Features 5
Metric Value
wmc 53
eloc 113
c 10
b 1
f 5
dl 0
loc 396
ccs 49
cts 146
cp 0.3356
rs 6.96

33 Methods

Rating   Name   Duplication   Size   Complexity  
A getEditedMessageEvent() 0 10 2
A channelPost() 0 3 1
A getCallbackQueryChecker() 0 4 1
A getChosenInlineResultChecker() 0 4 1
A getPreCheckoutQueryChecker() 0 4 1
A command() 0 3 1
A getInlineQueryChecker() 0 4 1
A __call() 0 8 3
A callbackQuery() 0 3 1
A editedMessage() 0 3 1
A __construct() 0 7 2
A shippingQuery() 0 3 1
A getChannelPostChecker() 0 4 1
A on() 0 5 1
A getCallbackQueryEvent() 0 10 2
A getChosenInlineResultEvent() 0 10 2
A getPreCheckoutQueryEvent() 0 10 2
A getChannelPostEvent() 0 10 2
A preCheckoutQuery() 0 3 1
A getEditedChannelPostChecker() 0 4 1
A getChecker() 0 11 4
A editedChannelPost() 0 3 1
A getShippingQueryEvent() 0 10 2
A getEditedChannelPostEvent() 0 10 2
A getEditedMessageChecker() 0 4 1
A chosenInlineResult() 0 3 1
A run() 0 4 2
A getEvent() 0 25 5
A getInlineQueryEvent() 0 10 2
A handle() 0 5 2
A getShippingQueryChecker() 0 4 1
A inlineQuery() 0 3 1
A getRawBody() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Client often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace TelegramBot\Api;
4
5
use Closure;
6
use ReflectionFunction;
7
use TelegramBot\Api\Events\EventCollection;
8
use TelegramBot\Api\Types\Update;
9
use TelegramBot\Api\Types\Message;
10
use TelegramBot\Api\Types\Inline\InlineKeyboardMarkup;
11
use TelegramBot\Api\Types\ReplyKeyboardRemove;
12
use TelegramBot\Api\Types\ForceReply;
13
use TelegramBot\Api\Types\ReplyKeyboardHide;
14
use TelegramBot\Api\Types\ReplyKeyboardMarkup;
15
16
/**
17
 * Class Client
18
 *
19
 * @package TelegramBot\Api
20
 * @method Message editMessageText(string $chatId, int $messageId, string $text, string $parseMode = null, bool $disablePreview = false, ReplyKeyboardMarkup|ReplyKeyboardHide|ForceReply|ReplyKeyboardRemove|InlineKeyboardMarkup|null $replyMarkup = null, string $inlineMessageId = null)
21
 */
22
class Client
23
{
24
    /**
25
     * RegExp for bot commands
26
     */
27
    const REGEXP = '/^(?:@\w+\s)?\/([^\s@]+)(@\S+)?\s?(.*)$/';
28
29
    /**
30
     * @var \TelegramBot\Api\BotApi
31
     */
32
    protected $api;
33
34
    /**
35
     * @var \TelegramBot\Api\Events\EventCollection
36
     */
37
    protected $events;
38
39
    /**
40
     * Client constructor
41
     *
42
     * @param string $token Telegram Bot API token
43
     * @param string|null $trackerToken Yandex AppMetrica application api_key
44
     */
45 7
    public function __construct($token, $trackerToken = null)
46
    {
47 7
        if ($trackerToken) {
48 7
            @trigger_error(sprintf('Passing $trackerToken to %s is deprecated', self::class), \E_USER_DEPRECATED);
49 7
        }
50
        $this->api = new BotApi($token);
51
        $this->events = new EventCollection($trackerToken);
52
    }
53
54
    /**
55
     * Use this method to add command. Parameters will be automatically parsed and passed to closure.
56
     *
57
     * @param string $name
58
     * @param \Closure $action
59
     *
60
     * @return \TelegramBot\Api\Client
61
     */
62
    public function command($name, Closure $action)
63
    {
64
        return $this->on(self::getEvent($action), self::getChecker($name));
65
    }
66
67
    public function editedMessage(Closure $action)
68
    {
69
        return $this->on(self::getEditedMessageEvent($action), self::getEditedMessageChecker());
70
    }
71
72
    public function callbackQuery(Closure $action)
73
    {
74
        return $this->on(self::getCallbackQueryEvent($action), self::getCallbackQueryChecker());
75
    }
76
77
    public function channelPost(Closure $action)
78
    {
79
        return $this->on(self::getChannelPostEvent($action), self::getChannelPostChecker());
80
    }
81
82
    public function editedChannelPost(Closure $action)
83
    {
84
        return $this->on(self::getEditedChannelPostEvent($action), self::getEditedChannelPostChecker());
85
    }
86
87
    public function inlineQuery(Closure $action)
88
    {
89
        return $this->on(self::getInlineQueryEvent($action), self::getInlineQueryChecker());
90
    }
91
92
    public function chosenInlineResult(Closure $action)
93
    {
94
        return $this->on(self::getChosenInlineResultEvent($action), self::getChosenInlineResultChecker());
95
    }
96
97
    public function shippingQuery(Closure $action)
98
    {
99
        return $this->on(self::getShippingQueryEvent($action), self::getShippingQueryChecker());
100
    }
101
102
    public function preCheckoutQuery(Closure $action)
103
    {
104
        return $this->on(self::getPreCheckoutQueryEvent($action), self::getPreCheckoutQueryChecker());
105
    }
106
107
    /**
108
     * Use this method to add an event.
109
     * If second closure will return true (or if you are passed null instead of closure), first one will be executed.
110
     *
111
     * @param \Closure $event
112
     * @param \Closure|null $checker
113 1
     *
114
     * @return \TelegramBot\Api\Client
115 1
     */
116
    public function on(Closure $event, Closure $checker = null)
117 1
    {
118
        $this->events->add($event, $checker);
119
120
        return $this;
121
    }
122
123
    /**
124
     * Handle updates
125 4
     *
126
     * @param Update[] $updates
127 4
     */
128
    public function handle(array $updates)
129 4
    {
130 4
        foreach ($updates as $update) {
131 4
            /* @var \TelegramBot\Api\Types\Update $update */
132
            $this->events->handle($update);
133
        }
134
    }
135
136
    /**
137
     * Webhook handler
138
     *
139
     * @return array
140
     * @throws \TelegramBot\Api\InvalidJsonException
141
     */
142
    public function run()
143
    {
144
        if ($data = BotApi::jsonValidate($this->getRawBody(), true)) {
145
            $this->handle([Update::fromResponse($data)]);
146
        }
147
    }
148
149
    public function getRawBody()
150
    {
151
        return file_get_contents('php://input');
152
    }
153
154
    /**
155
     * Returns event function to handling the command.
156
     *
157
     * @param \Closure $action
158 4
     *
159
     * @return \Closure
160
     */
161 4
    protected static function getEvent(Closure $action)
162 4
    {
163 1
        return function (Update $update) use ($action) {
164
            $message = $update->getMessage();
165
            if (!$message) {
0 ignored issues
show
introduced by
$message is of type TelegramBot\Api\Types\Message, thus it always evaluated to true.
Loading history...
166 3
                return true;
167
            }
168 3
169 1
            preg_match(self::REGEXP, $message->getText(), $matches);
170 1
171 2
            if (isset($matches[3]) && !empty($matches[3])) {
172
                $parameters = str_getcsv($matches[3], chr(32));
173
            } else {
174 3
                $parameters = [];
175
            }
176 3
177
            array_unshift($parameters, $message);
178 3
179 3
            $action = new ReflectionFunction($action);
180 3
181
            if (count($parameters) >= $action->getNumberOfRequiredParameters()) {
182 3
                $action->invokeArgs($parameters);
183 4
            }
184
185
            return false;
186
        };
187
    }
188
189
    protected static function getEditedMessageEvent(Closure $action)
190
    {
191
        return function (Update $update) use ($action) {
192
            if (!$update->getEditedMessage()) {
193
                return true;
194
            }
195
196
            $reflectionAction = new ReflectionFunction($action);
197
            $reflectionAction->invokeArgs([$update->getEditedMessage()]);
198
            return false;
199
        };
200
    }
201
202
    protected static function getChannelPostEvent(Closure $action)
203
    {
204
        return function (Update $update) use ($action) {
205
            if (!$update->getChannelPost()) {
206
                return true;
207
            }
208
209
            $reflectionAction = new ReflectionFunction($action);
210
            $reflectionAction->invokeArgs([$update->getChannelPost()]);
211
            return false;
212
        };
213
    }
214
215
    protected static function getCallbackQueryEvent(Closure $action)
216
    {
217
        return function (Update $update) use ($action) {
218
            if (!$update->getCallbackQuery()) {
219
                return true;
220
            }
221
222
            $reflectionAction = new ReflectionFunction($action);
223
            $reflectionAction->invokeArgs([$update->getCallbackQuery()]);
224
            return false;
225
        };
226
    }
227
228
    protected static function getEditedChannelPostEvent(Closure $action)
229
    {
230
        return function (Update $update) use ($action) {
231
            if (!$update->getEditedChannelPost()) {
232
                return true;
233
            }
234
235
            $reflectionAction = new ReflectionFunction($action);
236
            $reflectionAction->invokeArgs([$update->getEditedChannelPost()]);
237
            return false;
238 4
        };
239
    }
240
241 4
    protected static function getInlineQueryEvent(Closure $action)
242 3
    {
243
        return function (Update $update) use ($action) {
244
            if (!$update->getInlineQuery()) {
245 1
                return true;
246 1
            }
247 1
248 4
            $reflectionAction = new ReflectionFunction($action);
249
            $reflectionAction->invokeArgs([$update->getInlineQuery()]);
250
            return false;
251
        };
252
    }
253
254
    protected static function getChosenInlineResultEvent(Closure $action)
255
    {
256
        return function (Update $update) use ($action) {
257
            if (!$update->getChosenInlineResult()) {
258
                return true;
259
            }
260
261
            $reflectionAction = new ReflectionFunction($action);
262
            $reflectionAction->invokeArgs([$update->getChosenInlineResult()]);
263
            return false;
264
        };
265
    }
266
267
    protected static function getShippingQueryEvent(Closure $action)
268
    {
269
        return function (Update $update) use ($action) {
270
            if (!$update->getShippingQuery()) {
271
                return true;
272
            }
273
274
            $reflectionAction = new ReflectionFunction($action);
275
            $reflectionAction->invokeArgs([$update->getShippingQuery()]);
276
            return false;
277
        };
278
    }
279
280
    protected static function getPreCheckoutQueryEvent(Closure $action)
281
    {
282
        return function (Update $update) use ($action) {
283
            if (!$update->getPreCheckoutQuery()) {
284
                return true;
285
            }
286
287
            $reflectionAction = new ReflectionFunction($action);
288
            $reflectionAction->invokeArgs([$update->getPreCheckoutQuery()]);
289
            return false;
290
        };
291
    }
292
293
    /**
294
     * Returns check function to handling the command.
295
     *
296
     * @param string $name
297 4
     *
298
     * @return \Closure
299
     */
300 4
    protected static function getChecker($name)
301 4
    {
302 1
        return function (Update $update) use ($name) {
303
            $message = $update->getMessage();
304
            if (is_null($message) || !strlen($message->getText())) {
305 3
                return false;
306
            }
307 3
308 4
            preg_match(self::REGEXP, $message->getText(), $matches);
309
310
            return !empty($matches) && $matches[1] == $name;
311
        };
312
    }
313
314
    /**
315
     * Returns check function to handling the edited message.
316
     *
317
     * @return Closure
318
     */
319
    protected static function getEditedMessageChecker()
320
    {
321
        return function (Update $update) {
322
            return !is_null($update->getEditedMessage());
323
        };
324
    }
325
326
    /**
327
     * Returns check function to handling the channel post.
328
     *
329
     * @return Closure
330
     */
331
    protected static function getChannelPostChecker()
332
    {
333
        return function (Update $update) {
334
            return !is_null($update->getChannelPost());
335
        };
336
    }
337
338
    /**
339
     * Returns check function to handling the callbackQuery.
340
     *
341
     * @return Closure
342
     */
343
    protected static function getCallbackQueryChecker()
344
    {
345
        return function (Update $update) {
346
            return !is_null($update->getCallbackQuery());
347
        };
348
    }
349
350
    /**
351
     * Returns check function to handling the edited channel post.
352
     *
353
     * @return Closure
354
     */
355
    protected static function getEditedChannelPostChecker()
356
    {
357
        return function (Update $update) {
358
            return !is_null($update->getEditedChannelPost());
359
        };
360
    }
361
362
    /**
363
     * Returns check function to handling the chosen inline result.
364
     *
365
     * @return Closure
366
     */
367
    protected static function getChosenInlineResultChecker()
368
    {
369
        return function (Update $update) {
370
            return !is_null($update->getChosenInlineResult());
371
        };
372
    }
373
374
    /**
375
     * Returns check function to handling the inline queries.
376 4
     *
377
     * @return Closure
378
     */
379 4
    protected static function getInlineQueryChecker()
380 4
    {
381
        return function (Update $update) {
382
            return !is_null($update->getInlineQuery());
383
        };
384
    }
385
386
    /**
387
     * Returns check function to handling the shipping queries.
388
     *
389
     * @return Closure
390
     */
391
    protected static function getShippingQueryChecker()
392
    {
393
        return function (Update $update) {
394
            return !is_null($update->getShippingQuery());
395
        };
396
    }
397
398
    /**
399
     * Returns check function to handling the pre checkout queries.
400
     *
401
     * @return Closure
402
     */
403
    protected static function getPreCheckoutQueryChecker()
404
    {
405
        return function (Update $update) {
406
            return !is_null($update->getPreCheckoutQuery());
407 1
        };
408
    }
409 1
410
    public function __call($name, array $arguments)
411 1
    {
412
        if (method_exists($this, $name)) {
413
            return call_user_func_array([$this, $name], $arguments);
414 1
        } elseif (method_exists($this->api, $name)) {
415
            return call_user_func_array([$this->api, $name], $arguments);
416
        }
417
        throw new BadMethodCallException("Method {$name} not exists");
418
    }
419
}
420