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 ( 64a210...2d6991 )
by Stan
02:42
created

Executor::getMappedEventClass()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 25
rs 5.3846
cc 8
eloc 13
nc 7
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\Update;
17
use Teebot\Exception\Notice;
18
use Teebot\Exception\Output;
19
use Teebot\Method\AbstractMethod;
20
use Teebot\Request;
21
use Teebot\Entity\AbstractEntity;
22
use Teebot\Entity\Message;
23
use Teebot\Config;
24
use Teebot\Response;
25
26
class Executor
27
{
28
    /** @var Executor */
29
    protected static $instance;
30
31
    /** @var Config $config */
32
    protected $config;
33
34
    /** @var Request $request */
35
    protected $request;
36
37
    /**
38
     * Returns a singletone instance of executor
39
     *
40
     * @return Executor
41
     */
42
    public static function getInstance()
43
    {
44
        if (!self::$instance instanceof self) {
45
            self::$instance = new self();
46
        }
47
48
        return self::$instance;
49
    }
50
51
    /**
52
     * Returns an instance of config class
53
     *
54
     * @return Config
55
     */
56
    public function getConfig()
57
    {
58
        return $this->config;
59
    }
60
61
    /**
62
     * Initialises the executor, creates request object
63
     *
64
     * @param Config $config Configuration object
65
     */
66
    public function initWithConfig(Config $config)
67
    {
68
        $this->config  = $config;
69
        $this->request = new Request($config);
70
    }
71
72
    /**
73
     * Processes array of entities from response object, root entities must be either update entity
74
     * or error entity in case of error response from Telegram's API.
75
     *
76
     * @param array $entities Array of entities (Update or Error) passed either from response object or
77
     *                        directly to the method
78
     */
79
    public function processEntities(array $entities)
80
    {
81
        try {
82
            /** @var Update $entity */
83
            foreach ($entities as $entity) {
84
                $entitiesFlow = $this->getEntitiesFlow($entity);
85
86
                if (empty($entitiesFlow)) {
87
                    throw new Notice("Unknown entity! Skipping.");
88
                }
89
90
                $this->processEntitiesFlow($entitiesFlow);
91
            }
92
        } catch (Notice $e) {
93
            Output::log($e);
94
        }
95
    }
96
97
    /**
98
     * Returns flow generated from nested sub-entities of the passed entity
99
     *
100
     * Generated hierarchy of the events:
101
     *
102
     * - Error
103
     * - Update
104
     *   - Message
105
     *     - Command
106
     *       - <Command name>
107
     *     - Audio ... <Any supported entity>
108
     *   - Inline Query
109
     *   - Chosen Inline Result
110
     *
111
     * @param Update|Error $entity Update or Error entity object
112
     *
113
     * @return array
114
     */
115
    protected function getEntitiesFlow($entity)
116
    {
117
        if ($entity instanceof Error) {
118
            return [
119
                ['entity' => $entity]
120
            ];
121
        }
122
        if (!$entity instanceof Update) {
123
            return [];
124
        }
125
126
        $updateTypeEntity = $entity->getUpdateTypeEntity();
127
128
        $events = [
129
            ['entity' => $entity],
130
            ['entity' => $updateTypeEntity, 'parent' => $updateTypeEntity],
131
        ];
132
133
        if ($updateTypeEntity instanceof Message && $updateTypeEntity->getMessageTypeEntity()) {
134
135
            $messageTypeEntity = $updateTypeEntity->getMessageTypeEntity();
136
137
            $events[] = [
138
                'entity' => $messageTypeEntity,
139
                'parent' => $updateTypeEntity
140
            ];
141
142
            if ($messageTypeEntity instanceof Command) {
143
                $events[] = [
144
                    'entity' => $messageTypeEntity,
145
                    'parent' => $updateTypeEntity,
146
                    'is_command' => true
147
                ];
148
            }
149
        }
150
151
        return $events;
152
    }
153
154
    /**
155
     * Processes generated entities flow, if triggered event returns false stops processing
156
     *
157
     * @param array $entitiesFlow Array of entities flow
158
     *
159
     * @throws Notice
160
     *
161
     * @return bool
162
     */
163
    protected function processEntitiesFlow(array $entitiesFlow)
164
    {
165
        foreach ($entitiesFlow as $entityData) {
166
167
            try {
168
                $continue = $this->triggerEvent(
169
                    $entityData['entity'],
170
                    $entityData['parent'] ?? null,
171
                    $entityData['is_command'] ?? false
172
                );
173
174
                if (!$continue) {
175
                    return true;
176
                }
177
            } catch (\Exception $e) {
178
                Output::log($e);
179
            }
180
        }
181
182
        return true;
183
    }
184
185
    /**
186
     * Triggers desired event and returns boolean result from run method of the event. If run() returns
187
     * false the processing in main process method will be stopped and further events (if any)
188
     * will not be triggered otherwise process will continue until either first false returned or the very
189
     * last event in the flow.
190
     *
191
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
192
     * @param AbstractEntity $parent    Entity's parent if any
193
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
194
     *                                  be treated as a command
195
     *
196
     * @return bool
197
     */
198
    protected function triggerEvent(AbstractEntity $entity, AbstractEntity $parent = null, $isCommand = false)
199
    {
200
        if ($this->config->getEvents()) {
201
            $eventClassName = $this->getMappedEventClass($entity, $isCommand);
202
        } else {
203
            $eventClassName = $this->getDefaultEventClass($entity, $isCommand);
204
        }
205
206
        if (class_exists($eventClassName)) {
207
            $eventClass = new $eventClassName();
208
209
            if (!$eventClass instanceof AbstractEntityEvent && !$eventClass instanceof AbstractCommand) {
210
                return true;
211
            }
212
213
            /** @var AbstractCommand $eventClass */
214
            if ($eventClassName instanceof AbstractCommand && $entity instanceof Command) {
215
                $eventClass->setArgs($entity->getArgs());
216
            }
217
218
            $eventClass->setEntity($parent ?? $entity);
219
220
            return $eventClass->run();
221
        }
222
223
        return true;
224
    }
225
226
    /**
227
     * Returns mapped event class from the configuration (if it was previously defined)
228
     * 
229
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered 
230
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
231
     *                                  be treated as a command     
232
     * @return null|string
233
     */
234
    protected function getMappedEventClass(AbstractEntity $entity, $isCommand)
235
    {
236
        $preDefinedEvents = $this->config->getEvents();
237
        $entityEventType  = $entity->getEntityType();
238
239
        foreach ($preDefinedEvents as $preDefinedEvent) {
240
            if ($preDefinedEvent['type'] == $entityEventType) {
241
                $className = $preDefinedEvent['class'];
242
243
                if ($entity instanceof Command && $isCommand == true) {
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...
244
                    $command = $entity->getName();
245
246
                    if (isset($preDefinedEvent['command']) && $preDefinedEvent['command'] != $command) {
247
                        continue;
248
                    }
249
                }
250
251
                if (class_exists($className)) {
252
                    return $className;
253
                }
254
            }
255
        }
256
257
        return null;
258
    }
259
260
    /**
261
     * Returns default event class
262
     *
263
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
264
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
265
     *                                  be treated as a command
266
     * 
267
     * @return null|string
268
     */
269
    protected function getDefaultEventClass($entity, $isCommand)
270
    {
271
        $className = null;
272
273
        if ($entity instanceof Command && $isCommand == true) {
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...
274
            $ucName    = ucwords($entity->getName(), Command::PARTS_DELIMITER);
275
            $name      = str_replace(Command::PARTS_DELIMITER, '', $ucName);
276
277
            $className = $this->config->getCommandNamespace() . "\\" . $name;
278
        } elseif ($entity instanceof AbstractEntity) {
279
            $className = $this->config->getEntityEventNamespace() . "\\". $entity->getEntityType();
280
        }
281
282
        if ($className && class_exists($className)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $className of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
283
            return $className;
284
        }
285
286
        return null;
287
    }
288
289
    /**
290
     * Executes remote method and returns response object
291
     * 
292
     * @param AbstractMethod $method     Method instance
293
     * @param bool           $silentMode If set to true then the events, mapped (in config or by default)
294
     *                                   to the entities in the result will not be triggered
295
     * @param AbstractMethod $parent     Parent entity (if any)
296
     *
297
     * @return Response
298
     */
299
    public function callRemoteMethod(AbstractMethod $method, $silentMode = false, $parent = null)
300
    {
301
        /** @var Response $response */
302
        $response = $this->request->exec($method, $parent);
0 ignored issues
show
Bug introduced by
It seems like $parent defined by parameter $parent on line 299 can also be of type object<Teebot\Method\AbstractMethod>; however, Teebot\Request::exec() does only seem to accept null|object<Teebot\Entity\AbstractEntity>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
303
304
        return $this->processResponse($response, $silentMode);
305
    }
306
307
    /**
308
     * Returns a response object and starts the entities processing (if not in silent mode). Method
309
     * should be used only when webhook is set.
310
     *
311
     * @param string $data       Raw json data either received from php input or passed manually
312
     * @param bool   $silentMode If set to true then the events, mapped (in config or by default)
313
     *                           to the entities in the result will not be triggered
314
     *
315
     * @return Response
316
     */
317
    public function getWebhookResponse($data, $silentMode = false)
318
    {
319
        $response = new Response($data, Update::class);
320
321
        return $this->processResponse($response, $silentMode);
322
    }
323
324
    /**
325
     * Processes entities from the response object if not in silent mode or error is received.
326
     *
327
     * @param Response $response   Response object which includes entities
328
     * @param bool     $silentMode If set to true then the events, mapped (in config or by default)
329
     *                             to the entities in the result will not be triggered
330
     *
331
     * @return Response
332
     */
333
    protected function processResponse(Response $response, $silentMode = false)
334
    {
335
        if (!empty($response->getEntities()) && ($silentMode == false || $response->isErrorReceived())) {
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...
336
            $this->processEntities($response->getEntities());
337
        }
338
339
        return $response;
340
    }
341
}
342