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 ( b9cf05...84c3a7 )
by Stan
03:33
created

Executor::initWithConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
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\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
     * @return bool
80
     *
81
     * @throws Notice
82
     */
83
    public function processEntities(array $entities)
84
    {
85
        /** @var Update $entity */
86
        foreach ($entities as $entity) {
87
            $entitiesFlow = $this->getEntitiesFlow($entity);
88
89
            if (empty($entitiesFlow)) {
90
                throw new Notice("Unknown entity! Skipping.");
91
            }
92
93
            $result = $this->processEntitiesFlow($entitiesFlow);
94
95
            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...
96
                throw new Notice("Failed to process the entity!");
97
            }
98
        }
99
100
        return true;
101
    }
102
103
    /**
104
     * Returns flow generated from nested sub-entities of the passed entity
105
     *
106
     * Generated hierarchy of the events:
107
     *
108
     * - Error
109
     * - Update
110
     *   - Message
111
     *     - Command
112
     *       - <Command name>
113
     *     - Audio ... <Any supported entity>
114
     *   - Inline Query
115
     *   - Chosen Inline Result
116
     *
117
     * @param Update|Error $entity Update or Error entity object
118
     *
119
     * @return array
120
     */
121
    public function getEntitiesFlow($entity)
122
    {
123
        if ($entity instanceof Error) {
124
            return [
125
                ['entity' => $entity]
126
            ];
127
        }
128
        if (!$entity instanceof Update) {
129
            return [];
130
        }
131
132
        $updateTypeEntity = $entity->getUpdateTypeEntity();
133
134
        $events = [
135
            ['entity' => $entity],
136
            ['entity' => $updateTypeEntity, 'parent' => $updateTypeEntity],
137
        ];
138
139
        if ($updateTypeEntity instanceof Message && $updateTypeEntity->getMessageTypeEntity()) {
140
141
            $messageTypeEntity = $updateTypeEntity->getMessageTypeEntity();
142
143
            $events[] = [
144
                'entity' => $messageTypeEntity,
145
                'parent' => $updateTypeEntity
146
            ];
147
148
            if ($messageTypeEntity instanceof Command) {
149
                $events[] = [
150
                    'entity' => $messageTypeEntity,
151
                    'parent' => $updateTypeEntity,
152
                    'is_command' => true
153
                ];
154
            }
155
        }
156
157
        return $events;
158
    }
159
160
    /**
161
     * Processes generated entities flow, if triggered event returns false stops processing
162
     *
163
     * @param array $entitiesFlow Array of entities flow
164
     *
165
     * @throws Notice
166
     *
167
     * @return bool
168
     */
169
    protected function processEntitiesFlow(array $entitiesFlow)
170
    {
171
        foreach ($entitiesFlow as $entityData) {
172
173
            try {
174
                $continue = $this->triggerEvent(
175
                    $entityData['entity'],
176
                    $entityData['parent'] ?? null,
177
                    $entityData['is_command'] ?? false
178
                );
179
180
                if (!$continue) {
181
                    return true;
182
                }
183
            } catch (\Exception $e) {
184
                Output::log($e);
185
            }
186
        }
187
188
        return true;
189
    }
190
191
    /**
192
     * Triggers desired event and returns boolean result from run method of the event. If run() returns
193
     * false the processing in main process method will be stopped and further events (if any)
194
     * will not be triggered otherwise process will continue until either first false returned or the very
195
     * last event in the flow.
196
     *
197
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
198
     * @param AbstractEntity $parent    Entity's parent if any
199
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
200
     *                                  be treated as a command
201
     *
202
     * @return bool
203
     */
204
    protected function triggerEvent(AbstractEntity $entity, AbstractEntity $parent = null, $isCommand = false)
205
    {
206
        if ($this->config->getEvents()) {
207
            $eventClassName = $this->getMappedEventClass($entity, $isCommand);
0 ignored issues
show
Unused Code introduced by
The call to Executor::getMappedEventClass() has too many arguments starting with $isCommand.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
208
        } else {
209
            $eventClassName = $this->getDefaultEventClass($entity, $isCommand);
210
        }
211
212
        if (class_exists($eventClassName)) {
213
            $eventClass = new $eventClassName();
214
215
            if (!$eventClass instanceof AbstractEntityEvent && !$eventClass instanceof AbstractCommand) {
216
                return true;
217
            }
218
219
            /** @var AbstractCommand $eventClass */
220
            if ($eventClassName instanceof AbstractCommand && $entity instanceof Command) {
221
                $eventClass->setArgs($entity->getArgs());
222
            }
223
224
            $eventClass->setEntity($parent ?? $entity);
225
226
            return $eventClass->run();
227
        }
228
229
        return true;
230
    }
