GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( ff5597...7027db )
by Stan
02:49
created

Handler::triggerEventForEntity()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 23
rs 6.7272
cc 7
eloc 11
nc 4
nop 2
1
<?php
2
3
/**
4
 * Event and command executor class. Goes trough entities in response object and triggers corresponding
5
 * entity events and/or commands, either mapped in config file or via default Teebot\Bot namespace.
6
 *
7
 * @package Teebot (Telegram bot framework)
8
 *
9
 * @author  Stanislav Drozdov <[email protected]>
10
 */
11
12
namespace Teebot\Command;
13
14
use Teebot\Entity\Command;
15
use Teebot\Entity\Error;
16
use Teebot\Entity\MessageEntity;
17
use Teebot\Entity\Update;
18
use Teebot\Exception\Notice;
19
use Teebot\Exception\Output;
20
use Teebot\Method\AbstractMethod;
21
use Teebot\Request;
22
use Teebot\Entity\AbstractEntity;
23
use Teebot\Entity\Message;
24
use Teebot\Entity\MessageEntityArray;
25
use Teebot\Config;
26
use Teebot\Response;
27
28
class Handler
29
{
30
    /** @var Handler */
31
    protected static $instance;
32
33
    /** @var Config $config */
34
    protected $config;
35
36
    /** @var Request $request */
37
    protected $request;
38
39
    /**
40
     * Returns a singletone instance of executor
41
     *
42
     * @return Handler
43
     */
44
    public static function getInstance()
45
    {
46
        if (!self::$instance instanceof self) {
47
            self::$instance = new self();
48
        }
49
50
        return self::$instance;
51
    }
52
53
    /**
54
     * Returns an instance of config class
55
     *
56
     * @return Config
57
     */
58
    public function getConfig()
59
    {
60
        return $this->config;
61
    }
62
63
    /**
64
     * Initialises the executor, creates request object
65
     *
66
     * @param Config $config Configuration object
67
     */
68
    public function initWithConfig(Config $config)
69
    {
70
        $this->config  = $config;
71
        $this->request = new Request($config);
72
    }
73
74
    /**
75
     * Processes array of entities from response object, root entities must be either update entity
76
     * or error entity in case of error response from Telegram's API.
77
     *
78
     * @param array $entities Array of entities (Update or Error) passed either from response object or
79
     *                        directly to the method
80
     *
81
     * @return bool
82
     *
83
     * @throws Notice
84
     */
85
    public function processEntities(array $entities)
86
    {
87
        /** @var Update $entity */
88
        foreach ($entities as $entity) {
89
            $entitiesFlow = $this->getEntitiesChain($entity);
90
91
            if (empty($entitiesFlow)) {
92
                throw new Notice("Unknown entity! Skipping.");
93
            }
94
95
            $result = $this->processEntitiesChain($entitiesFlow);
96
97
            if ($result == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
98
                throw new Notice("Failed to process the entity!");
99
            }
100
        }
101
102
        return true;
103
    }
104
105
    /**
106
     * Returns entities chain generated from nested sub-entities of the passed entity
107
     *
108
     * Generated hierarchy of the events:
109
     *
110
     * - Error
111
     * - Update
112
     *   - Message
113
     *     - Command
114
     *       - <Command name>
115
     *     - Audio ... <Any supported entity>
116
     *   - Inline Query
117
     *   - Chosen Inline Result
118
     *
119
     * @param Update|Error $entity Update or Error entity object
120
     *
121
     * @return array
122
     */
123
    public function getEntitiesChain($entity)
124
    {
125
        if ($entity instanceof Error) {
126
            return [
127
                ['entity' => $entity]
128
            ];
129
        }
130
        if (!$entity instanceof Update) {
131
            return [];
132
        }
133
134
        $updateTypeEntity = $entity->getUpdateTypeEntity();
135
136
        $events = [
137
            ['entity' => $entity],
138
            ['entity' => $updateTypeEntity, 'parent' => $updateTypeEntity],
139
        ];
140
141
        if ($updateTypeEntity instanceof Message && $updateTypeEntity->getMessageTypeEntity()) {
142
143
            $messageTypeEntity = $updateTypeEntity->getMessageTypeEntity();
144
145
            $events[] = [
146
                'entity' => $messageTypeEntity,
147
                'parent' => $updateTypeEntity
148
            ];
149
150
            if ($messageTypeEntity instanceof MessageEntityArray) {
151
                $entities = $messageTypeEntity->getEntities();
152
153
                foreach ($entities as $entity) {
154
                    $events[] = [
155
                        'entity' => $entity,
156
                        'parent' => $updateTypeEntity
157
                    ];
158
                }
159
            }
160
        }
161
162
        return $events;
163
    }
164
165
    /**
166
     * Processes generated entities chain, if triggered event returns false stops processing
167
     *
168
     * @param array $entitiesFlow Array of entities flow
169
     *
170
     * @throws Notice
171
     *
172
     * @return bool
173
     */
174
    protected function processEntitiesChain(array $entitiesFlow)
175
    {
176
        foreach ($entitiesFlow as $entityData) {
177
178
            try {
179
                $continue = $this->triggerEventForEntity($entityData['entity'], $entityData['parent'] ?? null);
180
181
                if (!$continue) {
182
                    return true;
183
                }
184
            } catch (\Exception $e) {
185
                Output::log($e);
186
            }
187
        }
188
189
        return true;
190
    }
191
192
    /**
193
     * Triggers desired event and returns boolean result from run method of the event. If run() returns
194
     * false the processing in main process method will be stopped and further events (if any)
195
     * will not be triggered otherwise process will continue until either first false returned or the very
196
     * last event in the flow.
197
     *
198
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
199
     * @param AbstractEntity $parent    Entity's parent if any
200
     *
201
     * @return bool
202
     */
203
    protected function triggerEventForEntity(AbstractEntity $entity, AbstractEntity $parent = null)
204
    {
205
        $eventClass = $this->getEventClass($entity);
206
207
        if (class_exists($eventClass)) {
208
            $event = new $eventClass();
209
210
            if (!$event instanceof AbstractEntityEvent && !$event instanceof AbstractCommand) {
211
                return true;
212
            }
213
214
            /** @var AbstractCommand $eventClass */
215
            if ($event instanceof AbstractCommand && $entity instanceof MessageEntity && $entity->isCommand()) {
216
                $event->setArgs($entity->getArgs());
217
            }
218
219
            $event->setEntity($parent ?? $entity);
220
221
            return $event->run();
222
        }
223
224
        return true;
225
    }
226
227
    /**
228
     * Returns mapped event class from the configuration (if it was previously defined)
229
     *
230
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
231
     *                                  be treated as a command
232
     * @return null|string
233
     */
234
    protected function getEventClass(AbstractEntity $entity)
235
    {
236
        $preDefinedEvents = $this->config->getEvents();
237
        $entityEventType  = $entity->getEntityType();
238
239
        foreach ($preDefinedEvents as $preDefinedEvent) {
240
241
            if ($preDefinedEvent['type'] == $entityEventType) {
242
                $className = $preDefinedEvent['class'];
243
244
                if ($entity instanceof MessageEntity && $entity->isCommand()) {
245
                    if (!$this->isCommandSupported($preDefinedEvent, $entity->getCommand())) {
246
                        continue;
247
                    }
248
                }
249
250
                if (class_exists($className)) {
251
                    return $className;
252
                }
253
            }
254
        }
255
256
        return null;
257
    }
258
259
    /**
260
     * Checks whether command is defined in config and matches the current one
261
     *
262
     * @param array  $preDefinedEvent Pre defined event data
263
     * @param string $command         Command name
264
     *
265
     * @return bool
266
     */
267
    protected function isCommandSupported($preDefinedEvent, $command)
268
    {
269
        return isset($preDefinedEvent['command']) && strtolower($preDefinedEvent['command']) == strtolower($command);
270
    }
271
272
    /**
273
     * Executes remote method and returns response object
274
     * 
275
     * @param AbstractMethod $method     Method instance
276
     * @param bool           $silentMode If set to true then the events, mapped (in config or by default)
277
     *                                   to the entities in the result will not be triggered
278
     * @param AbstractEntity $parent     Parent entity (if any)
279
     *
280
     * @return Response
281
     */
282
    public function callRemoteMethod(AbstractMethod $method, $silentMode = false, $parent = null)
283
    {
284
        /** @var Response $response */
285
        $response = $this->request->exec($method, $parent);
286
287
        return $this->processResponse($response, $silentMode);
288
    }
289
290
    /**
291
     * Returns a response object and starts the entities processing (if not in silent mode). Method
292
     * should be used only when webhook is set.
293
     *
294
     * @param string $data       Raw json data either received from php input or passed manually
295
     * @param bool   $silentMode If set to true then the events, mapped (in config or by default)
296
     *                           to the entities in the result will not be triggered
297
     *
298
     * @return Response
299
     */
300
    public function getWebhookResponse($data, $silentMode = false)
301
    {
302
        $response = new Response($data, Update::class);
303
304
        return $this->processResponse($response, $silentMode);
305
    }
306
307
    /**
308
     * Processes entities from the response object if not in silent mode or error is received.
309
     *
310
     * @param Response $response   Response object which includes entities
311
     * @param bool     $silentMode If set to true then the events, mapped (in config or by default)
312
     *                             to the entities in the result will not be triggered
313
     *
314
     * @return Response
315
     */
316
    protected function processResponse(Response $response, $silentMode = false)
317
    {
318
        if (!empty($response->getEntities()) && ($silentMode === false || $response->isErrorReceived())) {
319
            $this->processEntities($response->getEntities());
320
        }
321
322
        return $response;
323
    }
324
}
325