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 ( 480f18...457cc0 )
by Stan
02:57
created

src/Command/Executor.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
    protected 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);
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
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
237
     *                                  be treated as a command     
238
     * @return null|string
239
     */
240
    protected function getMappedEventClass(AbstractEntity $entity, $isCommand)
241
    {
242
        $preDefinedEvents = $this->config->getEvents();
243
        $entityEventType  = $entity->getEntityType();
244
245
        foreach ($preDefinedEvents as $preDefinedEvent) {
246
            if ($preDefinedEvent['type'] == $entityEventType) {
247
                $className = $preDefinedEvent['class'];
248
249
                if ($entity instanceof Command && $isCommand === true) {
250
                    $command = $entity->getName();
251
252
                    if (isset($preDefinedEvent['command']) && $preDefinedEvent['command'] != $command) {
253
                        continue;
254
                    }
255
                }
256
257
                if (class_exists($className)) {
258
                    return $className;
259
                }
260
            }
261
        }
262
263
        return null;
264
    }
265
266
    /**
267
     * Returns default event class
268
     *
269
     * @param AbstractEntity $entity    Entity for which the corresponding event should be triggered
270
     * @param bool           $isCommand Boolean flag which indicates that passed entity should
271
     *                                  be treated as a command
272
     * 
273
     * @return null|string
274
     */
275
    protected function getDefaultEventClass($entity, $isCommand)
276
    {
277
        $className = null;
278
279
        if ($entity instanceof Command && $isCommand === true) {
280
            $ucName    = ucwords($entity->getName(), Command::PARTS_DELIMITER);
281
            $name      = str_replace(Command::PARTS_DELIMITER, '', $ucName);
282
283
            $className = $this->config->getCommandNamespace() . "\\" . $name;
284
        } elseif ($entity instanceof AbstractEntity) {
285
            $className = $this->config->getEntityEventNamespace() . "\\". $entity->getEntityType();
286
        }
287
288
        if ($className && class_exists($className)) {
289
            return $className;
290
        }
291
292
        return null;
293
    }
294
295
    /**
296
     * Executes remote method and returns response object
297
     * 
298
     * @param AbstractMethod $method     Method instance
299
     * @param bool           $silentMode If set to true then the events, mapped (in config or by default)
300
     *                                   to the entities in the result will not be triggered
301
     * @param AbstractEntity $parent     Parent entity (if any)
302
     *
303
     * @return Response
304
     */
305
    public function callRemoteMethod(AbstractMethod $method, $silentMode = false, $parent = null)
306
    {
307
        /** @var Response $response */
308
        $response = $this->request->exec($method, $parent);
309
310
        return $this->processResponse($response, $silentMode);
311
    }
312
313
    /**
314
     * Returns a response object and starts the entities processing (if not in silent mode). Method
315
     * should be used only when webhook is set.
316
     *
317
     * @param string $data       Raw json data either received from php input or passed manually
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
    public function getWebhookResponse($data, $silentMode = false)
324
    {
325
        $response = new Response($data, Update::class);
326
327
        return $this->processResponse($response, $silentMode);
328
    }
329
330
    /**
331
     * Processes entities from the response object if not in silent mode or error is received.
332
     *
333
     * @param Response $response   Response object which includes entities
334
     * @param bool     $silentMode If set to true then the events, mapped (in config or by default)
335
     *                             to the entities in the result will not be triggered
336
     *
337
     * @return Response
338
     */
339
    protected function processResponse(Response $response, $silentMode = false)
340
    {
341
        if (!empty($response->getEntities()) && ($silentMode === false || $response->isErrorReceived())) {
342
            $this->processEntities($response->getEntities());
343
        }
344
345
        return $response;
346
    }
347
}
348