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.

Processor::isCommandSupported()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
declare(strict_types=1);
13
14
namespace Teebot\Api\Command;
15
16
use Teebot\Api\Entity\{
17
    EntityInterface,
18
    Error,
19
    Message,
20
    MessageEntity,
21
    MessageEntityArray,
22
    Update
23
};
24
use Teebot\Api\Method\MethodInterface;
25
use Teebot\Api\Exception\{
26
    ProcessEntitiesChainException,
27
    ProcessEntitiesException
28
};
29
use Teebot\Api\{
30
    Command\ValueObject\ChainItem, HttpClient, Request, Response
31
};
32
use Teebot\Configuration\{
33
    AbstractContainer as ConfigContainer,
34
    ContainerInterface,
35
    ValueObject\EventConfig
36
};
37
38
class Processor
39
{
40
    /**
41
     * @var ConfigContainer $config
42
     */
43
    protected $config;
44
45
    /**
46
     * @var HttpClient $httpClient
47
     */
48
    protected $httpClient;
49
50
    /**
51
     * @param ContainerInterface $config
52
     * @param HttpClient      $httpClient
53
     */
54
    public function __construct(ContainerInterface $config, HttpClient $httpClient)
55
    {
56
        $this->config     = $config;
0 ignored issues
show
Documentation Bug introduced by
$config is of type Teebot\Configuration\ContainerInterface, but the property $config was declared to be of type Teebot\Configuration\AbstractContainer. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
57
        $this->httpClient = $httpClient;
58
    }
59
60
    /**
61
     * Returns configuration container
62
     *
63
     * @return ContainerInterface
64
     */
65
    public function getConfig(): ContainerInterface
66
    {
67
        return $this->config;
68
    }
69
70
    /**
71
     * Processes array of entities from response object, root entities must be either update entity
72
     * or error entity in case of error response from Telegram's API.
73
     *
74
     * @param array $entities Array of entities (Update or Error) passed either from response object or
75
     *                        directly to the method
76
     *
77
     * @throws ProcessEntitiesException
78
     */
79
    public function processEntities(array $entities)
80
    {
81
        /** @var Update $entity */
82
        foreach ($entities as $entity) {
83
            $entitiesChain = $this->getEntitiesChain($entity);
84
85
            if (empty($entitiesChain)) {
86
                throw new ProcessEntitiesException("Entities chain is empty! There must be an unknown entity passed!");
87
            }
88
89
            $this->processEntitiesChain($entitiesChain);
90
        }
91
    }
92
93
    /**
94
     * Returns entities chain generated from nested sub-entities of the passed entity
95
     *
96
     * Generated hierarchy of the events:
97
     *
98
     * - Error
99
     * - Update
100
     *   - Message
101
     *     - Command
102
     *       - <Command name>
103
     *     - Audio ... <Any supported entity>
104
     *   - Inline Query
105
     *   - Chosen Inline Result
106
     *
107
     * @param EntityInterface $entity Update or Error entity object
108
     *
109
     * @return array
110
     */
111
    public function getEntitiesChain(EntityInterface $entity): array
112
    {
113
        if ($entity instanceof Error) {
114
            return [
115
                new ChainItem($entity),
116
            ];
117
        }
118
        if (!$entity instanceof Update) {
119
            return [];
120
        }
121
122
        $updateTypeEntity = $entity->getUpdateTypeEntity();
123
124
        $events = [
125
            new ChainItem($entity),
126
            new ChainItem($updateTypeEntity, $updateTypeEntity),
127
        ];
128
129
        if ($updateTypeEntity instanceof Message && $updateTypeEntity->getMessageTypeEntity()) {
130
131
            $messageTypeEntity = $updateTypeEntity->getMessageTypeEntity();
132
133
            $events[] = new ChainItem($messageTypeEntity, $updateTypeEntity);
134
135
            if ($messageTypeEntity instanceof MessageEntityArray) {
136
                $entities = $messageTypeEntity->getEntities();
137
138
                foreach ($entities as $entity) {
139
                    $events[] = new ChainItem($entity, $updateTypeEntity);
140
                }
141
            }
142
        }
143
144
        return $events;
145
    }
146
147
    /**
148
     * Processes generated entities chain, if triggered event returns false stops processing
149
     *
150
     * @param array $entitiesChain Array of entities
151
     *
152
     * @throws ProcessEntitiesChainException
153
     */
154
    protected function processEntitiesChain(array $entitiesChain)
155
    {
156
        /** @var ChainItem $chainItem */
157
        foreach ($entitiesChain as $chainItem) {
158
            try {
159
                $continue = $this->triggerEventForEntity($chainItem);
160
161
                if (!$continue) {
162
                    return;
163
                }
164
            } catch (\Exception $e) {
165
                throw new ProcessEntitiesChainException('Processing of the entities chain error', 0, $e);
166
            }
167
        }
168
    }
169
170
    /**
171
     * Triggers desired event and returns boolean result from run method of the event. If run() returns
172
     * false the processing in main process method will be stopped and further events (if any)
173
     * will not be triggered otherwise process will continue until either first false returned or the very
174
     * last event in the flow.
175
     *
176
     * @param ChainItem $chainItem
177
     *
178
     * @return bool
179
     */
180
    protected function triggerEventForEntity(ChainItem $chainItem): bool
181
    {
182
        $entity             = $chainItem->getEntity();
183
        $eventConfiguration = $this->getEventConfiguration($entity);
184
185
        if ($eventConfiguration === null) {
186
            return true;
187
        }
188
189
        $eventClass = $eventConfiguration->getClass();
190
191
        if (class_exists($eventClass)) {
192
            $event = new $eventClass();
193
194
            if (!$event instanceof EventInterface && !$event instanceof CommandInterface) {
195
                return true;
196
            }
197
198
            /** @var AbstractCommand $eventClass */
199
            if ($event instanceof CommandInterface && $entity instanceof MessageEntity && $entity->isNativeCommand()) {
200
                $event->setArgs($entity->getArgs());
201
            }
202
203
            $referencedEntity = $chainItem->getParent() ? $chainItem->getParent() : $entity;
204
205
            $event
206
                ->setProcessor($this)
207
                ->setParams($eventConfiguration->getParams())
208
                ->setEntity($referencedEntity);
209
210
            return (bool) $event->run();
211
        }
212
213
        return true;
214
    }
215
216
    /**
217
     * Returns event configuration item search by the data from entity
218
     *
219
     * @param EntityInterface $entity   Entity for which the corresponding event should be triggered
220
     *                                  be treated as a command
221
     *
222
     * @return null|EventConfig
223
     */
224
    protected function getEventConfiguration(EntityInterface $entity): ?EventConfig
225
    {
226
        $preDefinedEvents = $this->config->get('events');
227
        $entityEventType  = $entity->getEntityType();
228
229
        if (!is_array($preDefinedEvents)) {
230
            return null;
231
        }
232
233
        /** @var EventConfig $preDefinedEvent */
234
        foreach ($preDefinedEvents as $preDefinedEvent) {
235
            $className = null;
236
237
            if ($preDefinedEvent->getType() == Message::MESSAGE_TYPE_REGEXP_COMMAND) {
238
                if ($entity instanceof Message && $entity->hasBuiltinRegexpCommand($preDefinedEvent->getCommand())) {
239
                    $className = $preDefinedEvent->getClass();
240
                }
241
            }
242
243
            if ($preDefinedEvent->getType() == $entityEventType) {
244
                $className = $preDefinedEvent->getClass();
245
246
                if ($entity instanceof MessageEntity && $entity->isNativeCommand()) {
247
                    if (!$this->isCommandSupported($preDefinedEvent->getCommand(), $entity->getCommand())) {
248
                        continue;
249
                    }
250
                }
251
            }
252
253
            if ($className && class_exists($className)) {
254
                return $preDefinedEvent;
255
            }
256
        }
257
258
        return null;
259
    }
260
261
    /**
262
     * Checks whether command is defined in config and matches the current one
263
     *
264
     * @param string|null $preDefinedCommand Pre command
265
     * @param string      $command           Command name
266
     *
267
     * @return bool
268
     */
269
    protected function isCommandSupported(?string $preDefinedCommand, string $command): bool
270
    {
271
        return $preDefinedCommand !== null && strtolower($preDefinedCommand) == strtolower($command);
272
    }
273
274
    /**
275
     * Executes remote method and returns response object
276
     *
277
     * @param MethodInterface $method     Method instance
278
     * @param bool            $silentMode If set to true then the events, mapped (in config or by default)
279
     *                                    to the entities in the result will not be triggered
280
     * @param EntityInterface $parent     Parent entity (if any)
281
     *
282
     * @return Response
283
     */
284
    public function call(MethodInterface $method, $silentMode = false, EntityInterface $parent = null)
285
    {
286
        $request  = new Request($this->httpClient);
287
        $response = $request->exec($method, $parent);
288
289
        return $this->processResponse($response, $silentMode);
290
    }
291
292
    /**
293
     * Returns a response object and starts the entities processing (if not in silent mode). Method
294
     * should be used only when webhook is set.
295
     *
296
     * @param string $data       Raw json data either received from php input or passed manually
297
     * @param bool   $silentMode If set to true then the events, mapped (in config or by default)
298
     *                           to the entities in the result will not be triggered
299
     *
300
     * @return Response
301
     */
302
    public function getWebhookResponse($data, $silentMode = false)
303
    {
304
        $response = new Response($data, Update::class);
305
306
        return $this->processResponse($response, $silentMode);
307
    }
308
309
    /**
310
     * Processes entities from the response object if not in silent mode or error is received.
311
     *
312
     * @param Response $response   Response object which includes entities
313
     * @param bool     $silentMode If set to true then the events, mapped (in config or by default)
314
     *                             to the entities in the result will not be triggered
315
     *
316
     * @return Response
317
     */
318
    protected function processResponse(Response $response, $silentMode = false)
319
    {
320
        if (!empty($response->getEntities()) && ($silentMode === false || $response->isErrorReceived())) {
321
            $this->processEntities($response->getEntities());
322
        }
323
324
        return $response;
325
    }
326
}
327