UpdateHandler::spreadUpdateWith()   A
last analyzed

Complexity

Conditions 4
Paths 5

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 7
c 2
b 0
f 0
dl 0
loc 13
rs 10
cc 4
nc 5
nop 2
1
<?php
2
3
namespace TelegramBot;
4
5
use TelegramBot\Entities\Update;
6
use TelegramBot\Exception\InvalidBotTokenException;
7
use TelegramBot\Interfaces\HandlerInterface;
8
use TelegramBot\Traits\HandlerTrait;
9
use TelegramBot\Util\Toolkit;
10
11
/**
12
 * UpdateHandler class
13
 *
14
 * @link    https://github.com/telegram-bot-php/core
15
 * @author  Shahrad Elahi (https://github.com/shahradelahi)
16
 * @license https://github.com/telegram-bot-php/core/blob/master/LICENSE (MIT License)
17
 */
18
class UpdateHandler extends Telegram implements HandlerInterface {
19
20
   use HandlerTrait;
21
22
   /**
23
    * @var ?Update
24
    */
25
   protected ?Update $update;
26
27
   /**
28
    * @var Plugin[]
29
    */
30
   private array $plugins = [];
31
32
   /**
33
    * @var bool
34
    */
35
   private bool $active_spreader = false;
36
37
   /**
38
    * The default configuration of the webhook.
39
    *
40
    * @var array
41
    */
42
   private array $config = [
43
      'autoload_env_file' => false,
44
      'env_file_path' => null,
45
   ];
46
47
   /**
48
    * Filter incoming updates.
49
    *
50
    * @var array
51
    */
52
   private array $filterIncomingUpdates = [];
53
54
   /**
55
    * Webhook constructor.
56
    *
57
    * @param string $api_token The API key of the bot. Leave it blank for autoload from .env file.
58
    */
59
   public function __construct(string $api_token = '') {
60
      parent::__construct($api_token);
61
62
      if (!Telegram::validateToken(self::getApiToken())) {
0 ignored issues
show
Bug introduced by
It seems like self::getApiToken() can also be of type false; however, parameter $token of TelegramBot\Telegram::validateToken() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
      if (!Telegram::validateToken(/** @scrutinizer ignore-type */ self::getApiToken())) {
Loading history...
63
         throw new InvalidBotTokenException();
64
      }
65
   }
66
67
   /**
68
    * Resolve the request on single plugin.
69
    *
70
    * @param Plugin $plugin The plugin to work with
71
    * @param ?Update $update The custom to work with
72
    * @param array $config The configuration of the receiver
73
    * @return void
74
    */
75
   public static function resolveOn(Plugin $plugin, Update $update = null, array $config = []): void {
0 ignored issues
show
Unused Code introduced by
The parameter $plugin is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

75
   public static function resolveOn(/** @scrutinizer ignore-unused */ Plugin $plugin, Update $update = null, array $config = []): void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $config is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

75
   public static function resolveOn(Plugin $plugin, Update $update = null, /** @scrutinizer ignore-unused */ array $config = []): void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $update is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

75
   public static function resolveOn(Plugin $plugin, /** @scrutinizer ignore-unused */ Update $update = null, array $config = []): void {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
76
      // TODO: Implement resolveOn() method.
77
   }
78
79
   /**
80
    * Add plugins to the receiver
81
    *
82
    * @param Plugin[]|array $plugins
83
    * @retrun void
84
    */
85
   public function addPlugins(Plugin|array $plugins): UpdateHandler {
86
      if (is_object($plugins)) {
0 ignored issues
show
introduced by
The condition is_object($plugins) is always false.
Loading history...
87
         $plugins = [$plugins];
88
      }
89
90
      foreach ($plugins as $plugin) {
91
         if (!is_subclass_of($plugin, Plugin::class)) {
92
            throw new \RuntimeException(
93
               sprintf('The plugin %s must be an instance of %s', get_class($plugin), Plugin::class)
94
            );
95
         }
96
97
         $reflection = Toolkit::reflectionClass($plugin);
0 ignored issues
show
Unused Code introduced by
The assignment to $reflection is dead and can be removed.
Loading history...
98
         $this->plugins[] = [
99
            'class' => $plugin,
100
            'initialized' => is_object($plugin),
101
         ];
102
      }
103
104
      return $this;
105
   }
106
107
   /**
108
    * Updates the filter for incoming updates.
109
    *
110
    * @param array $filter
111
    * @return void
112
    */
113
   public function filterIncomingUpdates(array $filter): void {
114
      $this->filterIncomingUpdates = $filter;
115
   }
116
117
   /**
118
    * Do not process updates that match the filter.
119
    *
120
    * @param Update $update
121
    * @return bool
122
    */
123
   private function isFiltered(Update $update): bool {
124
      if (empty($this->filterIncomingUpdates)) {
125
         return false;
126
      }
127
128
      foreach ($this->filterIncomingUpdates as $type => $value) {
129
         if (is_int($type)) {
130
            if ($update->getUpdateType() === $value) {
131
               return true;
132
            }
133
         } elseif (is_string($type)) {
134
            if ($update->getUpdateType() === $type) {
135
               if (is_callable($value)) {
136
                  return $value($update);
137
               } elseif (is_bool($value)) {
138
                  return $value;
139
               }
140
               throw new \InvalidArgumentException('The value of the filter must be a callable or a boolean');
141
            }
142
         }
143
         throw new \InvalidArgumentException('Invalid filter');
144
      }
145
146
      return false;
147
   }
148
149
   /**
150
    * Resolve the request.
151
    *
152
    * @param ?Update $update The custom to work with
153
    * @param array $config The configuration of the receiver
154
    *
155
    * @retrun void
156
    */
157
   public function resolve(Update|null $update = null, array $config = []): void {
158
      $this->update = $update ?? Telegram::getUpdate();
159
160
      if (empty($this->update)) {
161
         TelegramLog::error('The update is empty, the request is not processed');
162
         return;
163
      }
164
165
      if ($this->isFiltered($this->update)) {
166
         TelegramLog::notice('The update is filtered, the request is not processed');
167
         return;
168
      }
169
170
      if (!method_exists($this, '__process')) {
171
         throw new \RuntimeException('The method __process does not exist');
172
      }
173
174
      if (is_array($config)) {
0 ignored issues
show
introduced by
The condition is_array($config) is always true.
Loading history...
175
         $this->updateConfiguration($config);
176
      }
177
178
      putenv('TG_CURRENT_UPDATE=' . $this->update->getRawData(false));
179
180
      $this->__process($this->update);
181
      $this->loadPlugins($this->plugins);
182
   }
183
184
   /**
185
    * Update the configuration
186
    *
187
    * @param array $configuration
188
    * @return void
189
    */
190
   public function updateConfiguration(array $configuration): void {
191
      $this->config = array_merge($this->config, $configuration);
192
   }
193
194
   /**
195
    * Match the update with the given plugins
196
    *
197
    * @param Plugin[]|array $plugins
198
    * @return void
199
    */
200
   private function loadPlugins(array $plugins): void {
201
      $update = $update ?? ($this->update ?? Telegram::getUpdate());
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $update seems to never exist and therefore isset should always be false.
Loading history...
202
203
      foreach ($plugins as $plugin) {
204
         if (!is_subclass_of($plugin['class'], Plugin::class)) {
205
            throw new \InvalidArgumentException(sprintf(
206
               'The plugin %s must be an instance of %s',
207
               get_class($plugin['class']), Plugin::class
208
            ));
209
         }
210
      }
211
212
      if (!$update instanceof Update) {
213
         throw new \InvalidArgumentException(sprintf(
214
            'The update must be an instance of %s. %s given',
215
            Update::class, gettype($update)
216
         ));
217
      }
218
219
      $this->spreadUpdateWith($update, $plugins);
220
   }
221
222
   /**
223
    * This function will get update and spread it to the plugins
224
    *
225
    * @param Update $update
226
    * @param array<Plugin> $plugins
227
    * @return void
228
    */
229
   private function spreadUpdateWith(Update $update, array $plugins): void {
230
      $this->active_spreader = true;
231
232
      foreach ($plugins as $plugin) {
233
         if ($plugin['initialized'] === false) {
234
            $plugin['class'] = new $plugin['class']();
235
         }
236
237
         $plugin['class']->__execute($this, $update);
238
         if ($this->active_spreader === false) break;
239
      }
240
241
      $this->active_spreader = false;
242
   }
243
244
   /**
245
    * Stop the spreader process
246
    *
247
    * @return void
248
    */
249
   public function stop(): void {
250
      $this->active_spreader = false;
251
   }
252
253
}