231
232
    /**
233
     * Returns mapped event class from the configuration (if it was previously defined)
234
     *
235
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
236
     *                                  be treated as a command
237
     * @return null|string
238
     */
239
    protected function getMappedEventClass(AbstractEntity $entity)
240
    {
241
        $preDefinedEvents = $this->config->getEvents();
242
        $entityEventType  = $entity->getEntityType();
243
244
        foreach ($preDefinedEvents as $preDefinedEvent) {
245
            if ($preDefinedEvent['type'] == $entityEventType) {
246
                $className = $preDefinedEvent['class'];
247
248
                if ($entity instanceof Command) {
249
                    if (!$this->isCommandSupported($preDefinedEvent, $entity->getName())) {
250
                        continue;
251
                    }
252
                }
253
254
                if (class_exists($className)) {
255
                    return $className;
256
                }
257
            }
258
        }
259
260
        return null;
261
    }
262
263
    /**
264
     * Checks whether command is defined in config and matches the current one
265
     *
266
     * @param array  $preDefinedEvent Pre defined event data
267
     * @param string $command         Command name
268
     *
269
     * @return bool
270
     */
271
    protected function isCommandSupported($preDefinedEvent, $command)
272
    {
273
        return isset($preDefinedEvent['command']) && strtolower($preDefinedEvent['command']) == strtolower($command);
274
    }
275
276
    /**
277
     * Returns default event class
278
     *
279
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
280
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
281
     *                                  be treated as a command
282
     * 
283
     * @return null|string
284
     */
285
    protected function getDefaultEventClass($entity, $isCommand)
286
    {
287
        $className = null;
288
289
        if ($entity instanceof Command && $isCommand === true) {
290
            $ucName    = ucwords($entity->getName(), Command::PARTS_DELIMITER);
291
            $name      = str_replace(Command::PARTS_DELIMITER, '', $ucName);
292
293
            $className = $this->config->getCommandNamespace() . "\\" . $name;
294
        } elseif ($entity instanceof AbstractEntity) {
295
            $className = $this->config->getEntityEventNamespace() . "\\". $entity->getEntityType();
296
        }
297
298
        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...
299
            return $className;
300
        }
301
302
        return null;
303
    }
304
305
    /**
306
     * Executes remote method and returns response object
307
     * 
308
     * @param AbstractMethod $method     Method instance
309
     * @param bool           $silentMode If set to true then the events, mapped (in config or by default)
310
     *                                   to the entities in the result will not be triggered
311
     * @param AbstractEntity $parent     Parent entity (if any)
312
     *
313
     * @return Response
314
     */
315
    public function callRemoteMethod(AbstractMethod $method, $silentMode = false, $parent = null)
316
    {
317
        /** @var Response $response */
318
        $response = $this->request->exec($method, $parent);
319
320
        return $this->processResponse($response, $silentMode);
321
    }
322
323
    /**
324
     * Returns a response object and starts the entities processing (if not in silent mode). Method
325
     * should be used only when webhook is set.
326
     *
327
     * @param string $data       Raw json data either received from php input or passed manually
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
    public function getWebhookResponse($data, $silentMode = false)
334
    {
335
        $response = new Response($data, Update::class);
336
337
        return $this->processResponse($response, $silentMode);
338
    }
339
340
    /**
341
     * Processes entities from the response object if not in silent mode or error is received.
342
     *
343
     * @param Response $response   Response object which includes entities
344
     * @param bool     $silentMode If set to true then the events, mapped (in config or by default)
345
     *                             to the entities in the result will not be triggered
346
     *
347
     * @return Response
348
     */
349
    protected function processResponse(Response $response, $silentMode = false)
350
    {
351
        if (!empty($response->getEntities()) && ($silentMode === false || $response->isErrorReceived())) {
352
            $this->processEntities($response->getEntities());
353
        }
354
355
        return $response;
356
    }
357
}
358