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 ( 52e5ce...5f7adf )
by Stan
03:05
created

Handler::processEntitiesChain()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 0
loc 18
rs 8.8571
c 1
b 1
f 0
cc 5
eloc 10
nc 9
nop 1
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
            $entitiesChain = $this->getEntitiesChain($entity);
90
91
            if (empty($entitiesChain)) {
92
                throw new Notice("Unknown entity! Skipping.");
93
            }
94
95
            $result = $this->processEntitiesChain($entitiesChain);
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
                $parent   = isset($entityData['parent']) ? $entityData['parent'] : null;
180
                $continue = $this->triggerEventForEntity($entityData['entity'], $parent);
181
182
                if (!$continue) {
183
                    return true;
184
                }
185
            } catch (\Exception $e) {
186
                Output::log($e);
187
            }
188
        }
189
190
        return true;
191
    }
192
193
    /**
194
     * Triggers desired event and returns boolean result from run method of the event. If run() returns
195
     * false the processing in main process method will be stopped and further events (if any)
196
     * will not be triggered otherwise process will continue until either first false returned or the very
197
     * last event in the flow.
198
     *
199
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
200
     * @param AbstractEntity $parent    Entity's parent if any
201
     *
202
     * @return bool
203
     */
204
    protected function triggerEventForEntity(AbstractEntity $entity, AbstractEntity $parent = null)
205
    {
206
        $eventClass = $this->getEventClass($entity);
207
208
        if (class_exists($eventClass)) {
209
            $event = new $eventClass();
210
211
            if (!$event instanceof AbstractEntityEvent && !$event instanceof AbstractCommand) {
212
                return true;
213
            }
214
215
            /** @var AbstractCommand $eventClass */
216
            if ($event instanceof AbstractCommand && $entity instanceof MessageEntity && $entity->isCommand()) {
217
                $event->setArgs($entity->getArgs());
218
            }
219
220
            $referencedEntity = $parent ? $parent : $entity;
221
222
            $event->setEntity($referencedEntity);
223
224
            return $event->run();
225
        }
226
227
        return true;
228
    }
229
230
    /**
231
     * Returns mapped event class from the configuration (if it was previously defined)
232
     *
233
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
234
     *                                  be treated as a command
235
     * @return null|string
236
     */
237
    protected function getEventClass(AbstractEntity $entity)
238
    {
239
        $preDefinedEvents = $this->config->getEvents();
240
        $entityEventType  = $entity->getEntityType();
241
242
        if (!is_array($preDefinedEvents)) {
243
            return null;
244
        }
245
246
        foreach ($preDefinedEvents as $preDefinedEvent) {
247
248
            if ($preDefinedEvent['type'] == $entityEventType) {
249
                $className = $preDefinedEvent['class'];
250
251
                if ($entity instanceof MessageEntity && $entity->isCommand()) {
252
                    if (!$this->isCommandSupported($preDefinedEvent, $entity->getCommand())) {
253
                        continue;
254
                    }
255
                }
256
257
                if (class_exists($className)) {
258
                    return $className;
259
                }
260
            }
261
        }
262
263
        return null;
264
    }
265
266
    /**
267
     * Checks whether command is defined in config and matches the current one
268
     *
269
     * @param array  $preDefinedEvent Pre defined event data
270
     * @param string $command         Command name
271
     *
272
     * @return bool
273
     */
274
    protected function isCommandSupported($preDefinedEvent, $command)
275
    {
276
        return isset($preDefinedEvent['command']) && strtolower($preDefinedEvent['command']) == strtolower($command);
277
    }
278
279
    /**
280
     * Executes remote method and returns response object
281
     * 
282
     * @param AbstractMethod $method     Method instance
283
     * @param bool           $silentMode If set to true then the events, mapped (in config or by default)
284
     *                                   to the entities in the result will not be triggered
285
     * @param AbstractEntity $parent     Parent entity (if any)
286
     *
287
     * @return Response
288
     */
289
    public function callRemoteMethod(AbstractMethod $method, $silentMode = false, $parent = null)
290
    {
291
        /** @var Response $response */
292
        $response = $this->request->exec($method, $parent);
293
294
        return $this->processResponse($response, $silentMode);
295
    }
296
297
    /**
298
     * Returns a response object and starts the entities processing (if not in silent mode). Method
299
     * should be used only when webhook is set.
300
     *
301
     * @param string $data       Raw json data either received from php input or passed manually
302
     * @param bool   $silentMode If set to true then the events, mapped (in config or by default)
303
     *                           to the entities in the result will not be triggered
304
     *
305
     * @return Response
306
     */
307
    public function getWebhookResponse($data, $silentMode = false)
308
    {
309
        $response = new Response($data, Update::class);
310
311
        return $this->processResponse($response, $silentMode);
312
    }
313
314
    /**
315
     * Processes entities from the response object if not in silent mode or error is received.
316
     *
317
     * @param Response $response   Response object which includes entities
318
     * @param bool     $silentMode If set to true then the events, mapped (in config or by default)
319
     *                             to the entities in the result will not be triggered
320
     *
321
     * @return Response
322
     */
323
    protected function processResponse(Response $response, $silentMode = false)
324
    {
325
        if (!empty($response->getEntities()) && ($silentMode === false || $response->isErrorReceived())) {
326
            $this->processEntities($response->getEntities());
327
        }
328
329
        return $response;
330
    }
331
}
332